IMPALA-3369: Add ALTER TABLE SET COLUMN STATS statement.
Adds a new command to manually set the table-level column stats.
Syntax:
ALTER TABLE [<db_name>.]<tbl_name> SET COLUMN STATS <col_name>
('statsKey'='val','statsKey2',='val2')
Valid values for 'statsKey': numDVs, numNulls, avgSize, maxSize
The 'val' portion needs to be a number appropriate for the given stats
key (e.g., a long for numDVs, a float for avgSize).
The special value of '-1' is allowed to reset stats to 'unknown'.
The keys as well as the values are specified as string literals to be
consistent with the existing DDL for setting TBLPROPERTIES/SERDEPROPERTIES,
in particular, setting the 'numRows' table/partition property.
Testing: Ran the tests locally on exhaustive. Did private runs
on core/hdfs and core/S3.
Change-Id: I45cd8aa7241ea962788ba9ca7d0bbfd864c4304f
Reviewed-on: http://gerrit.cloudera.org:8080/3189
Reviewed-by: Alex Behm <[email protected]>
Tested-by: Internal Jenkins
Project: http://git-wip-us.apache.org/repos/asf/incubator-impala/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-impala/commit/77da3834
Tree: http://git-wip-us.apache.org/repos/asf/incubator-impala/tree/77da3834
Diff: http://git-wip-us.apache.org/repos/asf/incubator-impala/diff/77da3834
Branch: refs/heads/master
Commit: 77da3834ffed32a365e64aad66f9bd4863bc14ee
Parents: d16e832
Author: Alex Behm <[email protected]>
Authored: Mon May 23 11:01:39 2016 -0700
Committer: Tim Armstrong <[email protected]>
Committed: Tue May 31 23:32:11 2016 -0700
----------------------------------------------------------------------
common/thrift/JniCatalog.thrift | 5 +-
fe/src/main/cup/sql-parser.cup | 13 +-
.../analysis/AlterTableSetColumnStats.java | 155 +++++++++++++++++++
.../impala/analysis/AlterTableStmt.java | 8 +-
.../cloudera/impala/catalog/ColumnStats.java | 80 +++++++++-
.../com/cloudera/impala/catalog/KuduTable.java | 25 ++-
.../com/cloudera/impala/common/RuntimeEnv.java | 11 +-
.../impala/service/CatalogOpExecutor.java | 23 ++-
.../impala/analysis/AnalyzeDDLTest.java | 145 ++++++++++++++++-
.../cloudera/impala/analysis/ParserTest.java | 12 ++
.../com/cloudera/impala/testutil/TestUtils.java | 18 +--
.../QueryTest/alter-table-set-column-stats.test | 151 ++++++++++++++++++
tests/metadata/test_ddl.py | 8 +
13 files changed, 606 insertions(+), 48 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/common/thrift/JniCatalog.thrift
----------------------------------------------------------------------
diff --git a/common/thrift/JniCatalog.thrift b/common/thrift/JniCatalog.thrift
index 5d700cf..5f2bd9d 100644
--- a/common/thrift/JniCatalog.thrift
+++ b/common/thrift/JniCatalog.thrift
@@ -63,7 +63,7 @@ enum TAlterTableType {
SET_FILE_FORMAT,
SET_LOCATION,
SET_TBL_PROPERTIES,
- // Used internally by the COMPUTE STATS DDL command.
+ // Used internally by COMPUTE STATS and by ALTER TABLE SET COLUMN STATS.
UPDATE_STATS,
SET_CACHED,
RECOVER_PARTITIONS,
@@ -282,7 +282,8 @@ struct TAlterTableSetLocationParams {
}
// Parameters for updating the table and/or column statistics
-// of a table. Used internally by a COMPUTE STATS command.
+// of a table. Used by ALTER TABLE SET COLUMN STATS, and internally by
+// a COMPUTE STATS command.
struct TAlterTableUpdateStatsParams {
// Fully qualified name of the table to be updated.
1: required CatalogObjects.TTableName table_name
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/cup/sql-parser.cup
----------------------------------------------------------------------
diff --git a/fe/src/main/cup/sql-parser.cup b/fe/src/main/cup/sql-parser.cup
index e6d2f59..62a2b80 100644
--- a/fe/src/main/cup/sql-parser.cup
+++ b/fe/src/main/cup/sql-parser.cup
@@ -891,12 +891,19 @@ alter_tbl_stmt ::=
table_property_type:target LPAREN properties_map:properties RPAREN
{: RESULT = new AlterTableSetTblProperties(table, partition, target,
properties); :}
| KW_ALTER KW_TABLE table_name:table opt_partition_spec:partition KW_SET
+ KW_COLUMN KW_STATS IDENT:col LPAREN properties_map:map RPAREN
+ {:
+ // The opt_partition_spec is used to avoid conflicts even though
+ // a partition clause does not make sense for this stmt. If a partition
+ // is given, manually throw a parse error.
+ if (partition != null) parser.parseError("set", SqlParserSymbols.KW_SET);
+ RESULT = new AlterTableSetColumnStats(table, col, map);
+ :}
+ | KW_ALTER KW_TABLE table_name:table opt_partition_spec:partition KW_SET
cache_op_val:cache_op
{:
// Ensure a parser error is thrown for ALTER statements if no cache op is
specified.
- if (cache_op == null) {
- parser.parseError("set", SqlParserSymbols.KW_SET);
- }
+ if (cache_op == null) parser.parseError("set", SqlParserSymbols.KW_SET);
RESULT = new AlterTableSetCachedStmt(table, partition, cache_op);
:}
| KW_ALTER KW_TABLE table_name:table KW_RECOVER KW_PARTITIONS
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/analysis/AlterTableSetColumnStats.java
----------------------------------------------------------------------
diff --git
a/fe/src/main/java/com/cloudera/impala/analysis/AlterTableSetColumnStats.java
b/fe/src/main/java/com/cloudera/impala/analysis/AlterTableSetColumnStats.java
new file mode 100644
index 0000000..1c2d97a
--- /dev/null
+++
b/fe/src/main/java/com/cloudera/impala/analysis/AlterTableSetColumnStats.java
@@ -0,0 +1,155 @@
+// Copyright 2016 Cloudera Inc.
+//
+// Licensed 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 com.cloudera.impala.analysis;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.cloudera.impala.catalog.Column;
+import com.cloudera.impala.catalog.ColumnStats;
+import com.cloudera.impala.common.AnalysisException;
+import com.cloudera.impala.thrift.TAlterTableParams;
+import com.cloudera.impala.thrift.TAlterTableType;
+import com.cloudera.impala.thrift.TAlterTableUpdateStatsParams;
+import com.google.common.base.Joiner;
+import com.google.common.base.Preconditions;
+
+/**
+* Represents an ALTER TABLE [<dbName>.]<tableName> SET COLUMN STATS <colName>
+* ('statsKey'='val','statsKey2',='val2') statement.
+*
+* The keys as well as the values are specified as string literals to be
consistent
+* with the existing DDL for setting TBLPROPERTIES/SERDEPROPERTIES, in
particular,
+* setting the 'numRows' table/partition property.
+*
+* Stats key comparisons are case-insensitive.
+*/
+public class AlterTableSetColumnStats extends AlterTableStmt {
+ private final String colName_;
+ private final HashMap<String, String> statsMap_;
+
+ // Complete column stats reflecting this alteration. Existing stats values
+ // are preserved. Result of analysis.
+ private ColumnStats colStats_;
+
+ public AlterTableSetColumnStats(TableName tableName, String colName,
+ HashMap<String, String> statsMap) {
+ super(tableName);
+ colName_ = colName;
+ statsMap_ = statsMap;
+ }
+
+ @Override
+ public void analyze(Analyzer analyzer) throws AnalysisException {
+ super.analyze(analyzer);
+
+ Column col = getTargetTable().getColumn(colName_);
+ if (col == null) {
+ throw new AnalysisException(
+ String.format("Column '%s' does not exist in table: %s",
+ colName_, getTargetTable().getFullName()));
+ }
+ // Cannot update stats on partition columns because the HMS has no entries
+ // for them, and the stats can be computed directly from the metadata.
+ if (col.getPosition() < getTargetTable().getNumClusteringCols()) {
+ throw new AnalysisException(
+ "Updating the stats of a partition column is not allowed: " +
colName_);
+ }
+ // Cannot update the stats if they are not supported for the column's type.
+ if (!ColumnStats.isSupportedColType(col.getType())) {
+ throw new AnalysisException(String.format(
+ "Statistics for column '%s' are not supported because " +
+ "it has type '%s'.", col.getName(), col.getType().toSql()));
+ }
+
+ // Copy the existing stats and then change the values according to the
+ // stats map of this stmt. The existing stats are first copied to preserve
+ // those stats values that are not changed by this stmt because all stats
+ // values are updated when altering the stats in the HMS.
+ colStats_ = col.getStats().clone();
+ for (Map.Entry<String, String> entry: statsMap_.entrySet()) {
+ ColumnStats.StatsKey statsKey =
ColumnStats.StatsKey.fromString(entry.getKey());
+ if (statsKey == null) {
+ throw new AnalysisException(String.format(
+ "Invalid column stats key: %s\nValid keys are: %s",
+ entry.getKey(),
Joiner.on(',').join(ColumnStats.StatsKey.values())));
+ }
+ setStatsValue(statsKey, entry.getValue(), col, colStats_);
+ }
+ }
+
+ /**
+ * Updates the given column stats based on statsKey and statsValue.
+ * Throws an AnalysisException if the statsValue is invalid or not
applicable to the
+ * column (e.g., trying to update the avg/max size of a fixed-length column).
+ */
+ private void setStatsValue(ColumnStats.StatsKey statsKey, String statsValue,
+ Column col, ColumnStats stats) throws AnalysisException {
+ // Updating max/avg size is only allowed for variable length columns.
+ if (col.getType().isFixedLengthType()
+ && (statsKey == ColumnStats.StatsKey.AVG_SIZE
+ || statsKey == ColumnStats.StatsKey.MAX_SIZE)) {
+ throw new AnalysisException(String.format(
+ "Cannot update the '%s' stats of column '%s' with type '%s'.\n" +
+ "Changing '%s' is only allowed for variable-length columns.",
+ statsKey, col.getName(), col.getType().toSql(), statsKey));
+ }
+
+ if (statsKey == ColumnStats.StatsKey.NUM_DISTINCT_VALUES ||
+ statsKey == ColumnStats.StatsKey.NUM_NULLS ||
+ statsKey == ColumnStats.StatsKey.MAX_SIZE) {
+ Long statsVal = null;
+ try {
+ statsVal = Long.parseLong(statsValue);
+ } catch (Exception e) {
+ }
+ if (statsVal == null || statsVal < -1) {
+ throw new AnalysisException(String.format(
+ "Invalid stats value '%s' for column stats key: %s\n" +
+ "Expected a positive integer or -1 for unknown.",
+ statsValue, statsKey));
+ }
+ stats.update(statsKey, statsVal);
+ } else if (statsKey == ColumnStats.StatsKey.AVG_SIZE) {
+ Float statsVal = null;
+ try {
+ statsVal = Float.parseFloat(statsValue);
+ } catch (Exception e) {
+ }
+ if (statsVal == null || (statsVal < 0 && statsVal != -1) ||
+ statsVal.isNaN() || statsVal.isInfinite()) {
+ throw new AnalysisException(String.format(
+ "Invalid stats value '%s' for column stats key: %s\n" +
+ "Expected a positive floating-point number or -1 for unknown.",
+ statsValue, statsKey));
+ }
+ stats.update(statsKey, statsVal);
+ } else {
+ Preconditions.checkState(false, "Unhandled StatsKey value: " + statsKey);
+ }
+ }
+
+ @Override
+ public TAlterTableParams toThrift() {
+ TAlterTableParams params = super.toThrift();
+ params.setAlter_type(TAlterTableType.UPDATE_STATS);
+ TAlterTableUpdateStatsParams updateStatsParams =
+ new TAlterTableUpdateStatsParams();
+ updateStatsParams.setTable_name(getTargetTable().getTableName().toThrift());
+ updateStatsParams.putToColumn_stats(colName_.toString(),
colStats_.toThrift());
+ params.setUpdate_stats_params(updateStatsParams);
+ return params;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/analysis/AlterTableStmt.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/com/cloudera/impala/analysis/AlterTableStmt.java
b/fe/src/main/java/com/cloudera/impala/analysis/AlterTableStmt.java
index 961cbc2..9335bbd 100644
--- a/fe/src/main/java/com/cloudera/impala/analysis/AlterTableStmt.java
+++ b/fe/src/main/java/com/cloudera/impala/analysis/AlterTableStmt.java
@@ -67,7 +67,10 @@ public abstract class AlterTableStmt extends StatementBase {
@Override
public void analyze(Analyzer analyzer) throws AnalysisException {
table_ = analyzer.getTable(tableName_, Privilege.ALTER);
- if (table_ instanceof KuduTable && !KuduTable.alterTableAllowed(this)) {
+ if (table_ instanceof KuduTable
+ && !(this instanceof AlterTableSetTblProperties)
+ && !(this instanceof AlterTableSetColumnStats)
+ && !(this instanceof AlterTableOrViewRenameStmt)) {
throw new AnalysisException(String.format(
"ALTER TABLE not allowed on Kudu table: %s", table_.getFullName()));
}
@@ -75,7 +78,8 @@ public abstract class AlterTableStmt extends StatementBase {
throw new AnalysisException(String.format(
"ALTER TABLE not allowed on a view: %s", table_.getFullName()));
}
- if (table_ instanceof DataSourceTable) {
+ if (table_ instanceof DataSourceTable
+ && !(this instanceof AlterTableSetColumnStats)) {
throw new AnalysisException(String.format(
"ALTER TABLE not allowed on a table produced by a data source: %s",
table_.getFullName()));
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/catalog/ColumnStats.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/com/cloudera/impala/catalog/ColumnStats.java
b/fe/src/main/java/com/cloudera/impala/catalog/ColumnStats.java
index 5493c3e..b865ef3 100644
--- a/fe/src/main/java/com/cloudera/impala/catalog/ColumnStats.java
+++ b/fe/src/main/java/com/cloudera/impala/catalog/ColumnStats.java
@@ -47,6 +47,31 @@ public class ColumnStats {
PrimitiveType.STRING, PrimitiveType.TIMESTAMP, PrimitiveType.TINYINT,
PrimitiveType.DECIMAL);
+ public enum StatsKey {
+ NUM_DISTINCT_VALUES("numDVs"),
+ NUM_NULLS("numNulls"),
+ AVG_SIZE("avgSize"),
+ MAX_SIZE("maxSize");
+
+ private final String name_;
+
+ private StatsKey(String name) { name_ = name; }
+
+ /**
+ * Returns the StatsKey whose name matches 'key'. The comparison is
+ * case insensitive. Returns null if there is no matching StatsKey.
+ */
+ public static StatsKey fromString(String key) {
+ for (StatsKey k: values()) {
+ if (key.equalsIgnoreCase(k.name_)) return k;
+ }
+ return null;
+ }
+
+ @Override
+ public String toString() { return name_; }
+ }
+
// in bytes: excludes serialization overhead
private double avgSize_;
// in bytes; includes serialization overhead.
@@ -60,6 +85,17 @@ public class ColumnStats {
}
/**
+ * C'tor for clone().
+ */
+ private ColumnStats(ColumnStats other) {
+ avgSize_ = other.avgSize_;
+ avgSerializedSize_ = other.avgSerializedSize_;
+ maxSize_ = other.maxSize_;
+ numDistinctValues_ = other.numDistinctValues_;
+ numNulls_ = other.numNulls_;
+ }
+
+ /**
* Initializes all column stats values as "unknown". For fixed-length type
* (those which don't need additional storage besides the slot they occupy),
* sets avgSerializedSize and maxSize to their slot size.
@@ -118,13 +154,14 @@ public class ColumnStats {
return this;
}
- public void setAvgSerializedSize(float avgSize) { this.avgSerializedSize_ =
avgSize; }
- public void setMaxSize(long maxSize) { this.maxSize_ = maxSize; }
+ public void setAvgSize(float avgSize) { avgSize_ = avgSize; }
+ public void setAvgSerializedSize(float avgSize) { avgSerializedSize_ =
avgSize; }
+ public void setMaxSize(long maxSize) { maxSize_ = maxSize; }
public long getNumDistinctValues() { return numDistinctValues_; }
public void setNumDistinctValues(long numDistinctValues) {
this.numDistinctValues_ = numDistinctValues;
}
- public void setNumNulls(long numNulls) { this.numNulls_ = numNulls; }
+ public void setNumNulls(long numNulls) { numNulls_ = numNulls; }
public double getAvgSerializedSize() { return avgSerializedSize_; }
public double getAvgSize() { return avgSize_; }
public long getMaxSize() { return maxSize_; }
@@ -216,6 +253,40 @@ public class ColumnStats {
}
/**
+ * Sets the member corresponding to the given stats key to 'value'.
+ * Requires that the given value is of a type appropriate for the
+ * member being set. Throws if that is not the case.
+ */
+ public void update(StatsKey key, Number value) {
+ Preconditions.checkNotNull(key);
+ Preconditions.checkNotNull(value);
+ if (key == StatsKey.AVG_SIZE) {
+ Preconditions.checkArgument(value instanceof Float);
+ } else {
+ Preconditions.checkArgument(value instanceof Long);
+ }
+ switch (key) {
+ case NUM_DISTINCT_VALUES: {
+ numDistinctValues_ = (Long) value;
+ break;
+ }
+ case NUM_NULLS: {
+ numNulls_ = (Long) value;
+ break;
+ }
+ case AVG_SIZE: {
+ avgSize_ = (Float) value;
+ break;
+ }
+ case MAX_SIZE: {
+ maxSize_ = (Long) value;
+ break;
+ }
+ default: Preconditions.checkState(false);
+ }
+ }
+
+ /**
* Returns true if the given PrimitiveType supports column stats updates.
*/
public static boolean isSupportedColType(Type colType) {
@@ -254,4 +325,7 @@ public class ColumnStats {
.add("numNulls_", numNulls_)
.toString();
}
+
+ @Override
+ public ColumnStats clone() { return new ColumnStats(this); }
}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/catalog/KuduTable.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/com/cloudera/impala/catalog/KuduTable.java
b/fe/src/main/java/com/cloudera/impala/catalog/KuduTable.java
index b1d11bc..81e63f1 100644
--- a/fe/src/main/java/com/cloudera/impala/catalog/KuduTable.java
+++ b/fe/src/main/java/com/cloudera/impala/catalog/KuduTable.java
@@ -18,11 +18,16 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+
import javax.xml.bind.DatatypeConverter;
-import com.cloudera.impala.analysis.AlterTableOrViewRenameStmt;
-import com.cloudera.impala.analysis.AlterTableSetTblProperties;
-import com.cloudera.impala.analysis.AlterTableStmt;
+import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
+import org.apache.hadoop.hive.metastore.api.FieldSchema;
+import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
+import org.apache.log4j.Logger;
+import org.kududb.client.KuduClient;
+import org.kududb.client.LocatedTablet;
+
import com.cloudera.impala.common.ImpalaRuntimeException;
import com.cloudera.impala.thrift.TCatalogObjectType;
import com.cloudera.impala.thrift.TColumn;
@@ -32,21 +37,14 @@ import com.cloudera.impala.thrift.TResultSetMetadata;
import com.cloudera.impala.thrift.TTable;
import com.cloudera.impala.thrift.TTableDescriptor;
import com.cloudera.impala.thrift.TTableType;
-import com.cloudera.impala.util.TResultRowBuilder;
import com.cloudera.impala.util.KuduUtil;
+import com.cloudera.impala.util.TResultRowBuilder;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
-import org.apache.hadoop.hive.metastore.HiveMetaStoreClient;
-import org.apache.hadoop.hive.metastore.TableType;
-import org.apache.hadoop.hive.metastore.api.FieldSchema;
-import org.apache.hadoop.hive.metastore.api.hive_metastoreConstants;
-import org.apache.log4j.Logger;
-import org.kududb.client.KuduClient;
-import org.kududb.client.LocatedTablet;
/**
* Impala representation of a Kudu table.
@@ -92,11 +90,6 @@ public class KuduTable extends Table {
// The set of columns that are key columns in Kudu.
private ImmutableList<String> kuduKeyColumnNames_;
- public static boolean alterTableAllowed(AlterTableStmt stmt) {
- return stmt instanceof AlterTableSetTblProperties ||
- stmt instanceof AlterTableOrViewRenameStmt;
- }
-
protected KuduTable(TableId id, org.apache.hadoop.hive.metastore.api.Table
msTable,
Db db, String name, String owner) {
super(id, msTable, db, name, owner);
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/common/RuntimeEnv.java
----------------------------------------------------------------------
diff --git a/fe/src/main/java/com/cloudera/impala/common/RuntimeEnv.java
b/fe/src/main/java/com/cloudera/impala/common/RuntimeEnv.java
index 6f562e5..91b93ef 100644
--- a/fe/src/main/java/com/cloudera/impala/common/RuntimeEnv.java
+++ b/fe/src/main/java/com/cloudera/impala/common/RuntimeEnv.java
@@ -14,14 +14,12 @@
package com.cloudera.impala.common;
-import java.lang.System;
-
-import com.cloudera.impala.thrift.TStartupOptions;
-import com.cloudera.impala.service.FeSupport;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.cloudera.impala.service.FeSupport;
+import com.cloudera.impala.thrift.TStartupOptions;
+
/**
* Contains runtime-specific parameters such as the number of CPU cores.
Currently only
* used in Plan cost estimation. The static RuntimeEnv members can be set so
that tests
@@ -63,5 +61,8 @@ public class RuntimeEnv {
public void setTestEnv(boolean v) { isTestEnv_ = v; }
public boolean isTestEnv() { return isTestEnv_; }
public boolean computeLineage() { return computeLineage_; }
+ public boolean isKuduSupported() {
+ return "true".equals(System.getenv("KUDU_IS_SUPPORTED"));
+ }
}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/main/java/com/cloudera/impala/service/CatalogOpExecutor.java
----------------------------------------------------------------------
diff --git
a/fe/src/main/java/com/cloudera/impala/service/CatalogOpExecutor.java
b/fe/src/main/java/com/cloudera/impala/service/CatalogOpExecutor.java
index ac21665..602f65a 100644
--- a/fe/src/main/java/com/cloudera/impala/service/CatalogOpExecutor.java
+++ b/fe/src/main/java/com/cloudera/impala/service/CatalogOpExecutor.java
@@ -23,7 +23,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
-import com.cloudera.impala.thrift.TDistributeParam;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hive.common.StatsSetupConst;
import org.apache.hadoop.hive.conf.HiveConf;
@@ -80,10 +79,10 @@ import com.cloudera.impala.catalog.TableLoadingException;
import com.cloudera.impala.catalog.TableNotFoundException;
import com.cloudera.impala.catalog.Type;
import com.cloudera.impala.catalog.View;
-import com.cloudera.impala.common.FileSystemUtil;
import com.cloudera.impala.catalog.delegates.DdlDelegate;
import com.cloudera.impala.catalog.delegates.KuduDdlDelegate;
import com.cloudera.impala.catalog.delegates.UnsupportedOpDelegate;
+import com.cloudera.impala.common.FileSystemUtil;
import com.cloudera.impala.common.ImpalaException;
import com.cloudera.impala.common.ImpalaRuntimeException;
import com.cloudera.impala.common.InternalException;
@@ -119,6 +118,7 @@ import com.cloudera.impala.thrift.TCreateTableParams;
import com.cloudera.impala.thrift.TDatabase;
import com.cloudera.impala.thrift.TDdlExecRequest;
import com.cloudera.impala.thrift.TDdlExecResponse;
+import com.cloudera.impala.thrift.TDistributeParam;
import com.cloudera.impala.thrift.TDropDataSourceParams;
import com.cloudera.impala.thrift.TDropDbParams;
import com.cloudera.impala.thrift.TDropFunctionParams;
@@ -543,13 +543,20 @@ public class CatalogOpExecutor {
}
/**
- * Alters an existing table's table and column statistics. Partitions are
updated
+ * Alters an existing table's table and/or column statistics. Partitions are
updated
* in batches of size 'MAX_PARTITION_UPDATES_PER_RPC'.
*/
private void alterTableUpdateStats(Table table, TAlterTableUpdateStatsParams
params,
TDdlExecResponse resp) throws ImpalaException {
Preconditions.checkState(Thread.holdsLock(table));
- Preconditions.checkState(params.isSetPartition_stats() &&
params.isSetTable_stats());
+ if (params.isSetTable_stats()) {
+ // Updating table and column stats via COMPUTE STATS.
+ Preconditions.checkState(
+ params.isSetPartition_stats() && params.isSetTable_stats());
+ } else {
+ // Only changing column stats via ALTER TABLE SET COLUMN STATS.
+ Preconditions.checkState(params.isSetColumn_stats());
+ }
TableName tableName = table.getTableName();
Preconditions.checkState(tableName != null &&
tableName.isFullyQualified());
@@ -569,13 +576,15 @@ public class CatalogOpExecutor {
}
MetaStoreClient msClient = catalog_.getMetaStoreClient();
- int numTargetedPartitions;
+ int numTargetedPartitions = 0;
int numUpdatedColumns = 0;
try {
// Update the table and partition row counts based on the query results.
List<HdfsPartition> modifiedParts = Lists.newArrayList();
- numTargetedPartitions = updateTableStats(table, params, msTbl,
partitions,
- modifiedParts);
+ if (params.isSetTable_stats()) {
+ numTargetedPartitions = updateTableStats(table, params, msTbl,
partitions,
+ modifiedParts);
+ }
ColumnStatistics colStats = null;
if (params.isSetColumn_stats()) {
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/test/java/com/cloudera/impala/analysis/AnalyzeDDLTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/com/cloudera/impala/analysis/AnalyzeDDLTest.java
b/fe/src/test/java/com/cloudera/impala/analysis/AnalyzeDDLTest.java
index e349ca7..796e8eb 100644
--- a/fe/src/test/java/com/cloudera/impala/analysis/AnalyzeDDLTest.java
+++ b/fe/src/test/java/com/cloudera/impala/analysis/AnalyzeDDLTest.java
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
@@ -32,7 +33,7 @@ import org.junit.Test;
import com.cloudera.impala.catalog.ArrayType;
import com.cloudera.impala.catalog.CatalogException;
-import com.cloudera.impala.analysis.CreateTableStmt;
+import com.cloudera.impala.catalog.ColumnStats;
import com.cloudera.impala.catalog.DataSource;
import com.cloudera.impala.catalog.DataSourceTable;
import com.cloudera.impala.catalog.PrimitiveType;
@@ -42,8 +43,10 @@ import com.cloudera.impala.catalog.StructType;
import com.cloudera.impala.catalog.Type;
import com.cloudera.impala.common.AnalysisException;
import com.cloudera.impala.common.FileSystemUtil;
+import com.cloudera.impala.common.RuntimeEnv;
import com.cloudera.impala.testutil.TestUtils;
import com.cloudera.impala.util.MetaStoreUtil;
+import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
@@ -563,6 +566,146 @@ public class AnalyzeDDLTest extends AnalyzerTest {
"Partition spec does not exist: (year=9999, month=1).");
}
+ @Test
+ public void TestAlterTableSetColumnStats() {
+ // Contains entries of the form 'statsKey'='statsValue' for every
+ // stats key. A dummy value is used for 'statsValue'.
+ List<String> testKeyValues = Lists.newArrayList();
+ for (ColumnStats.StatsKey statsKey: ColumnStats.StatsKey.values()) {
+ testKeyValues.add(String.format("'%s'='10'", statsKey));
+ }
+ // Test updating all stats keys individually.
+ for (String kv: testKeyValues) {
+ AnalyzesOk(String.format(
+ "alter table functional.alltypes set column stats string_col (%s)",
kv));
+ // Stats key is case-insensitive.
+ AnalyzesOk(String.format(
+ "alter table functional.alltypes set column stats string_col (%s)",
+ kv.toLowerCase()));
+ AnalyzesOk(String.format(
+ "alter table functional.alltypes set column stats string_col (%s)",
+ kv.toUpperCase()));
+ }
+ // Test updating all stats keys at once in a single statement.
+ AnalyzesOk(String.format(
+ "alter table functional.alltypes set column stats string_col (%s)",
+ Joiner.on(",").join(testKeyValues)));
+ // Test setting all stats keys to -1 (unknown).
+ for (ColumnStats.StatsKey statsKey: ColumnStats.StatsKey.values()) {
+ AnalyzesOk(String.format(
+ "alter table functional.alltypes set column stats string_col
('%s'='-1')",
+ statsKey));
+ }
+ // Duplicate stats keys are valid. The last entry is used.
+ AnalyzesOk("alter table functional.alltypes set column stats " +
+ "int_col ('numDVs'='2','numDVs'='3')");
+
+ // Test updating stats on all scalar types.
+ for (Type t: Type.getSupportedTypes()) {
+ if (t.isNull()) continue;
+ Preconditions.checkState(t.isScalarType());
+ String typeStr = t.getPrimitiveType().toString();
+ if (t.getPrimitiveType() == PrimitiveType.CHAR ||
+ t.getPrimitiveType() == PrimitiveType.VARCHAR) {
+ typeStr += "(60)";
+ }
+ String tblName = "t_" + t.getPrimitiveType();
+ addTestTable(String.format("create table %s (c %s)", tblName, typeStr));
+ AnalyzesOk(String.format(
+ "alter table %s set column stats c ('%s'='100','%s'='10')",
+ tblName, ColumnStats.StatsKey.NUM_DISTINCT_VALUES,
+ ColumnStats.StatsKey.NUM_NULLS));
+ // Test setting stats values to -1 (unknown).
+ AnalyzesOk(String.format(
+ "alter table %s set column stats c ('%s'='-1','%s'='-1')",
+ tblName, ColumnStats.StatsKey.NUM_DISTINCT_VALUES,
+ ColumnStats.StatsKey.NUM_NULLS));
+ }
+
+ // Setting stats works on all table types.
+ AnalyzesOk("alter table functional_hbase.alltypes set column stats " +
+ "int_col ('numNulls'='2')");
+ AnalyzesOk("alter table functional.alltypes_datasource set column stats " +
+ "int_col ('numDVs'='2')");
+ if (RuntimeEnv.INSTANCE.isKuduSupported()) {
+ AnalyzesOk("alter table functional_kudu.testtbl set column stats " +
+ "name ('numNulls'='2')");
+ }
+
+ // Table does not exist.
+ AnalysisError("alter table bad_tbl set column stats int_col
('numNulls'='2')",
+ "Table does not exist: default.bad_tbl");
+ // Column does not exist.
+ AnalysisError(
+ "alter table functional.alltypes set column stats bad_col
('numNulls'='2')",
+ "Column 'bad_col' does not exist in table: functional.alltypes");
+
+ // Cannot set column stats of a view.
+ AnalysisError(
+ "alter table functional.alltypes_view set column stats int_col
('numNulls'='2')",
+ "ALTER TABLE not allowed on a view: functional.alltypes_view");
+ // Cannot set column stats of partition columns.
+ AnalysisError(
+ "alter table functional.alltypes set column stats month
('numDVs'='10')",
+ "Updating the stats of a partition column is not allowed: month");
+ // Cannot set the size of a fixed-length column.
+ AnalysisError(
+ "alter table functional.alltypes set column stats int_col
('maxSize'='10')",
+ "Cannot update the 'maxSize' stats of column 'int_col' with type
'INT'.\n" +
+ "Changing 'maxSize' is only allowed for variable-length columns.");
+ AnalysisError(
+ "alter table functional.alltypes set column stats int_col
('avgSize'='10')",
+ "Cannot update the 'avgSize' stats of column 'int_col' with type
'INT'.\n" +
+ "Changing 'avgSize' is only allowed for variable-length columns.");
+ // Cannot set column stats of complex-typed columns.
+ AnalysisError(
+ "alter table functional.allcomplextypes set column stats int_array_col
" +
+ "('numNulls'='10')",
+ "Statistics for column 'int_array_col' are not supported because " +
+ "it has type 'ARRAY<INT>'.");
+ AnalysisError(
+ "alter table functional.allcomplextypes set column stats int_map_col "
+
+ "('numDVs'='10')",
+ "Statistics for column 'int_map_col' are not supported because " +
+ "it has type 'MAP<STRING,INT>'.");
+ AnalysisError(
+ "alter table functional.allcomplextypes set column stats
int_struct_col " +
+ "('numDVs'='10')",
+ "Statistics for column 'int_struct_col' are not supported because " +
+ "it has type 'STRUCT<f1:INT,f2:INT>'.");
+
+ // Invalid stats key.
+ AnalysisError(
+ "alter table functional.alltypes set column stats int_col
('badKey'='10')",
+ "Invalid column stats key: badKey");
+ AnalysisError(
+ "alter table functional.alltypes set column stats " +
+ "int_col ('numDVs'='10',''='10')",
+ "Invalid column stats key: ");
+ // Invalid long stats values.
+ AnalysisError(
+ "alter table functional.alltypes set column stats int_col
('numDVs'='bad')",
+ "Invalid stats value 'bad' for column stats key: numDVs");
+ AnalysisError(
+ "alter table functional.alltypes set column stats int_col
('numDVs'='-10')",
+ "Invalid stats value '-10' for column stats key: numDVs");
+ // Invalid float stats values.
+ AnalysisError(
+ "alter table functional.alltypes set column stats string_col
('avgSize'='bad')",
+ "Invalid stats value 'bad' for column stats key: avgSize");
+ AnalysisError(
+ "alter table functional.alltypes set column stats string_col
('avgSize'='-1.5')",
+ "Invalid stats value '-1.5' for column stats key: avgSize");
+ AnalysisError(
+ "alter table functional.alltypes set column stats string_col
('avgSize'='-0.5')",
+ "Invalid stats value '-0.5' for column stats key: avgSize");
+ AnalysisError(
+ "alter table functional.alltypes set column stats string_col
('avgSize'='NaN')",
+ "Invalid stats value 'NaN' for column stats key: avgSize");
+ AnalysisError(
+ "alter table functional.alltypes set column stats string_col
('avgSize'='inf')",
+ "Invalid stats value 'inf' for column stats key: avgSize");
+ }
@Test
public void TestAlterTableSetAvroProperties() {
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java
b/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java
index f503d69..f39f902 100644
--- a/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java
+++ b/fe/src/test/java/com/cloudera/impala/analysis/ParserTest.java
@@ -2097,6 +2097,18 @@ public class ParserTest {
}
}
+ // Test SET COLUMN STATS.
+ ParsesOk("ALTER TABLE Foo SET COLUMN STATS col ('numDVs'='10')");
+ ParsesOk("ALTER TABLE Foo SET COLUMN STATS col
('numDVs'='10','maxSize'='20')");
+ ParsesOk("ALTER TABLE TestDb.Foo SET COLUMN STATS col ('avgSize'='20')");
+ ParserError("ALTER TABLE SET COLUMN STATS col ('numDVs'='10'");
+ ParserError("ALTER TABLE Foo SET COLUMN STATS ('numDVs'='10'");
+ ParserError("ALTER TABLE Foo SET COLUMN STATS col");
+ ParserError("ALTER TABLE Foo SET COLUMN STATS col ()");
+ ParserError("ALTER TABLE Foo SET COLUMN STATS col (numDVs='10')");
+ ParserError("ALTER TABLE Foo SET COLUMN STATS col ('numDVs'=10)");
+ ParserError("ALTER TABLE Foo PARTITION (p=1) SET COLUMN STATS col
('avgSize'='20')");
+
for (String cacheClause: Lists.newArrayList("UNCACHED", "CACHED in 'pool'",
"CACHED in 'pool' with replication = 4")) {
ParsesOk("ALTER TABLE Foo SET " + cacheClause);
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/fe/src/test/java/com/cloudera/impala/testutil/TestUtils.java
----------------------------------------------------------------------
diff --git a/fe/src/test/java/com/cloudera/impala/testutil/TestUtils.java
b/fe/src/test/java/com/cloudera/impala/testutil/TestUtils.java
index 44070c7..7f51502 100644
--- a/fe/src/test/java/com/cloudera/impala/testutil/TestUtils.java
+++ b/fe/src/test/java/com/cloudera/impala/testutil/TestUtils.java
@@ -1,27 +1,28 @@
// Copyright (c) 2012 Cloudera, Inc. All rights reserved.
package com.cloudera.impala.testutil;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
-import java.util.Scanner;
import java.util.Map;
-import java.io.StringWriter;
-import java.io.StringReader;
+import java.util.Scanner;
import javax.json.Json;
-import javax.json.stream.JsonGenerator;
-import javax.json.JsonReader;
import javax.json.JsonObject;
+import javax.json.JsonReader;
import javax.json.JsonWriter;
import javax.json.JsonWriterFactory;
+import javax.json.stream.JsonGenerator;
+import org.junit.Assume;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.junit.Assume;
import com.cloudera.impala.catalog.Catalog;
+import com.cloudera.impala.common.RuntimeEnv;
import com.cloudera.impala.thrift.TClientRequest;
import com.cloudera.impala.thrift.TNetworkAddress;
import com.cloudera.impala.thrift.TQueryCtx;
@@ -29,7 +30,6 @@ import com.cloudera.impala.thrift.TQueryOptions;
import com.cloudera.impala.thrift.TSessionState;
import com.cloudera.impala.thrift.TSessionType;
import com.cloudera.impala.thrift.TUniqueId;
-
import com.google.common.collect.Maps;
public class TestUtils {
@@ -52,7 +52,7 @@ public class TestUtils {
static class PathFilter implements ResultFilter {
private final static String PATH_FILTER = "-*\\d+--\\d+_\\d+.*$";
private final static String PORT_FILTER =
"//\\w+(\\.\\w+)?(\\.\\w+)?:\\d+";
- private String filterKey_;
+ private final String filterKey_;
public PathFilter(String prefix) { filterKey_ = prefix; }
@@ -270,6 +270,6 @@ public class TestUtils {
}
public static void assumeKuduIsSupported() {
- Assume.assumeTrue("true".equals(System.getenv("KUDU_IS_SUPPORTED")));
+ Assume.assumeTrue(RuntimeEnv.INSTANCE.isKuduSupported());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/testdata/workloads/functional-query/queries/QueryTest/alter-table-set-column-stats.test
----------------------------------------------------------------------
diff --git
a/testdata/workloads/functional-query/queries/QueryTest/alter-table-set-column-stats.test
b/testdata/workloads/functional-query/queries/QueryTest/alter-table-set-column-stats.test
new file mode 100644
index 0000000..c3d07c2
--- /dev/null
+++
b/testdata/workloads/functional-query/queries/QueryTest/alter-table-set-column-stats.test
@@ -0,0 +1,151 @@
+====
+---- QUERY
+create external table alltypes_clone like functional_parquet.alltypes
+location '/test-warehouse/alltypes_parquet';
+alter table alltypes_clone recover partitions;
+====
+---- QUERY
+# Set various column stats.
+alter table alltypes_clone set column stats double_col ('numDVs'='2');
+alter table alltypes_clone set column stats timestamp_col ('numNulls'='9');
+alter table alltypes_clone set column stats int_col
('numDVs'='100','numNulls'='20');
+alter table alltypes_clone set column stats string_col
('maxSize'='555','avgSize'='60');
+====
+---- QUERY
+show column stats alltypes_clone
+---- LABELS
+COLUMN, TYPE, #DISTINCT VALUES, #NULLS, MAX SIZE, AVG SIZE
+---- RESULTS
+'id','INT',-1,-1,4,4
+'bool_col','BOOLEAN',-1,-1,1,1
+'tinyint_col','TINYINT',-1,-1,1,1
+'smallint_col','SMALLINT',-1,-1,2,2
+'int_col','INT',100,20,4,4
+'bigint_col','BIGINT',-1,-1,8,8
+'float_col','FLOAT',-1,-1,4,4
+'double_col','DOUBLE',2,-1,8,8
+'date_string_col','STRING',-1,-1,-1,-1
+'string_col','STRING',-1,-1,555,60
+'timestamp_col','TIMESTAMP',-1,9,16,16
+'year','INT',2,0,4,4
+'month','INT',12,0,4,4
+---- TYPES
+STRING, STRING, BIGINT, BIGINT, INT, DOUBLE
+====
+---- QUERY
+# Make sure compute stats still works.
+compute stats alltypes_clone
+---- RESULTS
+'Updated 24 partition(s) and 11 column(s).'
+---- TYPES
+STRING
+====
+---- QUERY
+# Reset the column stats to an unknown state by setting the values to -1
+alter table alltypes_clone set column stats double_col ('numDVs'='-1');
+alter table alltypes_clone set column stats timestamp_col ('numNulls'='-1');
+alter table alltypes_clone set column stats int_col
('numDVs'='-1','numNulls'='-1');
+alter table alltypes_clone set column stats string_col
('maxSize'='-1','avgSize'='-1');
+====
+---- QUERY
+show column stats alltypes_clone
+---- LABELS
+COLUMN, TYPE, #DISTINCT VALUES, #NULLS, MAX SIZE, AVG SIZE
+---- RESULTS
+'id','INT',7505,-1,4,4
+'bool_col','BOOLEAN',2,-1,1,1
+'tinyint_col','TINYINT',10,-1,1,1
+'smallint_col','SMALLINT',10,-1,2,2
+'int_col','INT',-1,-1,4,4
+'bigint_col','BIGINT',10,-1,8,8
+'float_col','FLOAT',10,-1,4,4
+'double_col','DOUBLE',-1,-1,8,8
+'date_string_col','STRING',736,-1,8,8
+'string_col','STRING',10,-1,-1,-1
+'timestamp_col','TIMESTAMP',7554,-1,16,16
+'year','INT',2,0,4,4
+'month','INT',12,0,4,4
+---- TYPES
+STRING, STRING, BIGINT, BIGINT, INT, DOUBLE
+====
+---- QUERY
+# Also alter a few 'numRows' parameters to make sure manually setting all
stats works.
+alter table alltypes_clone partition(year=2009,month=2) set
tblproperties('numRows'='280');
+alter table alltypes_clone set tblproperties('numRows'='7300');
+====
+---- QUERY
+# Check that we can query the table.
+select id, int_col, double_col, string_col, timestamp_col from alltypes_clone
+where year = 2009 and month between 2 and 3 and int_col = 9 and id between 300
and 400
+---- RESULTS
+319,9,90.89999999999999,'9',2009-02-01 00:09:00.360000000
+329,9,90.89999999999999,'9',2009-02-02 00:19:00.810000000
+339,9,90.89999999999999,'9',2009-02-03 00:29:01.260000000
+349,9,90.89999999999999,'9',2009-02-04 00:39:01.710000000
+359,9,90.89999999999999,'9',2009-02-05 00:49:02.160000000
+369,9,90.89999999999999,'9',2009-02-06 00:59:02.610000000
+379,9,90.89999999999999,'9',2009-02-07 01:09:03.600000000
+389,9,90.89999999999999,'9',2009-02-08 01:19:03.510000000
+399,9,90.89999999999999,'9',2009-02-09 01:29:03.960000000
+---- TYPES
+INT, INT, DOUBLE, STRING, TIMESTAMP
+====
+---- QUERY
+# Similar test on an HBase table.
+create external table alltypes_hbase_clone like functional_hbase.alltypes
+====
+---- QUERY
+alter table alltypes_hbase_clone set column stats double_col ('numDVs'='2');
+alter table alltypes_hbase_clone set column stats timestamp_col
('numNulls'='9');
+alter table alltypes_hbase_clone set column stats int_col
('numDVs'='100','numNulls'='20');
+alter table alltypes_hbase_clone set column stats string_col
('maxSize'='555','avgSize'='60');
+====
+---- QUERY
+show column stats alltypes_hbase_clone
+---- LABELS
+COLUMN, TYPE, #DISTINCT VALUES, #NULLS, MAX SIZE, AVG SIZE
+---- RESULTS
+'id','INT',-1,-1,4,4
+'bool_col','BOOLEAN',-1,-1,1,1
+'tinyint_col','TINYINT',-1,-1,1,1
+'smallint_col','SMALLINT',-1,-1,2,2
+'int_col','INT',100,20,4,4
+'bigint_col','BIGINT',-1,-1,8,8
+'float_col','FLOAT',-1,-1,4,4
+'double_col','DOUBLE',2,-1,8,8
+'date_string_col','STRING',-1,-1,-1,-1
+'string_col','STRING',-1,-1,555,60
+'timestamp_col','TIMESTAMP',-1,9,16,16
+'year','INT',-1,-1,4,4
+'month','INT',-1,-1,4,4
+---- TYPES
+STRING, STRING, BIGINT, BIGINT, INT, DOUBLE
+====
+---- QUERY
+# Reset the column stats to an unknown state by setting the values to -1
+alter table alltypes_hbase_clone set column stats double_col ('numDVs'='-1');
+alter table alltypes_hbase_clone set column stats timestamp_col
('numNulls'='-1');
+alter table alltypes_hbase_clone set column stats int_col
('numDVs'='-1','numNulls'='-1');
+alter table alltypes_hbase_clone set column stats string_col
('maxSize'='-1','avgSize'='-1');
+====
+---- QUERY
+show column stats alltypes_hbase_clone
+---- LABELS
+COLUMN, TYPE, #DISTINCT VALUES, #NULLS, MAX SIZE, AVG SIZE
+---- RESULTS
+'id','INT',-1,-1,4,4
+'bool_col','BOOLEAN',-1,-1,1,1
+'tinyint_col','TINYINT',-1,-1,1,1
+'smallint_col','SMALLINT',-1,-1,2,2
+'int_col','INT',-1,-1,4,4
+'bigint_col','BIGINT',-1,-1,8,8
+'float_col','FLOAT',-1,-1,4,4
+'double_col','DOUBLE',-1,-1,8,8
+'date_string_col','STRING',-1,-1,-1,-1
+'string_col','STRING',-1,-1,-1,-1
+'timestamp_col','TIMESTAMP',-1,-1,16,16
+'year','INT',-1,-1,4,4
+'month','INT',-1,-1,4,4
+---- TYPES
+STRING, STRING, BIGINT, BIGINT, INT, DOUBLE
+====
http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/77da3834/tests/metadata/test_ddl.py
----------------------------------------------------------------------
diff --git a/tests/metadata/test_ddl.py b/tests/metadata/test_ddl.py
index 6179d1a..9cb5ce5 100644
--- a/tests/metadata/test_ddl.py
+++ b/tests/metadata/test_ddl.py
@@ -318,6 +318,14 @@ class TestDdlStatements(ImpalaTestSuite):
self.run_test_case('QueryTest/alter-table', vector,
use_db='alter_table_test_db',
multiple_impalad=self._use_multiple_impalad(vector))
+ @pytest.mark.execute_serially
+ @SkipIf.not_default_fs
+ def test_alter_set_column_stats(self, vector):
+ self._create_db('alter_table_test_db', sync=True)
+ self.run_test_case('QueryTest/alter-table-set-column-stats',
+ vector, use_db='alter_table_test_db',
+ multiple_impalad=self._use_multiple_impalad(vector))
+
@SkipIfLocal.hdfs_client
@pytest.mark.execute_serially
def test_drop_partition_with_purge(self, vector):