From c3e07ca121250fe71eed1b6dfc4fbedf8ed71352 Mon Sep 17 00:00:00 2001 From: Sridhar R Manikarnike Date: Sun, 15 Dec 2024 18:36:23 +0000 Subject: [PATCH] [Enhancement] (nereids)implement showColumnStatsCommand in nereids --- .../org/apache/doris/nereids/DorisParser.g4 | 8 +- .../nereids/parser/LogicalPlanBuilder.java | 18 + .../doris/nereids/trees/plans/PlanType.java | 1 + .../commands/ShowColumnStatsCommand.java | 414 ++++++++++++++++++ .../trees/plans/visitor/CommandVisitor.java | 5 + .../test_show_column_stats_command.groovy | 54 +++ 6 files changed, 498 insertions(+), 2 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnStatsCommand.java create mode 100644 regression-test/suites/nereids_p0/show/test_show_column_stats_command.groovy diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 97876c231fec69..5a1ee40579d4ef 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -63,6 +63,7 @@ statementBase | supportedRecoverStatement #supportedRecoverStatementAlias | supportedAdminStatement #supportedAdminStatementAlias | supportedUseStatement #supportedUseStatementAlias + | supportedStatsStatement #supportedStatsStatementAlias | unsupportedStatement #unsupported ; @@ -703,6 +704,11 @@ unsupportedDropStatement | DROP STAGE (IF EXISTS)? name=identifier #dropStage ; +supportedStatsStatement + : SHOW COLUMN CACHED? STATS tableName=multipartIdentifier + columnList=identifierList? partitionSpec? #showColumnStats + ; + unsupportedStatsStatement : ANALYZE TABLE name=multipartIdentifier partitionSpec? columns=identifierList? (WITH analyzeProperties)* propertyClause? #analyzeTable @@ -723,8 +729,6 @@ unsupportedStatsStatement partitionSpec? columnList=identifierList? #showTableStats | SHOW TABLE STATS tableId=INTEGER_VALUE #showTableStats | SHOW INDEX STATS tableName=multipartIdentifier indexId=identifier #showIndexStats - | SHOW COLUMN CACHED? STATS tableName=multipartIdentifier - columnList=identifierList? partitionSpec? #showColumnStats | SHOW COLUMN HISTOGRAM tableName=multipartIdentifier columnList=identifierList #showColumnHistogramStats | SHOW AUTO? ANALYZE tableName=multipartIdentifier? wildWhere? #showAnalyze diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index bb344e1b376deb..9465f57871a9c1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -246,6 +246,7 @@ import org.apache.doris.nereids.DorisParser.ShowBrokerContext; import org.apache.doris.nereids.DorisParser.ShowCharsetContext; import org.apache.doris.nereids.DorisParser.ShowCollationContext; +import org.apache.doris.nereids.DorisParser.ShowColumnStatsContext; import org.apache.doris.nereids.DorisParser.ShowConfigContext; import org.apache.doris.nereids.DorisParser.ShowConstraintContext; import org.apache.doris.nereids.DorisParser.ShowCreateCatalogContext; @@ -566,6 +567,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowColumnStatsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateCatalogCommand; @@ -4571,6 +4573,22 @@ private Expression getWildWhere(DorisParser.WildWhereContext ctx) { } } + @Override + public LogicalPlan visitShowColumnStats(ShowColumnStatsContext ctx) { + List tableNameParts = visitMultipartIdentifier(ctx.tableName); + List colNames = ctx.columnList == null ? ImmutableList.of() : visitIdentifierList(ctx.columnList); + Pair> partitionSpec = visitPartitionSpec(ctx.partitionSpec()); + PartitionNamesInfo partitionNames; + if (partitionSpec.second == null) { + partitionNames = new PartitionNamesInfo(true); //isStar = true + } else { + partitionNames = new PartitionNamesInfo(partitionSpec.first, partitionSpec.second); + } + boolean isCached = ctx.CACHED() != null; + + return new ShowColumnStatsCommand(new TableNameInfo(tableNameParts), colNames, partitionNames, isCached); + } + @Override public ShowViewCommand visitShowView(ShowViewContext ctx) { List tableNameParts = visitMultipartIdentifier(ctx.tableName); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java index dfc129f10b0fd6..63c2bff6d00c63 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/PlanType.java @@ -210,6 +210,7 @@ public enum PlanType { SHOW_BROKER_COMMAND, SHOW_CHARSET_COMMAND, SHOW_COLLATION_COMMAND, + SHOW_COLUMN_STATS_COMMAND, SHOW_CONFIG_COMMAND, SHOW_CREATE_CATALOG_COMMAND, SHOW_CREATE_DATABASE_COMMAND, diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnStatsCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnStatsCommand.java new file mode 100644 index 00000000000000..7d32fd1a0c07de --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ShowColumnStatsCommand.java @@ -0,0 +1,414 @@ +// 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.doris.nereids.trees.plans.commands; + +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.OlapTable; +import org.apache.doris.catalog.Partition; +import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.common.AnalysisException; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.common.ErrorReport; +import org.apache.doris.common.Pair; +import org.apache.doris.datasource.CatalogIf; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.nereids.trees.plans.PlanType; +import org.apache.doris.nereids.trees.plans.commands.info.PartitionNamesInfo; +import org.apache.doris.nereids.trees.plans.commands.info.TableNameInfo; +import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.ShowResultSet; +import org.apache.doris.qe.ShowResultSetMetaData; +import org.apache.doris.qe.StmtExecutor; +import org.apache.doris.statistics.AnalysisManager; +import org.apache.doris.statistics.ColStatsMeta; +import org.apache.doris.statistics.ColumnStatistic; +import org.apache.doris.statistics.PartitionColumnStatistic; +import org.apache.doris.statistics.PartitionColumnStatisticCacheKey; +import org.apache.doris.statistics.ResultRow; +import org.apache.doris.statistics.StatisticsRepository; +import org.apache.doris.statistics.TableStatsMeta; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Represents the command for SHOW COLUMN STATS. + */ +public class ShowColumnStatsCommand extends ShowCommand { + private static final Logger LOG = LogManager.getLogger(ShowColumnStatsCommand.class); + private static final ImmutableList TABLE_COLUMN_TITLE_NAMES = + new ImmutableList.Builder() + .add("column_name") + .add("index_name") + .add("count") + .add("ndv") + .add("num_null") + .add("data_size") + .add("avg_size_byte") + .add("min") + .add("max") + .add("method") + .add("type") + .add("trigger") + .add("query_times") + .add("updated_time") + .add("update_rows") + .add("last_analyze_row_count") + .add("last_analyze_version") + .build(); + + private static final ImmutableList PARTITION_COLUMN_TITLE_NAMES = + new ImmutableList.Builder() + .add("column_name") + .add("partition_name") + .add("index_name") + .add("count") + .add("ndv") + .add("num_null") + .add("min") + .add("max") + .add("data_size") + .add("updated_time") + .add("update_rows") + .add("trigger") + .build(); + + private final TableNameInfo tableName; + private final List columnNames; + private final PartitionNamesInfo partitionNames; + private final boolean isCached; + private TableIf table; + + public ShowColumnStatsCommand(TableNameInfo tableName, List columnNames, + PartitionNamesInfo partitionSpec, boolean isCached) { + super(PlanType.SHOW_COLUMN_STATS_COMMAND); + this.tableName = Objects.requireNonNull(tableName, "Table name cannot be null"); + this.columnNames = columnNames; + this.partitionNames = partitionSpec; + this.isCached = isCached; + } + + private void validate(ConnectContext ctx) throws Exception { + tableName.analyze(ctx); + if (partitionNames != null) { + partitionNames.validate(ctx); + } + CatalogIf catalog = Env.getCurrentEnv().getCatalogMgr().getCatalog(tableName.getCtl()); + if (catalog == null) { + ErrorReport.reportAnalysisException("Catalog: %s not exists", tableName.getCtl()); + } + DatabaseIf db = catalog.getDb(tableName.getDb()).orElse(null); + if (db == null) { + ErrorReport.reportAnalysisException("DB: %s not exists", tableName.getDb()); + } + table = db.getTable(tableName.getTbl()).orElse(null); + if (table == null) { + ErrorReport.reportAnalysisException("Table: %s not exists", tableName.getTbl()); + } + + if (!Env.getCurrentEnv().getAccessManager() + .checkTblPriv(ConnectContext.get(), tableName.getCtl(), tableName.getDb(), tableName.getTbl(), + PrivPredicate.SHOW)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "Permission denied", + ConnectContext.get().getQualifiedUser(), ConnectContext.get().getRemoteIP(), + tableName.getDb() + ": " + tableName.getTbl()); + } + + if (columnNames != null) { + for (String name : columnNames) { + if (table.getColumn(name) == null) { + ErrorReport.reportAnalysisException("Column: %s not exists", name); + } + } + } + } + + private Map getCachedPartitionColumnStats( + Set columnNames, List partitionNames, TableIf tableIf) { + Map ret = new HashMap<>(); + long catalogId = tableIf.getDatabase().getCatalog().getId(); + long dbId = tableIf.getDatabase().getId(); + long tableId = tableIf.getId(); + for (String colName : columnNames) { + // Olap base index use -1 as index id. + List indexIds = Lists.newArrayList(); + if (tableIf instanceof OlapTable) { + indexIds = ((OlapTable) tableIf).getMvColumnIndexIds(colName); + } else { + indexIds.add(-1L); + } + for (long indexId : indexIds) { + String indexName = tableIf.getName(); + if (tableIf instanceof OlapTable) { + OlapTable olapTable = (OlapTable) tableIf; + indexName = olapTable.getIndexNameById(indexId == -1 ? olapTable.getBaseIndexId() : indexId); + } + if (indexName == null) { + continue; + } + for (String partName : partitionNames) { + PartitionColumnStatistic partitionStatistics = Env.getCurrentEnv().getStatisticsCache() + .getPartitionColumnStatistics(catalogId, dbId, tableId, indexId, partName, colName); + ret.put(new PartitionColumnStatisticCacheKey(catalogId, dbId, tableId, indexId, partName, colName), + partitionStatistics); + } + } + } + return ret; + } + + private void getStatsForAllColumns(List, ColumnStatistic>> columnStatistics, + TableIf tableIf) { + List resultRows = StatisticsRepository.queryColumnStatisticsForTable( + tableIf.getDatabase().getCatalog().getId(), tableIf.getDatabase().getId(), tableIf.getId()); + // row[4] is index id, row[5] is column name. + for (ResultRow row : resultRows) { + String indexName = tableIf.getName(); + long indexId = Long.parseLong(row.get(4)); + if (tableIf instanceof OlapTable) { + OlapTable olapTable = (OlapTable) tableIf; + indexName = olapTable.getIndexNameById(indexId == -1 ? olapTable.getBaseIndexId() : indexId); + } + if (indexName == null) { + continue; + } + try { + columnStatistics.add(Pair.of(Pair.of(indexName, row.get(5)), ColumnStatistic.fromResultRow(row))); + } catch (Exception e) { + LOG.warn("Failed to deserialize column statistics. reason: [{}]. Row [{}]", e.getMessage(), row); + if (LOG.isDebugEnabled()) { + LOG.debug(e); + } + } + } + } + + private void getStatsForSpecifiedColumns(List, ColumnStatistic>> columnStatistics, + Set columnNames, TableIf tableIf, boolean showCache) + throws AnalysisException { + for (String colName : columnNames) { + // Olap base index use -1 as index id. + List indexIds = Lists.newArrayList(); + if (tableIf instanceof OlapTable) { + indexIds = ((OlapTable) tableIf).getMvColumnIndexIds(colName); + } else { + indexIds.add(-1L); + } + for (long indexId : indexIds) { + String indexName = tableIf.getName(); + if (tableIf instanceof OlapTable) { + OlapTable olapTable = (OlapTable) tableIf; + indexName = olapTable.getIndexNameById(indexId == -1 ? olapTable.getBaseIndexId() : indexId); + } + if (indexName == null) { + continue; + } + // Show column statistics in columnStatisticsCache. + ColumnStatistic columnStatistic; + if (showCache) { + columnStatistic = Env.getCurrentEnv().getStatisticsCache().getColumnStatistics( + tableIf.getDatabase().getCatalog().getId(), + tableIf.getDatabase().getId(), tableIf.getId(), indexId, colName); + } else { + columnStatistic = StatisticsRepository.queryColumnStatisticsByName( + tableIf.getDatabase().getCatalog().getId(), + tableIf.getDatabase().getId(), tableIf.getId(), indexId, colName); + } + columnStatistics.add(Pair.of(Pair.of(indexName, colName), columnStatistic)); + } + } + } + + private ShowResultSetMetaData getMetaData() { + ShowResultSetMetaData.Builder builder = ShowResultSetMetaData.builder(); + ImmutableList titles = partitionNames == null ? TABLE_COLUMN_TITLE_NAMES : PARTITION_COLUMN_TITLE_NAMES; + for (String title : titles) { + builder.addColumn(new Column(title, ScalarType.createVarchar(30))); + } + return builder.build(); + } + + private ShowResultSet constructResultSet(List, ColumnStatistic>> columnStatistics) { + List> result = Lists.newArrayList(); + AnalysisManager analysisManager = Env.getCurrentEnv().getAnalysisManager(); + columnStatistics.forEach(p -> { + if (p.second.isUnKnown) { + return; + } + List row = Lists.newArrayList(); + // p data structure is Pair, ColumnStatistic> + row.add(p.first.second); + row.add(p.first.first); + row.add(String.valueOf(p.second.count)); + row.add(String.valueOf(p.second.ndv)); + row.add(String.valueOf(p.second.numNulls)); + row.add(String.valueOf(p.second.dataSize)); + row.add(String.valueOf(p.second.avgSizeByte)); + row.add(String.valueOf(p.second.minExpr == null ? "N/A" : p.second.minExpr.toSql())); + row.add(String.valueOf(p.second.maxExpr == null ? "N/A" : p.second.maxExpr.toSql())); + ColStatsMeta colStatsMeta = analysisManager.findColStatsMeta(table.getId(), p.first.first, p.first.second); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.analysisMethod)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.analysisType)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.jobType)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.queriedTimes)); + row.add(String.valueOf(p.second.updatedTime)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.updatedRows)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.rowCount)); + row.add(String.valueOf(colStatsMeta == null ? "N/A" : colStatsMeta.tableVersion)); + result.add(row); + }); + return new ShowResultSet(getMetaData(), result); + } + + private ShowResultSet constructPartitionResultSet(List resultRows, TableIf tableIf) { + List> result = Lists.newArrayList(); + for (ResultRow r : resultRows) { + List row = Lists.newArrayList(); + row.add(r.get(0)); // column_name + row.add(r.get(1)); // partition_name + long indexId = Long.parseLong(r.get(2)); + String indexName = indexId == -1 ? tableIf.getName() : ((OlapTable) tableIf).getIndexNameById(indexId); + row.add(indexName); // index_name. + row.add(r.get(3)); // count + row.add(r.get(4)); // ndv + row.add(r.get(5)); // num_null + row.add(r.get(6)); // min + row.add(r.get(7)); // max + row.add(r.get(8)); // data_size + row.add(r.get(9)); // updated_time + String updateRows = "N/A"; + TableStatsMeta tableStats = Env.getCurrentEnv().getAnalysisManager().findTableStatsStatus(tableIf.getId()); + ColStatsMeta columnStatsMeta = null; + if (tableStats != null && tableIf instanceof OlapTable) { + OlapTable olapTable = (OlapTable) tableIf; + columnStatsMeta = tableStats.findColumnStatsMeta(indexName, r.get(0)); + if (columnStatsMeta != null && columnStatsMeta.partitionUpdateRows != null) { + Partition partition = olapTable.getPartition(r.get(1)); + if (partition != null) { + Long rows = columnStatsMeta.partitionUpdateRows.get(partition.getId()); + if (rows != null) { + updateRows = rows.toString(); + } + } + } + } + row.add(updateRows); // update_rows + row.add(columnStatsMeta == null ? "N/A" : columnStatsMeta.jobType.name()); // trigger. Manual or System + result.add(row); + } + return new ShowResultSet(getMetaData(), result); + } + + private ShowResultSet constructPartitionCachedColumnStats( + Map resultMap, TableIf tableIf) { + List> result = Lists.newArrayList(); + for (Map.Entry entry : resultMap.entrySet()) { + PartitionColumnStatisticCacheKey key = entry.getKey(); + PartitionColumnStatistic value = entry.getValue(); + if (value == null || value.isUnKnown) { + continue; + } + List row = Lists.newArrayList(); + row.add(key.colName); // column_name + row.add(key.partId); // partition_name + long indexId = key.idxId; + String indexName = indexId == -1 ? tableIf.getName() : ((OlapTable) tableIf).getIndexNameById(indexId); + row.add(indexName); // index_name. + row.add(String.valueOf(value.count)); // count + row.add(String.valueOf(value.ndv.estimateCardinality())); // ndv + row.add(String.valueOf(value.numNulls)); // num_null + row.add(String.valueOf(value.minExpr == null ? "N/A" : value.minExpr.toSql())); // min + row.add(String.valueOf(value.maxExpr == null ? "N/A" : value.maxExpr.toSql())); // max + row.add(String.valueOf(value.dataSize)); // data_size + row.add(value.updatedTime); // updated_time + row.add("N/A"); // update_rows + row.add("N/A"); // trigger + result.add(row); + } + return new ShowResultSet(getMetaData(), result); + } + + private PartitionNamesInfo getPartitionNames() { + return partitionNames; + } + + private Set getColumnNames() { + if (columnNames != null) { + return Sets.newHashSet(columnNames); + } + return table.getColumns().stream() + .map(Column::getName).collect(Collectors.toSet()); + } + + private boolean isAllColumns() { + return columnNames == null; + } + + @Override + public ShowResultSet doRun(ConnectContext ctx, StmtExecutor executor) throws Exception { + ShowResultSet resultSet; + validate(ctx); + TableIf tableIf = table; + List, ColumnStatistic>> columnStatistics = new ArrayList<>(); + Set columnNames = getColumnNames(); + PartitionNamesInfo partitionNames = getPartitionNames(); + boolean showCache = isCached; + boolean isAllColumns = isAllColumns(); + if (partitionNames != null) { + List partNames = partitionNames.getPartitionNames() == null + ? new ArrayList<>(tableIf.getPartitionNames()) + : partitionNames.getPartitionNames(); + if (showCache) { + resultSet = this.constructPartitionCachedColumnStats( + getCachedPartitionColumnStats(columnNames, partNames, tableIf), tableIf); + } else { + List partitionColumnStats = + StatisticsRepository.queryColumnStatisticsByPartitions(tableIf, columnNames, partNames); + resultSet = constructPartitionResultSet(partitionColumnStats, tableIf); + } + } else { + if (isAllColumns && !showCache) { + getStatsForAllColumns(columnStatistics, tableIf); + } else { + getStatsForSpecifiedColumns(columnStatistics, columnNames, tableIf, showCache); + } + resultSet = constructResultSet(columnStatistics); + } + return resultSet; + } + + @Override + public R accept(PlanVisitor visitor, C context) { + return visitor.visitShowColumnStatsCommand(this, context); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java index d3749e94d57d0f..e9d567529bac46 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java @@ -91,6 +91,7 @@ import org.apache.doris.nereids.trees.plans.commands.ShowBrokerCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCharsetCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCollationCommand; +import org.apache.doris.nereids.trees.plans.commands.ShowColumnStatsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConfigCommand; import org.apache.doris.nereids.trees.plans.commands.ShowConstraintsCommand; import org.apache.doris.nereids.trees.plans.commands.ShowCreateCatalogCommand; @@ -389,6 +390,10 @@ default R visitSetOptionsCommand(SetOptionsCommand setOptionsCommand, C context) return visitCommand(setOptionsCommand, context); } + default R visitShowColumnStatsCommand(ShowColumnStatsCommand showColumnStatsCommand, C context) { + return visitCommand(showColumnStatsCommand, context); + } + default R visitSetTransactionCommand(SetTransactionCommand setTransactionCommand, C context) { return visitCommand(setTransactionCommand, context); } diff --git a/regression-test/suites/nereids_p0/show/test_show_column_stats_command.groovy b/regression-test/suites/nereids_p0/show/test_show_column_stats_command.groovy new file mode 100644 index 00000000000000..4aeb08961f5c63 --- /dev/null +++ b/regression-test/suites/nereids_p0/show/test_show_column_stats_command.groovy @@ -0,0 +1,54 @@ +// 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. + +suite("test_show_column_stats_command", "nereids_p0") { + def dbName = "test_show_column_stats_db" + def tableName = "test_show_column_stats_table" + + try { + // Drop and recreate the database + sql "DROP DATABASE IF EXISTS ${dbName}" + sql "CREATE DATABASE ${dbName}" + sql "USE ${dbName}" + + // Create the table + sql """ + CREATE TABLE ${tableName} ( + id INT, + name STRING, + score DOUBLE + ) + DUPLICATE KEY(id) + DISTRIBUTED BY HASH(id) BUCKETS 3 + PROPERTIES("replication_num" = "1") + """ + + // Insert data + sql """INSERT INTO ${tableName} VALUES (1, 'Alice', 72.3), (2, 'Bob', 88.0), (3, 'Charlie', 95.5)""" + + // Analyze the table for column stats + sql "ANALYZE TABLE ${dbName}.${tableName}" + + // Execute the SHOW COLUMN STATS command + checkNereidsExecute(""" + SHOW COLUMN STATS ${dbName}.${tableName} (id, name, score) + """) + } finally { + // Cleanup + sql "DROP DATABASE IF EXISTS ${dbName}" + } +}