You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@impala.apache.org by ta...@apache.org on 2019/02/21 19:40:01 UTC

[impala] 13/13: IMPALA-7140 (part 2). Create skeleton for LocalFsTable

This is an automated email from the ASF dual-hosted git repository.

tarmstrong pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/impala.git

commit ec2e30aa96507410c4ee22d3e79642c1f8829db9
Author: Todd Lipcon <to...@cloudera.com>
AuthorDate: Tue Jun 12 18:43:43 2018 -0700

    IMPALA-7140 (part 2). Create skeleton for LocalFsTable
    
    This adds a skeleton implementation of FeFsTable for the LocalCatalog.
    Most of the implementation in this patch is stubbed out -- only the bare
    bones necessary to instantiate the subclass when a table is loaded and
    the simplest table-level methods.
    
    Change-Id: Id2b184104d92e128250df5a08ac7ffb3dde011a8
    Reviewed-on: http://gerrit.cloudera.org:8080/10712
    Tested-by: Impala Public Jenkins <im...@cloudera.com>
    Reviewed-by: Todd Lipcon <to...@apache.org>
---
 .../java/org/apache/impala/catalog/FeFsTable.java  |  11 ++
 .../java/org/apache/impala/catalog/HdfsTable.java  |  43 +++--
 .../impala/catalog/local/DirectMetaProvider.java   |   8 +
 .../apache/impala/catalog/local/LocalCatalog.java  |  14 ++
 .../catalog/local/LocalCatalogException.java       |   4 +
 .../apache/impala/catalog/local/LocalFsTable.java  | 176 +++++++++++++++++++++
 .../apache/impala/catalog/local/LocalTable.java    |  14 +-
 .../apache/impala/catalog/local/MetaProvider.java  |   3 +
 .../java/org/apache/impala/util/MetaStoreUtil.java |  19 ++-
 .../impala/catalog/local/LocalCatalogTest.java     |  10 ++
 10 files changed, 278 insertions(+), 24 deletions(-)

diff --git a/fe/src/main/java/org/apache/impala/catalog/FeFsTable.java b/fe/src/main/java/org/apache/impala/catalog/FeFsTable.java
index 02c6211..86bf2eb 100644
--- a/fe/src/main/java/org/apache/impala/catalog/FeFsTable.java
+++ b/fe/src/main/java/org/apache/impala/catalog/FeFsTable.java
@@ -32,6 +32,9 @@ import org.apache.impala.util.ListMap;
 
 /**
  * Frontend interface for interacting with a filesystem-backed table.
+ *
+ * TODO(vercegovac): various method names and comments in this interface refer
+ * to HDFS where they should be more generically "Fs".
  */
 public interface FeFsTable extends FeTable {
   /** hive's default value for table property 'serialization.null.format' */
@@ -61,6 +64,9 @@ public interface FeFsTable extends FeTable {
 
   /**
    * @return the value Hive is configured to use for NULL partition key values.
+   *
+   * TODO(todd): this is an HMS-wide setting, rather than a per-table setting.
+   * Perhaps this should move to the FeCatalog interface?
    */
   public String getNullPartitionKeyValue();
 
@@ -84,6 +90,11 @@ public interface FeFsTable extends FeTable {
   long getTotalHdfsBytes();
 
   /**
+   * @return the total number of files stored on HDFS for this table
+   */
+  long getTotalNumFiles();
+
+  /**
    * @return true if this table is backed by the Avro file format
    */
   boolean isAvroTable();
diff --git a/fe/src/main/java/org/apache/impala/catalog/HdfsTable.java b/fe/src/main/java/org/apache/impala/catalog/HdfsTable.java
index 48095d4..b0b4387 100644
--- a/fe/src/main/java/org/apache/impala/catalog/HdfsTable.java
+++ b/fe/src/main/java/org/apache/impala/catalog/HdfsTable.java
@@ -1254,8 +1254,7 @@ public class HdfsTable extends Table implements FeFsTable {
       try {
         if (loadTableSchema) {
             // set nullPartitionKeyValue from the hive conf.
-            nullPartitionKeyValue_ = client.getConfigValue(
-                "hive.exec.default.partition.name", "__HIVE_DEFAULT_PARTITION__");
+            nullPartitionKeyValue_ = MetaStoreUtil.getNullPartitionKeyValue(client);
             loadSchema(msTbl);
             loadAllColumnStats(client);
         }
@@ -1775,6 +1774,9 @@ public class HdfsTable extends Table implements FeFsTable {
   public long getTotalHdfsBytes() { return fileMetadataStats_.totalFileBytes; }
 
   @Override // FeFsTable
+  public long getTotalNumFiles() { return fileMetadataStats_.numFiles; }
+
+  @Override // FeFsTable
   public String getHdfsBaseDir() { return hdfsBaseDir_; }
   public Path getHdfsBaseDirPath() { return new Path(hdfsBaseDir_); }
   @Override // FeFsTable
@@ -1988,18 +1990,24 @@ public class HdfsTable extends Table implements FeFsTable {
 
   @Override // FeFsTable
   public TResultSet getTableStats() {
+    return getTableStats(this);
+  }
+
+  // TODO(todd): move to FeCatalogUtils. Upon moving to Java 8, could be
+  // a default method of FeFsTable.
+  public static TResultSet getTableStats(FeFsTable table) {
     TResultSet result = new TResultSet();
     TResultSetMetadata resultSchema = new TResultSetMetadata();
     result.setSchema(resultSchema);
 
-    for (int i = 0; i < numClusteringCols_; ++i) {
+    for (int i = 0; i < table.getNumClusteringCols(); ++i) {
       // Add the partition-key values as strings for simplicity.
-      Column partCol = getColumns().get(i);
+      Column partCol = table.getColumns().get(i);
       TColumn colDesc = new TColumn(partCol.getName(), Type.STRING.toThrift());
       resultSchema.addToColumns(colDesc);
     }
 
-    boolean statsExtrap = isStatsExtrapolationEnabled();
+    boolean statsExtrap = table.isStatsExtrapolationEnabled();
 
     resultSchema.addToColumns(new TColumn("#Rows", Type.BIGINT.toThrift()));
     if (statsExtrap) {
@@ -2014,12 +2022,12 @@ public class HdfsTable extends Table implements FeFsTable {
     resultSchema.addToColumns(new TColumn("Location", Type.STRING.toThrift()));
 
     // Pretty print partitions and their stats.
-    ArrayList<HdfsPartition> orderedPartitions =
-        Lists.newArrayList(partitionMap_.values());
+    List<FeFsPartition> orderedPartitions = new ArrayList<>(
+        FeCatalogUtils.loadAllPartitions(table));
     Collections.sort(orderedPartitions, HdfsPartition.KV_COMPARATOR);
 
     long totalCachedBytes = 0L;
-    for (HdfsPartition p: orderedPartitions) {
+    for (FeFsPartition p: orderedPartitions) {
       TResultRowBuilder rowBuilder = new TResultRowBuilder();
 
       // Add the partition-key values (as strings for simplicity).
@@ -2032,7 +2040,7 @@ public class HdfsTable extends Table implements FeFsTable {
       // Compute and report the extrapolated row count because the set of files could
       // have changed since we last computed stats for this partition. We also follow
       // this policy during scan-cardinality estimation.
-      if (statsExtrap) rowBuilder.add(getExtrapolatedNumRows(p.getSize()));
+      if (statsExtrap) rowBuilder.add(table.getExtrapolatedNumRows(p.getSize()));
       rowBuilder.add(p.getFileDescriptors().size()).addBytes(p.getSize());
       if (!p.isMarkedCached()) {
         // Helps to differentiate partitions that have 0B cached versus partitions
@@ -2057,7 +2065,7 @@ public class HdfsTable extends Table implements FeFsTable {
         // Extract cache replication factor from the parameters of the table
         // if the table is not partitioned or directly from the partition.
         Short rep = HdfsCachingUtil.getCachedCacheReplication(
-            numClusteringCols_ == 0 ?
+            table.getNumClusteringCols() == 0 ?
             p.getTable().getMetaStoreTable().getParameters() :
             p.getParameters());
         rowBuilder.add(rep.toString());
@@ -2070,9 +2078,9 @@ public class HdfsTable extends Table implements FeFsTable {
     }
 
     // For partitioned tables add a summary row at the bottom.
-    if (numClusteringCols_ > 0) {
+    if (table.getNumClusteringCols() > 0) {
       TResultRowBuilder rowBuilder = new TResultRowBuilder();
-      int numEmptyCells = numClusteringCols_ - 1;
+      int numEmptyCells = table.getNumClusteringCols() - 1;
       rowBuilder.add("Total");
       for (int i = 0; i < numEmptyCells; ++i) {
         rowBuilder.add("");
@@ -2080,15 +2088,15 @@ public class HdfsTable extends Table implements FeFsTable {
 
       // Total rows, extrapolated rows, files, bytes, cache stats.
       // Leave format empty.
-      rowBuilder.add(getNumRows());
+      rowBuilder.add(table.getNumRows());
       // Compute and report the extrapolated row count because the set of files could
       // have changed since we last computed stats for this partition. We also follow
       // this policy during scan-cardinality estimation.
       if (statsExtrap) {
-        rowBuilder.add(getExtrapolatedNumRows(fileMetadataStats_.totalFileBytes));
+        rowBuilder.add(table.getExtrapolatedNumRows(table.getTotalHdfsBytes()));
       }
-      rowBuilder.add(fileMetadataStats_.numFiles)
-          .addBytes(fileMetadataStats_.totalFileBytes)
+      rowBuilder.add(table.getTotalNumFiles())
+          .addBytes(table.getTotalHdfsBytes())
           .addBytes(totalCachedBytes).add("").add("").add("").add("");
       result.addToRows(rowBuilder.get());
     }
@@ -2278,7 +2286,8 @@ public class HdfsTable extends Table implements FeFsTable {
     HiveConf hiveConf = new HiveConf(HdfsTable.class);
     // set nullPartitionKeyValue from the hive conf.
     tmpTable.nullPartitionKeyValue_ = hiveConf.get(
-        "hive.exec.default.partition.name", "__HIVE_DEFAULT_PARTITION__");
+        MetaStoreUtil.NULL_PARTITION_KEY_VALUE_CONF_KEY,
+        MetaStoreUtil.DEFAULT_NULL_PARTITION_KEY_VALUE);
     tmpTable.loadSchema(msTbl);
     tmpTable.initializePartitionMetadata(msTbl);
     tmpTable.setTableStats(msTbl);
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/DirectMetaProvider.java b/fe/src/main/java/org/apache/impala/catalog/local/DirectMetaProvider.java
index 0867091..ac86bfd 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/DirectMetaProvider.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/DirectMetaProvider.java
@@ -26,6 +26,7 @@ import org.apache.impala.catalog.MetaStoreClientPool;
 import org.apache.impala.catalog.MetaStoreClientPool.MetaStoreClient;
 import org.apache.impala.service.BackendConfig;
 import org.apache.impala.thrift.TBackendGflags;
+import org.apache.impala.util.MetaStoreUtil;
 import org.apache.thrift.TException;
 
 import com.google.common.collect.ImmutableList;
@@ -80,4 +81,11 @@ class DirectMetaProvider implements MetaProvider {
       return c.getHiveClient().getTable(dbName, tableName);
     }
   }
+
+  @Override
+  public String loadNullPartitionKeyValue() throws MetaException, TException {
+    try (MetaStoreClient c = msClientPool_.getClient()) {
+      return MetaStoreUtil.getNullPartitionKeyValue(c.getHiveClient());
+    }
+  }
 }
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalog.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalog.java
index 2b246e6..007450d 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalog.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalog.java
@@ -66,6 +66,7 @@ import com.google.common.collect.Maps;
 public class LocalCatalog implements FeCatalog {
   private final MetaProvider metaProvider_;
   private Map<String, FeDb> dbs_ = Maps.newHashMap();
+  private String nullPartitionKeyValue_;
   private static final Db builtinsDb_ = new BuiltinsDb(ImpaladCatalog.BUILTINS_DB);
 
   public static FeCatalog create(String defaultKuduMasterHosts) {
@@ -200,6 +201,19 @@ public class LocalCatalog implements FeCatalog {
     throw new UnsupportedOperationException("TODO");
   }
 
+  public String getNullPartitionKeyValue() {
+    if (nullPartitionKeyValue_ == null) {
+      try {
+        nullPartitionKeyValue_ =
+            metaProvider_.loadNullPartitionKeyValue();
+      } catch (TException e) {
+        throw new LocalCatalogException(
+            "Could not load null partition key value", e);
+      }
+    }
+    return nullPartitionKeyValue_;
+  }
+
   @Override
   public boolean isReady() {
     // We are always ready.
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalogException.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalogException.java
index 7b4624c..6875bd3 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalogException.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalCatalogException.java
@@ -34,4 +34,8 @@ public class LocalCatalogException extends RuntimeException {
   public LocalCatalogException(Throwable cause) {
     super(cause);
   }
+
+  public LocalCatalogException(String msg) {
+    super(msg);
+  }
 }
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalFsTable.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalFsTable.java
new file mode 100644
index 0000000..b6ab559
--- /dev/null
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalFsTable.java
@@ -0,0 +1,176 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.impala.catalog.local;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.impala.analysis.LiteralExpr;
+import org.apache.impala.catalog.CatalogException;
+import org.apache.impala.catalog.FeFsPartition;
+import org.apache.impala.catalog.FeFsTable;
+import org.apache.impala.catalog.HdfsTable;
+import org.apache.impala.catalog.HdfsPartition.FileDescriptor;
+import org.apache.impala.catalog.PrunablePartition;
+import org.apache.impala.thrift.TNetworkAddress;
+import org.apache.impala.thrift.TPartitionKeyValue;
+import org.apache.impala.thrift.TResultSet;
+import org.apache.impala.util.ListMap;
+
+public class LocalFsTable extends LocalTable implements FeFsTable {
+  public LocalFsTable(LocalDb db, String tblName, SchemaInfo schemaInfo) {
+    super(db, tblName, schemaInfo);
+  }
+
+  @Override
+  public boolean isCacheable() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  @Override
+  public boolean isLocationCacheable() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  @Override
+  public boolean isMarkedCached() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  @Override
+  public String getLocation() {
+    return getMetaStoreTable().getSd().getLocation();
+  }
+
+  @Override
+  public String getNullPartitionKeyValue() {
+    return db_.getCatalog().getNullPartitionKeyValue();
+  }
+
+  @Override
+  public TResultSet getFiles(List<List<TPartitionKeyValue>> partitionSet)
+      throws CatalogException {
+    // TODO(todd): implement fetching files from HDFS
+    return null;
+  }
+
+  @Override
+  public String getHdfsBaseDir() {
+    // TODO(todd): this is redundant with getLocation, it seems.
+    return getLocation();
+  }
+
+  @Override
+  public long getTotalHdfsBytes() {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  @Override
+  public long getTotalNumFiles() {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  @Override
+  public boolean isAvroTable() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  @Override
+  public long getExtrapolatedNumRows(long totalBytes) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  @Override
+  public boolean isStatsExtrapolationEnabled() {
+    // TODO Auto-generated method stub
+    return false;
+  }
+
+  @Override
+  public TResultSet getTableStats() {
+    return HdfsTable.getTableStats(this);
+  }
+
+  @Override
+  public Collection<? extends PrunablePartition> getPartitions() {
+    // TODO(todd): implement me
+    return Collections.emptyList();
+  }
+
+  @Override
+  public Set<Long> getPartitionIds() {
+    // TODO(todd): implement me
+    return Collections.emptySet();
+  }
+
+  @Override
+  public Map<Long, ? extends PrunablePartition> getPartitionMap() {
+    // TODO(todd): implement me
+    return Collections.emptyMap();
+  }
+
+  @Override
+  public TreeMap<LiteralExpr, HashSet<Long>> getPartitionValueMap(int col) {
+    // TODO(todd): implement me
+    return new TreeMap<>();
+  }
+
+  @Override
+  public Set<Long> getNullPartitionIds(int colIdx) {
+    // TODO(todd): implement me
+    return Collections.emptySet();
+  }
+
+  @Override
+  public List<? extends FeFsPartition> loadPartitions(Collection<Long> ids) {
+    // TODO(todd): implement me
+    return Collections.emptyList();
+  }
+
+  @Override
+  public int parseSkipHeaderLineCount(StringBuilder error) {
+    // TODO Auto-generated method stub
+    return 0;
+  }
+
+  @Override
+  public Map<Long, List<FileDescriptor>> getFilesSample(
+      Collection<? extends FeFsPartition> inputParts,
+      long percentBytes, long minSampleBytes, long randomSeed) {
+    // TODO(todd): implement me
+    return Collections.emptyMap();
+  }
+
+  @Override
+  public ListMap<TNetworkAddress> getHostIndex() {
+    // TODO(todd): implement me
+    return new ListMap<>();
+  }
+}
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
index d6b0c88..191f248 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/LocalTable.java
@@ -32,6 +32,7 @@ import org.apache.impala.catalog.FeCatalogUtils;
 import org.apache.impala.catalog.FeDb;
 import org.apache.impala.catalog.FeFsTable;
 import org.apache.impala.catalog.FeTable;
+import org.apache.impala.catalog.HdfsFileFormat;
 import org.apache.impala.catalog.StructField;
 import org.apache.impala.catalog.StructType;
 import org.apache.impala.catalog.TableLoadingException;
@@ -53,7 +54,7 @@ import com.google.common.collect.Lists;
  * each catalog instance.
  */
 class LocalTable implements FeTable {
-  private final LocalDb db_;
+  protected final LocalDb db_;
   /** The lower-case name of the table. */
   private final String name_;
   private final SchemaInfo schemaInfo_;
@@ -62,10 +63,13 @@ class LocalTable implements FeTable {
     // In order to know which kind of table subclass to instantiate, we need
     // to eagerly grab and parse the top-level Table object from the HMS.
     SchemaInfo schemaInfo = SchemaInfo.load(db, tblName);
+    if (HdfsFileFormat.isHdfsInputFormatClass(
+        schemaInfo.msTable_.getSd().getInputFormat())) {
+      return new LocalFsTable(db, tblName, schemaInfo);
+    }
 
-    // TODO: change this function to instantiate the appropriate
-    // subclass based on the table type (eg view, hbase table, etc)
-    return new LocalTable(db, tblName, schemaInfo);
+    throw new LocalCatalogException("Unknown table type for table " +
+        db.getName() + "." + tblName);
   }
 
   public LocalTable(LocalDb db, String tblName, SchemaInfo schemaInfo) {
@@ -191,7 +195,7 @@ class LocalTable implements FeTable {
    * effort, could be refactored to avoid duplication.
    */
   @Immutable
-  private static class SchemaInfo {
+  protected static class SchemaInfo {
     private final Table msTable_;
 
     private final ArrayType type_;
diff --git a/fe/src/main/java/org/apache/impala/catalog/local/MetaProvider.java b/fe/src/main/java/org/apache/impala/catalog/local/MetaProvider.java
index cd8d3b9..6b25257 100644
--- a/fe/src/main/java/org/apache/impala/catalog/local/MetaProvider.java
+++ b/fe/src/main/java/org/apache/impala/catalog/local/MetaProvider.java
@@ -45,4 +45,7 @@ interface MetaProvider {
 
   Table loadTable(String dbName, String tableName)
       throws NoSuchObjectException, MetaException, TException;
+
+  String loadNullPartitionKeyValue()
+      throws MetaException, TException;
 }
diff --git a/fe/src/main/java/org/apache/impala/util/MetaStoreUtil.java b/fe/src/main/java/org/apache/impala/util/MetaStoreUtil.java
index 34082f4..5a5654a 100644
--- a/fe/src/main/java/org/apache/impala/util/MetaStoreUtil.java
+++ b/fe/src/main/java/org/apache/impala/util/MetaStoreUtil.java
@@ -23,6 +23,7 @@ import java.util.Map;
 
 import org.apache.hadoop.hive.conf.HiveConf;
 import org.apache.hadoop.hive.metastore.IMetaStoreClient;
+import org.apache.hadoop.hive.metastore.api.ConfigValSecurityException;
 import org.apache.hadoop.hive.metastore.api.MetaException;
 import org.apache.hadoop.hive.metastore.api.Partition;
 import org.apache.impala.catalog.HdfsTable;
@@ -31,8 +32,6 @@ import org.apache.impala.compat.MetastoreShim;
 import org.apache.log4j.Logger;
 import org.apache.thrift.TException;
 
-import org.apache.impala.catalog.HdfsTable;
-import org.apache.impala.common.AnalysisException;
 import org.apache.impala.thrift.TColumn;
 import com.google.common.base.Joiner;
 import com.google.common.base.Preconditions;
@@ -72,6 +71,13 @@ public class MetaStoreUtil {
   // Hive configuration.
   private static short maxPartitionsPerRpc_ = DEFAULT_MAX_PARTITIONS_PER_RPC;
 
+  // The configuration key that Hive uses to set the null partition key value.
+  public static final String NULL_PARTITION_KEY_VALUE_CONF_KEY =
+      "hive.exec.default.partition.name";
+  // The default value for the above configuration key.
+  public static final String DEFAULT_NULL_PARTITION_KEY_VALUE =
+      "__HIVE_DEFAULT_PARTITION__";
+
   static {
     // Get the value from the Hive configuration, if present.
     HiveConf hiveConf = new HiveConf(HdfsTable.class);
@@ -92,6 +98,15 @@ public class MetaStoreUtil {
   }
 
   /**
+   * Return the value that Hive is configured to use for NULL partition key values.
+   */
+  public static String getNullPartitionKeyValue(IMetaStoreClient client)
+      throws ConfigValSecurityException, TException {
+    return client.getConfigValue(
+        NULL_PARTITION_KEY_VALUE_CONF_KEY, DEFAULT_NULL_PARTITION_KEY_VALUE);
+  }
+
+  /**
    * Fetches all partitions for a table in batches, with each batch containing at most
    * 'maxPartsPerRpc' partitions. Returns a List containing all fetched Partitions.
    * Will throw a MetaException if existing partitions are dropped while a fetch is in
diff --git a/fe/src/test/java/org/apache/impala/catalog/local/LocalCatalogTest.java b/fe/src/test/java/org/apache/impala/catalog/local/LocalCatalogTest.java
index c8315df..a296399 100644
--- a/fe/src/test/java/org/apache/impala/catalog/local/LocalCatalogTest.java
+++ b/fe/src/test/java/org/apache/impala/catalog/local/LocalCatalogTest.java
@@ -23,8 +23,11 @@ import java.util.Set;
 
 import org.apache.impala.catalog.CatalogTest;
 import org.apache.impala.catalog.FeDb;
+import org.apache.impala.catalog.FeFsTable;
 import org.apache.impala.catalog.FeTable;
 import org.apache.impala.catalog.Type;
+import org.apache.impala.thrift.TResultSet;
+import org.apache.impala.util.MetaStoreUtil;
 import org.apache.impala.util.PatternMatcher;
 import org.junit.Before;
 import org.junit.Test;
@@ -91,5 +94,12 @@ public class LocalCatalogTest {
            Type.TIMESTAMP});
     FeTable t = functionalDb.getTable("alltypes");
     assertEquals(7300, t.getNumRows());
+
+    assertTrue(t instanceof LocalFsTable);
+    FeFsTable fsTable = (FeFsTable) t;
+    assertEquals(MetaStoreUtil.DEFAULT_NULL_PARTITION_KEY_VALUE,
+        fsTable.getNullPartitionKeyValue());
+    TResultSet stats = fsTable.getTableStats();
+    assertEquals(1, stats.getRowsSize());
   }
 }