From df3f4df8d5c8542e9cb1bf3be680be52d950451f Mon Sep 17 00:00:00 2001 From: Jiexi Lin Date: Tue, 2 Oct 2018 11:35:22 -0700 Subject: [PATCH] ANALYZE statement: Analyzer, planner and execution Extracted-From: https://github.com/prestodb/presto/pull/11376 --- .../prestosql/connector/ConnectorManager.java | 11 + .../prestosql/metadata/AnalyzeMetadata.java | 40 ++++ .../metadata/AnalyzePropertyManager.java | 25 +++ .../metadata/AnalyzeTableHandle.java | 87 ++++++++ .../java/io/prestosql/metadata/Metadata.java | 19 ++ .../prestosql/metadata/MetadataManager.java | 59 ++++++ .../operator/StatisticsWriterOperator.java | 197 ++++++++++++++++++ .../io/prestosql/server/ServerMainModule.java | 4 + .../io/prestosql/sql/analyzer/Analysis.java | 11 + .../sql/analyzer/StatementAnalyzer.java | 48 +++++ .../planner/DistributedExecutionPlanner.java | 7 + .../sql/planner/LocalExecutionPlanner.java | 18 ++ .../prestosql/sql/planner/LogicalPlanner.java | 59 +++++- .../prestosql/sql/planner/PlanFragmenter.java | 8 + .../planner/optimizations/AddExchanges.java | 20 ++ .../optimizations/AddLocalExchanges.java | 9 + .../optimizations/BeginTableWrite.java | 19 ++ .../optimizations/PropertyDerivations.java | 9 + .../PruneUnreferencedOutputs.java | 14 ++ .../StreamPropertyDerivations.java | 9 + .../planner/optimizations/SymbolMapper.java | 12 ++ .../UnaliasSymbolReferences.java | 9 + .../prestosql/sql/planner/plan/PlanNode.java | 4 +- .../sql/planner/plan/PlanVisitor.java | 5 + .../planner/plan/StatisticsWriterNode.java | 172 +++++++++++++++ .../sql/planner/planPrinter/PlanPrinter.java | 10 + .../sanity/ValidateDependenciesChecker.java | 18 ++ .../prestosql/testing/LocalQueryRunner.java | 2 + .../io/prestosql/testing/TestingMetadata.java | 6 + .../io/prestosql/util/GraphvizPrinter.java | 12 +- .../io/prestosql/util/StatementUtils.java | 2 + .../execution/TestResetSessionTask.java | 2 + .../prestosql/execution/TestSetPathTask.java | 2 + .../prestosql/execution/TestSetRoleTask.java | 2 + .../execution/TestSetSessionTask.java | 2 + .../metadata/AbstractMockMetadata.java | 30 +++ .../TestInformationSchemaMetadata.java | 1 + .../prestosql/sql/analyzer/TestAnalyzer.java | 29 ++- .../sql/planner/TestLogicalPlanner.java | 16 ++ .../io/prestosql/spi/StandardErrorCode.java | 1 + .../io/prestosql/spi/connector/Connector.java | 8 + .../connector/ConnectorAnalyzeMetadata.java | 40 ++++ .../spi/connector/ConnectorMetadata.java | 35 +++- .../ClassLoaderSafeConnectorMetadata.java | 32 +++ .../spi/resourcegroups/QueryType.java | 1 + .../prestosql/tests/TestLocalQueryRunner.java | 13 ++ .../prestosql/plugin/tpch/TpchMetadata.java | 28 +++ 47 files changed, 1161 insertions(+), 6 deletions(-) create mode 100644 presto-main/src/main/java/io/prestosql/metadata/AnalyzeMetadata.java create mode 100644 presto-main/src/main/java/io/prestosql/metadata/AnalyzePropertyManager.java create mode 100644 presto-main/src/main/java/io/prestosql/metadata/AnalyzeTableHandle.java create mode 100644 presto-main/src/main/java/io/prestosql/operator/StatisticsWriterOperator.java create mode 100644 presto-main/src/main/java/io/prestosql/sql/planner/plan/StatisticsWriterNode.java create mode 100644 presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorAnalyzeMetadata.java diff --git a/presto-main/src/main/java/io/prestosql/connector/ConnectorManager.java b/presto-main/src/main/java/io/prestosql/connector/ConnectorManager.java index 628dbf231bd7..5ae7d0f86383 100644 --- a/presto-main/src/main/java/io/prestosql/connector/ConnectorManager.java +++ b/presto-main/src/main/java/io/prestosql/connector/ConnectorManager.java @@ -267,6 +267,7 @@ private synchronized void addConnectorInternal(MaterializedConnector connector) metadataManager.getTablePropertyManager().addProperties(connectorId, connector.getTableProperties()); metadataManager.getColumnPropertyManager().addProperties(connectorId, connector.getColumnProperties()); metadataManager.getSchemaPropertyManager().addProperties(connectorId, connector.getSchemaProperties()); + metadataManager.getAnalyzePropertyManager().addProperties(connectorId, connector.getAnalyzeProperties()); metadataManager.getSessionPropertyManager().addConnectorSessionProperties(connectorId, connector.getSessionProperties()); } @@ -337,6 +338,7 @@ private static class MaterializedConnector private final List> tableProperties; private final List> schemaProperties; private final List> columnProperties; + private final List> analyzeProperties; public MaterializedConnector(ConnectorId connectorId, Connector connector) { @@ -425,6 +427,10 @@ public MaterializedConnector(ConnectorId connectorId, Connector connector) List> columnProperties = connector.getColumnProperties(); requireNonNull(columnProperties, "Connector %s returned a null column properties set"); this.columnProperties = ImmutableList.copyOf(columnProperties); + + List> analyzeProperties = connector.getAnalyzeProperties(); + requireNonNull(analyzeProperties, "Connector %s returned a null analyze properties set"); + this.analyzeProperties = ImmutableList.copyOf(analyzeProperties); } public ConnectorId getConnectorId() @@ -496,5 +502,10 @@ public List> getSchemaProperties() { return schemaProperties; } + + public List> getAnalyzeProperties() + { + return analyzeProperties; + } } } diff --git a/presto-main/src/main/java/io/prestosql/metadata/AnalyzeMetadata.java b/presto-main/src/main/java/io/prestosql/metadata/AnalyzeMetadata.java new file mode 100644 index 000000000000..7ea134663e57 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/metadata/AnalyzeMetadata.java @@ -0,0 +1,40 @@ +/* + * 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 io.prestosql.metadata; + +import io.prestosql.spi.statistics.TableStatisticsMetadata; + +import static java.util.Objects.requireNonNull; + +public class AnalyzeMetadata +{ + private final TableStatisticsMetadata statisticsMetadata; + private final TableHandle tableHandle; + + public AnalyzeMetadata(TableStatisticsMetadata statisticsMetadata, TableHandle tableHandle) + { + this.statisticsMetadata = requireNonNull(statisticsMetadata, "statisticsMetadata is null"); + this.tableHandle = requireNonNull(tableHandle, "tableHandle is null"); + } + + public TableStatisticsMetadata getStatisticsMetadata() + { + return statisticsMetadata; + } + + public TableHandle getTableHandle() + { + return tableHandle; + } +} diff --git a/presto-main/src/main/java/io/prestosql/metadata/AnalyzePropertyManager.java b/presto-main/src/main/java/io/prestosql/metadata/AnalyzePropertyManager.java new file mode 100644 index 000000000000..84b4de98356d --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/metadata/AnalyzePropertyManager.java @@ -0,0 +1,25 @@ +/* + * 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 io.prestosql.metadata; + +import static io.prestosql.spi.StandardErrorCode.INVALID_ANALYZE_PROPERTY; + +public class AnalyzePropertyManager + extends AbstractPropertyManager +{ + public AnalyzePropertyManager() + { + super("analyze", INVALID_ANALYZE_PROPERTY); + } +} diff --git a/presto-main/src/main/java/io/prestosql/metadata/AnalyzeTableHandle.java b/presto-main/src/main/java/io/prestosql/metadata/AnalyzeTableHandle.java new file mode 100644 index 000000000000..d5bc3d7e9e11 --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/metadata/AnalyzeTableHandle.java @@ -0,0 +1,87 @@ +/* + * 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 io.prestosql.metadata; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.prestosql.connector.ConnectorId; +import io.prestosql.spi.connector.ConnectorTableHandle; +import io.prestosql.spi.connector.ConnectorTransactionHandle; + +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +public class AnalyzeTableHandle +{ + private final ConnectorId connectorId; + private final ConnectorTransactionHandle transactionHandle; + private final ConnectorTableHandle connectorHandle; + + @JsonCreator + public AnalyzeTableHandle( + @JsonProperty("connectorId") ConnectorId connectorId, + @JsonProperty("transactionHandle") ConnectorTransactionHandle transactionHandle, + @JsonProperty("connectorHandle") ConnectorTableHandle connectorHandle) + { + this.connectorId = requireNonNull(connectorId, "connectorId is null"); + this.transactionHandle = requireNonNull(transactionHandle, "transactionHandle is null"); + this.connectorHandle = requireNonNull(connectorHandle, "connectorHandle is null"); + } + + @JsonProperty + public ConnectorId getConnectorId() + { + return connectorId; + } + + @JsonProperty + public ConnectorTableHandle getConnectorHandle() + { + return connectorHandle; + } + + @JsonProperty + public ConnectorTransactionHandle getTransactionHandle() + { + return transactionHandle; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AnalyzeTableHandle that = (AnalyzeTableHandle) o; + return Objects.equals(connectorId, that.connectorId) && + Objects.equals(transactionHandle, that.transactionHandle) && + Objects.equals(connectorHandle, that.connectorHandle); + } + + @Override + public int hashCode() + { + return Objects.hash(connectorId, transactionHandle, connectorHandle); + } + + @Override + public String toString() + { + return connectorId + ":" + connectorHandle + ":" + transactionHandle; + } +} diff --git a/presto-main/src/main/java/io/prestosql/metadata/Metadata.java b/presto-main/src/main/java/io/prestosql/metadata/Metadata.java index b768ab284ab1..d8f505f83d1f 100644 --- a/presto-main/src/main/java/io/prestosql/metadata/Metadata.java +++ b/presto-main/src/main/java/io/prestosql/metadata/Metadata.java @@ -71,6 +71,8 @@ public interface Metadata Optional getSystemTable(Session session, QualifiedObjectName tableName); + Optional getTableHandleForStatisticsCollection(Session session, QualifiedObjectName tableName, Map analyzeProperties); + List getLayouts(Session session, TableHandle tableHandle, Constraint constraint, Optional> desiredColumns); TableLayout getLayout(Session session, TableLayoutHandle handle); @@ -193,8 +195,23 @@ public interface Metadata /** * Describes statistics that must be collected during a write. */ + TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(Session session, String catalogName, ConnectorTableMetadata tableMetadata); + + /** + * Describe statistics that must be collected during a statistics collection + */ TableStatisticsMetadata getStatisticsCollectionMetadata(Session session, String catalogName, ConnectorTableMetadata tableMetadata); + /** + * Begin statistics collection + */ + AnalyzeTableHandle beginStatisticsCollection(Session session, TableHandle tableHandle); + + /** + * Finish statistics collection + */ + void finishStatisticsCollection(Session session, AnalyzeTableHandle tableHandle, Collection computedStatistics); + /** * Start a SELECT/UPDATE/INSERT/DELETE query */ @@ -361,4 +378,6 @@ public interface Metadata TablePropertyManager getTablePropertyManager(); ColumnPropertyManager getColumnPropertyManager(); + + AnalyzePropertyManager getAnalyzePropertyManager(); } diff --git a/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java b/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java index 555368ed8c17..411ae09220f9 100644 --- a/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java +++ b/presto-main/src/main/java/io/prestosql/metadata/MetadataManager.java @@ -123,6 +123,7 @@ public class MetadataManager private final SchemaPropertyManager schemaPropertyManager; private final TablePropertyManager tablePropertyManager; private final ColumnPropertyManager columnPropertyManager; + private final AnalyzePropertyManager analyzePropertyManager; private final TransactionManager transactionManager; private final ConcurrentMap> catalogsByQueryId = new ConcurrentHashMap<>(); @@ -134,6 +135,7 @@ public MetadataManager(FeaturesConfig featuresConfig, SchemaPropertyManager schemaPropertyManager, TablePropertyManager tablePropertyManager, ColumnPropertyManager columnPropertyManager, + AnalyzePropertyManager analyzePropertyManager, TransactionManager transactionManager) { this(featuresConfig, @@ -144,6 +146,7 @@ public MetadataManager(FeaturesConfig featuresConfig, schemaPropertyManager, tablePropertyManager, columnPropertyManager, + analyzePropertyManager, transactionManager); } @@ -156,6 +159,7 @@ public MetadataManager(FeaturesConfig featuresConfig, SchemaPropertyManager schemaPropertyManager, TablePropertyManager tablePropertyManager, ColumnPropertyManager columnPropertyManager, + AnalyzePropertyManager analyzePropertyManager, TransactionManager transactionManager) { functions = new FunctionRegistry(typeManager, blockEncodingSerde, featuresConfig); @@ -167,6 +171,7 @@ public MetadataManager(FeaturesConfig featuresConfig, this.schemaPropertyManager = requireNonNull(schemaPropertyManager, "schemaPropertyManager is null"); this.tablePropertyManager = requireNonNull(tablePropertyManager, "tablePropertyManager is null"); this.columnPropertyManager = requireNonNull(columnPropertyManager, "columnPropertyManager is null"); + this.analyzePropertyManager = requireNonNull(analyzePropertyManager, "analyzePropertyManager is null"); this.transactionManager = requireNonNull(transactionManager, "transactionManager is null"); verifyComparableOrderableContract(); @@ -203,6 +208,7 @@ public static MetadataManager createTestMetadataManager(TransactionManager trans new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); } @@ -328,6 +334,25 @@ public Optional getTableHandle(Session session, QualifiedObjectName return Optional.empty(); } + @Override + public Optional getTableHandleForStatisticsCollection(Session session, QualifiedObjectName table, Map analyzeProperties) + { + requireNonNull(table, "table is null"); + + Optional catalog = getOptionalCatalogMetadata(session, table.getCatalogName()); + if (catalog.isPresent()) { + CatalogMetadata catalogMetadata = catalog.get(); + ConnectorId connectorId = catalogMetadata.getConnectorId(session, table); + ConnectorMetadata metadata = catalogMetadata.getMetadataFor(connectorId); + + ConnectorTableHandle tableHandle = metadata.getTableHandleForStatisticsCollection(session.toConnectorSession(connectorId), table.asSchemaTableName(), analyzeProperties); + if (tableHandle != null) { + return Optional.of(new TableHandle(connectorId, tableHandle)); + } + } + return Optional.empty(); + } + @Override public Optional getSystemTable(Session session, QualifiedObjectName tableName) { @@ -621,6 +646,15 @@ public Optional getInsertLayout(Session session, TableHandle tab .map(layout -> new NewTableLayout(connectorId, catalogMetadata.getTransactionHandleFor(connectorId), layout)); } + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(Session session, String catalogName, ConnectorTableMetadata tableMetadata) + { + CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, catalogName); + ConnectorMetadata metadata = catalogMetadata.getMetadata(); + ConnectorId connectorId = catalogMetadata.getConnectorId(); + return metadata.getStatisticsCollectionMetadataForWrite(session.toConnectorSession(connectorId), tableMetadata); + } + @Override public TableStatisticsMetadata getStatisticsCollectionMetadata(Session session, String catalogName, ConnectorTableMetadata tableMetadata) { @@ -630,6 +664,26 @@ public TableStatisticsMetadata getStatisticsCollectionMetadata(Session session, return metadata.getStatisticsCollectionMetadata(session.toConnectorSession(connectorId), tableMetadata); } + @Override + public AnalyzeTableHandle beginStatisticsCollection(Session session, TableHandle tableHandle) + { + ConnectorId connectorId = tableHandle.getConnectorId(); + CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, connectorId); + ConnectorMetadata metadata = catalogMetadata.getMetadata(); + + ConnectorTransactionHandle transactionHandle = catalogMetadata.getTransactionHandleFor(connectorId); + ConnectorTableHandle connectorTableHandle = metadata.beginStatisticsCollection(session.toConnectorSession(connectorId), tableHandle.getConnectorHandle()); + return new AnalyzeTableHandle(connectorId, transactionHandle, connectorTableHandle); + } + + @Override + public void finishStatisticsCollection(Session session, AnalyzeTableHandle tableHandle, Collection computedStatistics) + { + ConnectorId connectorId = tableHandle.getConnectorId(); + CatalogMetadata catalogMetadata = getCatalogMetadataForWrite(session, connectorId); + catalogMetadata.getMetadata().finishStatisticsCollection(session.toConnectorSession(), tableHandle.getConnectorHandle(), computedStatistics); + } + @Override public Optional getNewTableLayout(Session session, String catalogName, ConnectorTableMetadata tableMetadata) { @@ -1064,6 +1118,11 @@ public ColumnPropertyManager getColumnPropertyManager() return columnPropertyManager; } + public AnalyzePropertyManager getAnalyzePropertyManager() + { + return analyzePropertyManager; + } + private ViewDefinition deserializeView(String data) { try { diff --git a/presto-main/src/main/java/io/prestosql/operator/StatisticsWriterOperator.java b/presto-main/src/main/java/io/prestosql/operator/StatisticsWriterOperator.java new file mode 100644 index 000000000000..4acd45a796ed --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/operator/StatisticsWriterOperator.java @@ -0,0 +1,197 @@ +/* + * 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 io.prestosql.operator; + +import com.google.common.collect.ImmutableList; +import io.prestosql.spi.Page; +import io.prestosql.spi.PageBuilder; +import io.prestosql.spi.block.Block; +import io.prestosql.spi.block.BlockBuilder; +import io.prestosql.spi.statistics.ComputedStatistics; +import io.prestosql.spi.type.Type; +import io.prestosql.sql.planner.plan.PlanNodeId; +import io.prestosql.sql.planner.plan.StatisticAggregationsDescriptor; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +import static com.google.common.base.Preconditions.checkState; +import static io.prestosql.spi.statistics.TableStatisticType.ROW_COUNT; +import static io.prestosql.spi.type.BigintType.BIGINT; +import static java.util.Objects.requireNonNull; + +public class StatisticsWriterOperator + implements Operator +{ + public static final List TYPES = ImmutableList.of(BIGINT); + + public static class StatisticsWriterOperatorFactory + implements OperatorFactory + { + private final int operatorId; + private final PlanNodeId planNodeId; + private final StatisticsWriter statisticsWriter; + private final boolean rowCountEnabled; + private final StatisticAggregationsDescriptor descriptor; + private boolean closed; + + public StatisticsWriterOperatorFactory(int operatorId, PlanNodeId planNodeId, StatisticsWriter statisticsWriter, boolean rowCountEnabled, StatisticAggregationsDescriptor descriptor) + { + this.operatorId = operatorId; + this.planNodeId = requireNonNull(planNodeId, "planNodeId is null"); + this.statisticsWriter = requireNonNull(statisticsWriter, "statisticsWriter is null"); + this.rowCountEnabled = rowCountEnabled; + this.descriptor = requireNonNull(descriptor, "descriptor is null"); + } + + @Override + public Operator createOperator(DriverContext driverContext) + { + checkState(!closed, "Factory is already closed"); + OperatorContext context = driverContext.addOperatorContext(operatorId, planNodeId, StatisticsWriterOperator.class.getSimpleName()); + return new StatisticsWriterOperator(context, statisticsWriter, descriptor, rowCountEnabled); + } + + @Override + public void noMoreOperators() + { + closed = true; + } + + @Override + public OperatorFactory duplicate() + { + return new StatisticsWriterOperatorFactory(operatorId, planNodeId, statisticsWriter, rowCountEnabled, descriptor); + } + } + + private enum State + { + RUNNING, FINISHING, FINISHED + } + + private final OperatorContext operatorContext; + private final StatisticsWriter statisticsWriter; + private final StatisticAggregationsDescriptor descriptor; + private final boolean rowCountEnabled; + + private State state = State.RUNNING; + private final ImmutableList.Builder computedStatisticsBuilder = ImmutableList.builder(); + + public StatisticsWriterOperator(OperatorContext operatorContext, StatisticsWriter statisticsWriter, StatisticAggregationsDescriptor descriptor, boolean rowCountEnabled) + { + this.operatorContext = requireNonNull(operatorContext, "operatorContext is null"); + this.statisticsWriter = requireNonNull(statisticsWriter, "statisticsWriter is null"); + this.descriptor = requireNonNull(descriptor, "descriptor is null"); + this.rowCountEnabled = rowCountEnabled; + } + + @Override + public OperatorContext getOperatorContext() + { + return operatorContext; + } + + @Override + public boolean needsInput() + { + return state == State.RUNNING; + } + + @Override + public void addInput(Page page) + { + requireNonNull(page, "page is null"); + checkState(state == State.RUNNING, "Operator is %s", state); + + for (int position = 0; position < page.getPositionCount(); position++) { + computedStatisticsBuilder.add(getComputedStatistics(page, position)); + } + } + + @Override + public Page getOutput() + { + if (state != State.FINISHING) { + return null; + } + state = State.FINISHED; + + Collection computedStatistics = computedStatisticsBuilder.build(); + statisticsWriter.writeStatistics(computedStatistics); + + // output page will only be constructed once, + // so a new PageBuilder is constructed (instead of using PageBuilder.reset) + PageBuilder page = new PageBuilder(1, TYPES); + page.declarePosition(); + BlockBuilder rowsBuilder = page.getBlockBuilder(0); + if (rowCountEnabled) { + BIGINT.writeLong(rowsBuilder, getRowCount(computedStatistics)); + } + else { + rowsBuilder.appendNull(); + } + + return page.build(); + } + + @Override + public void finish() + { + if (state == State.RUNNING) { + state = State.FINISHING; + } + } + + @Override + public boolean isFinished() + { + return state == State.FINISHED; + } + + private ComputedStatistics getComputedStatistics(Page page, int position) + { + ImmutableList.Builder groupingColumns = ImmutableList.builder(); + ImmutableList.Builder groupingValues = ImmutableList.builder(); + descriptor.getGrouping().forEach((column, channel) -> { + groupingColumns.add(column); + groupingValues.add(page.getBlock(channel).getSingleValueBlock(position)); + }); + + ComputedStatistics.Builder statistics = ComputedStatistics.builder(groupingColumns.build(), groupingValues.build()); + + descriptor.getTableStatistics().forEach((type, channel) -> + statistics.addTableStatistic(type, page.getBlock(channel).getSingleValueBlock(position))); + + descriptor.getColumnStatistics().forEach((metadata, channel) -> statistics.addColumnStatistic(metadata, page.getBlock(channel).getSingleValueBlock(position))); + + return statistics.build(); + } + + private static long getRowCount(Collection computedStatistics) + { + return computedStatistics.stream() + .map(statistics -> statistics.getTableStatistics().get(ROW_COUNT)) + .filter(Objects::nonNull) + .mapToLong(block -> BIGINT.getLong(block, 0)) + .reduce((first, second) -> first + second) + .orElse(0L); + } + + public interface StatisticsWriter + { + void writeStatistics(Collection computedStatistics); + } +} diff --git a/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java b/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java index 064cf6ebde53..d582e72da902 100644 --- a/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java +++ b/presto-main/src/main/java/io/prestosql/server/ServerMainModule.java @@ -66,6 +66,7 @@ import io.prestosql.memory.MemoryResource; import io.prestosql.memory.NodeMemoryConfig; import io.prestosql.memory.ReservedSystemMemoryConfig; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; import io.prestosql.metadata.DiscoveryNodeManager; @@ -220,6 +221,9 @@ protected void setup(Binder binder) // column properties binder.bind(ColumnPropertyManager.class).in(Scopes.SINGLETON); + // analyze properties + binder.bind(AnalyzePropertyManager.class).in(Scopes.SINGLETON); + // node manager discoveryBinder(binder).bindSelector("presto"); binder.bind(DiscoveryNodeManager.class).in(Scopes.SINGLETON); diff --git a/presto-main/src/main/java/io/prestosql/sql/analyzer/Analysis.java b/presto-main/src/main/java/io/prestosql/sql/analyzer/Analysis.java index f1c36de99c6f..aaaf1c9232b6 100644 --- a/presto-main/src/main/java/io/prestosql/sql/analyzer/Analysis.java +++ b/presto-main/src/main/java/io/prestosql/sql/analyzer/Analysis.java @@ -130,6 +130,7 @@ public class Analysis private Optional createTableComment = Optional.empty(); private Optional insert = Optional.empty(); + private Optional analyzeTarget = Optional.empty(); // for describe input and describe output private final boolean isDescribe; @@ -522,6 +523,16 @@ public Optional getCreateTableDestination() return createTableDestination; } + public Optional getAnalyzeTarget() + { + return analyzeTarget; + } + + public void setAnalyzeTarget(TableHandle analyzeTarget) + { + this.analyzeTarget = Optional.of(analyzeTarget); + } + public void setCreateTableProperties(Map createTableProperties) { this.createTableProperties = ImmutableMap.copyOf(createTableProperties); diff --git a/presto-main/src/main/java/io/prestosql/sql/analyzer/StatementAnalyzer.java b/presto-main/src/main/java/io/prestosql/sql/analyzer/StatementAnalyzer.java index c94d8f08efbf..615cc26d5f66 100644 --- a/presto-main/src/main/java/io/prestosql/sql/analyzer/StatementAnalyzer.java +++ b/presto-main/src/main/java/io/prestosql/sql/analyzer/StatementAnalyzer.java @@ -23,6 +23,7 @@ import com.google.common.collect.Multimap; import io.prestosql.Session; import io.prestosql.SystemSessionProperties; +import io.prestosql.connector.ConnectorId; import io.prestosql.execution.warnings.WarningCollector; import io.prestosql.metadata.FunctionKind; import io.prestosql.metadata.Metadata; @@ -39,6 +40,7 @@ import io.prestosql.spi.connector.ColumnHandle; import io.prestosql.spi.connector.ColumnMetadata; import io.prestosql.spi.function.OperatorType; +import io.prestosql.spi.security.AccessDeniedException; import io.prestosql.spi.security.Identity; import io.prestosql.spi.type.ArrayType; import io.prestosql.spi.type.MapType; @@ -53,6 +55,7 @@ import io.prestosql.sql.tree.AddColumn; import io.prestosql.sql.tree.AliasedRelation; import io.prestosql.sql.tree.AllColumns; +import io.prestosql.sql.tree.Analyze; import io.prestosql.sql.tree.Call; import io.prestosql.sql.tree.Commit; import io.prestosql.sql.tree.CreateSchema; @@ -153,6 +156,7 @@ import static io.prestosql.metadata.FunctionKind.WINDOW; import static io.prestosql.metadata.MetadataUtil.createQualifiedObjectName; import static io.prestosql.spi.StandardErrorCode.INVALID_FUNCTION_ARGUMENT; +import static io.prestosql.spi.StandardErrorCode.NOT_FOUND; import static io.prestosql.spi.type.BigintType.BIGINT; import static io.prestosql.spi.type.BooleanType.BOOLEAN; import static io.prestosql.spi.type.VarcharType.VARCHAR; @@ -209,6 +213,7 @@ import static io.prestosql.sql.tree.WindowFrame.Type.RANGE; import static io.prestosql.type.UnknownType.UNKNOWN; import static java.lang.Math.toIntExact; +import static java.lang.String.format; import static java.util.Collections.emptyList; import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; @@ -402,6 +407,49 @@ protected Scope visitDelete(Delete node, Optional scope) return createAndAssignScope(node, scope, Field.newUnqualified("rows", BIGINT)); } + @Override + protected Scope visitAnalyze(Analyze node, Optional scope) + { + analysis.setUpdateType("ANALYZE"); + QualifiedObjectName tableName = createQualifiedObjectName(session, node, node.getTableName()); + + // verify the target table exists and it's not a view + if (metadata.getView(session, tableName).isPresent()) { + throw new SemanticException(NOT_SUPPORTED, node, "Analyzing views is not supported"); + } + + validateProperties(node.getProperties(), scope); + ConnectorId connectorId = metadata.getCatalogHandle(session, tableName.getCatalogName()) + .orElseThrow(() -> new PrestoException(NOT_FOUND, "Catalog not found: " + tableName.getCatalogName())); + + Map analyzeProperties = metadata.getAnalyzePropertyManager().getProperties( + connectorId, + connectorId.getCatalogName(), + mapFromProperties(node.getProperties()), + session, + metadata, + analysis.getParameters()); + TableHandle tableHandle = metadata.getTableHandleForStatisticsCollection(session, tableName, analyzeProperties) + .orElseThrow(() -> (new SemanticException(MISSING_TABLE, node, "Table '%s' does not exist", tableName))); + + // user must have read and insert permission in order to analyze stats of a table + analysis.addTableColumnReferences( + accessControl, + session.getIdentity(), + ImmutableMultimap.builder() + .putAll(tableName, metadata.getColumnHandles(session, tableHandle).keySet()) + .build()); + try { + accessControl.checkCanInsertIntoTable(session.getRequiredTransactionId(), session.getIdentity(), tableName); + } + catch (AccessDeniedException exception) { + throw new AccessDeniedException(format("Cannot ANALYZE (missing insert privilege) table %s", tableName)); + } + + analysis.setAnalyzeTarget(tableHandle); + return createAndAssignScope(node, scope, Field.newUnqualified("rows", BIGINT)); + } + @Override protected Scope visitCreateTableAsSelect(CreateTableAsSelect node, Optional scope) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java index 92322714998f..eef8f8e602c5 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/DistributedExecutionPlanner.java @@ -46,6 +46,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -336,6 +337,12 @@ public Map visitTableFinish(TableFinishNode node, Void return node.getSource().accept(this, context); } + @Override + public Map visitStatisticsWriterNode(StatisticsWriterNode node, Void context) + { + return node.getSource().accept(this, context); + } + @Override public Map visitDelete(DeleteNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java index 0bed69e9e372..56523947c13d 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LocalExecutionPlanner.java @@ -84,6 +84,7 @@ import io.prestosql.operator.SpatialIndexBuilderOperator.SpatialPredicate; import io.prestosql.operator.SpatialJoinOperator.SpatialJoinOperatorFactory; import io.prestosql.operator.StageExecutionStrategy; +import io.prestosql.operator.StatisticsWriterOperator.StatisticsWriterOperatorFactory; import io.prestosql.operator.StreamingAggregationOperator.StreamingAggregationOperatorFactory; import io.prestosql.operator.TableScanOperator.TableScanOperatorFactory; import io.prestosql.operator.TaskContext; @@ -167,6 +168,7 @@ import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; import io.prestosql.sql.planner.plan.StatisticAggregationsDescriptor; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -2244,6 +2246,22 @@ public PhysicalOperation visitTableWriter(TableWriterNode node, LocalExecutionPl return new PhysicalOperation(operatorFactory, outputMapping.build(), context, source); } + @Override + public PhysicalOperation visitStatisticsWriterNode(StatisticsWriterNode node, LocalExecutionPlanContext context) + { + PhysicalOperation source = node.getSource().accept(this, context); + + StatisticAggregationsDescriptor descriptor = node.getDescriptor().map(symbol -> source.getLayout().get(symbol)); + + OperatorFactory operatorFactory = new StatisticsWriterOperatorFactory( + context.getNextOperatorId(), + node.getId(), + computedStatistics -> metadata.finishStatisticsCollection(session, ((StatisticsWriterNode.WriteStatisticsHandle) node.getTarget()).getHandle(), computedStatistics), + node.isRowCountEnabled(), + descriptor); + return new PhysicalOperation(operatorFactory, makeLayout(node), context, source); + } + @Override public PhysicalOperation visitTableFinish(TableFinishNode node, LocalExecutionPlanContext context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/LogicalPlanner.java b/presto-main/src/main/java/io/prestosql/sql/planner/LogicalPlanner.java index fb4f96290c98..557685f6d1e8 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/LogicalPlanner.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/LogicalPlanner.java @@ -14,6 +14,7 @@ package io.prestosql.sql.planner; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import io.prestosql.Session; import io.prestosql.connector.ConnectorId; @@ -28,6 +29,7 @@ import io.prestosql.metadata.Metadata; import io.prestosql.metadata.NewTableLayout; import io.prestosql.metadata.QualifiedObjectName; +import io.prestosql.metadata.TableHandle; import io.prestosql.metadata.TableMetadata; import io.prestosql.spi.PrestoException; import io.prestosql.spi.connector.ColumnHandle; @@ -43,6 +45,7 @@ import io.prestosql.sql.parser.SqlParser; import io.prestosql.sql.planner.StatisticsAggregationPlanner.TableStatisticAggregation; import io.prestosql.sql.planner.optimizations.PlanOptimizer; +import io.prestosql.sql.planner.plan.AggregationNode; import io.prestosql.sql.planner.plan.Assignments; import io.prestosql.sql.planner.plan.DeleteNode; import io.prestosql.sql.planner.plan.ExplainAnalyzeNode; @@ -51,10 +54,13 @@ import io.prestosql.sql.planner.plan.PlanNode; import io.prestosql.sql.planner.plan.ProjectNode; import io.prestosql.sql.planner.plan.StatisticAggregations; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; +import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; import io.prestosql.sql.planner.plan.ValuesNode; import io.prestosql.sql.planner.sanity.PlanSanityChecker; +import io.prestosql.sql.tree.Analyze; import io.prestosql.sql.tree.Cast; import io.prestosql.sql.tree.CreateTableAsSelect; import io.prestosql.sql.tree.Delete; @@ -84,8 +90,10 @@ import static com.google.common.collect.Streams.zip; import static io.prestosql.spi.StandardErrorCode.NOT_FOUND; import static io.prestosql.spi.StandardErrorCode.NOT_SUPPORTED; +import static io.prestosql.spi.statistics.TableStatisticType.ROW_COUNT; import static io.prestosql.spi.type.BigintType.BIGINT; import static io.prestosql.spi.type.VarbinaryType.VARBINARY; +import static io.prestosql.sql.planner.plan.AggregationNode.singleGroupingSet; import static io.prestosql.sql.planner.plan.TableWriterNode.CreateName; import static io.prestosql.sql.planner.plan.TableWriterNode.InsertReference; import static io.prestosql.sql.planner.plan.TableWriterNode.WriterTarget; @@ -195,6 +203,9 @@ private RelationPlan planStatementWithoutOutput(Analysis analysis, Statement sta } return createTableCreationPlan(analysis, ((CreateTableAsSelect) statement).getQuery()); } + else if (statement instanceof Analyze) { + return createAnalyzePlan(analysis, (Analyze) statement); + } else if (statement instanceof Insert) { checkState(analysis.getInsert().isPresent(), "Insert handle is missing"); return createInsertPlan(analysis, (Insert) statement); @@ -223,6 +234,50 @@ private RelationPlan createExplainAnalyzePlan(Analysis analysis, Explain stateme return new RelationPlan(root, scope, ImmutableList.of(outputSymbol)); } + private RelationPlan createAnalyzePlan(Analysis analysis, Analyze analyzeStatement) + { + TableHandle targetTable = analysis.getAnalyzeTarget().get(); + + // Plan table scan + Map columnHandles = metadata.getColumnHandles(session, targetTable); + ImmutableList.Builder tableScanOutputs = ImmutableList.builder(); + ImmutableMap.Builder symbolToColumnHandle = ImmutableMap.builder(); + ImmutableMap.Builder columnNameToSymbol = ImmutableMap.builder(); + TableMetadata tableMetadata = metadata.getTableMetadata(session, targetTable); + for (ColumnMetadata column : tableMetadata.getColumns()) { + Symbol symbol = symbolAllocator.newSymbol(column.getName(), column.getType()); + tableScanOutputs.add(symbol); + symbolToColumnHandle.put(symbol, columnHandles.get(column.getName())); + columnNameToSymbol.put(column.getName(), symbol); + } + + TableStatisticsMetadata tableStatisticsMetadata = metadata.getStatisticsCollectionMetadata( + session, + targetTable.getConnectorId().getCatalogName(), + tableMetadata.getMetadata()); + + TableStatisticAggregation tableStatisticAggregation = statisticsAggregationPlanner.createStatisticsAggregation(tableStatisticsMetadata, columnNameToSymbol.build()); + StatisticAggregations statisticAggregations = tableStatisticAggregation.getAggregations(); + List groupingSymbols = statisticAggregations.getGroupingSymbols(); + + PlanNode planNode = new StatisticsWriterNode( + idAllocator.getNextId(), + new AggregationNode( + idAllocator.getNextId(), + new TableScanNode(idAllocator.getNextId(), targetTable, tableScanOutputs.build(), symbolToColumnHandle.build()), + statisticAggregations.getAggregations(), + singleGroupingSet(groupingSymbols), + ImmutableList.of(), + AggregationNode.Step.SINGLE, + Optional.empty(), + Optional.empty()), + new StatisticsWriterNode.WriteStatisticsReference(targetTable), + symbolAllocator.newSymbol("rows", BIGINT), + tableStatisticsMetadata.getTableStatistics().contains(ROW_COUNT), + tableStatisticAggregation.getDescriptor()); + return new RelationPlan(planNode, analysis.getScope(analyzeStatement), planNode.getOutputSymbols()); + } + private RelationPlan createTableCreationPlan(Analysis analysis, Query query) { QualifiedObjectName destination = analysis.getCreateTableDestination().get(); @@ -242,7 +297,7 @@ private RelationPlan createTableCreationPlan(Analysis analysis, Query query) .map(ColumnMetadata::getName) .collect(toImmutableList()); - TableStatisticsMetadata statisticsMetadata = metadata.getStatisticsCollectionMetadata(session, destination.getCatalogName(), tableMetadata); + TableStatisticsMetadata statisticsMetadata = metadata.getStatisticsCollectionMetadataForWrite(session, destination.getCatalogName(), tableMetadata); return createTableWriterPlan( analysis, @@ -305,7 +360,7 @@ private RelationPlan createInsertPlan(Analysis analysis, Insert insertStatement) Optional newTableLayout = metadata.getInsertLayout(session, insert.getTarget()); String catalogName = insert.getTarget().getConnectorId().getCatalogName(); - TableStatisticsMetadata statisticsMetadata = metadata.getStatisticsCollectionMetadata(session, catalogName, tableMetadata.getMetadata()); + TableStatisticsMetadata statisticsMetadata = metadata.getStatisticsCollectionMetadataForWrite(session, catalogName, tableMetadata.getMetadata()); return createTableWriterPlan( analysis, diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java b/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java index 49e57415a146..0461ab593461 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/PlanFragmenter.java @@ -40,6 +40,7 @@ import io.prestosql.sql.planner.plan.PlanVisitor; import io.prestosql.sql.planner.plan.RemoteSourceNode; import io.prestosql.sql.planner.plan.SimplePlanRewriter; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -243,6 +244,13 @@ public PlanNode visitExplainAnalyze(ExplainAnalyzeNode node, RewriteContext context) + { + context.get().setCoordinatorOnlyDistribution(); + return context.defaultRewrite(node, context.get()); + } + @Override public PlanNode visitTableFinish(TableFinishNode node, RewriteContext context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddExchanges.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddExchanges.java index 368d7b0b5a20..e6d698166cc6 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddExchanges.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddExchanges.java @@ -64,6 +64,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -586,6 +587,25 @@ public PlanWithProperties visitExplainAnalyze(ExplainAnalyzeNode node, Preferred return rebaseAndDeriveProperties(node, child); } + @Override + public PlanWithProperties visitStatisticsWriterNode(StatisticsWriterNode node, PreferredProperties context) + { + PlanWithProperties child = planChild(node, PreferredProperties.any()); + + // if the child is already a gathering exchange, don't add another + if ((child.getNode() instanceof ExchangeNode) && ((ExchangeNode) child.getNode()).getType().equals(GATHER)) { + return rebaseAndDeriveProperties(node, child); + } + + if (!child.getProperties().isCoordinatorOnly()) { + child = withDerivedProperties( + gatheringExchange(idAllocator.getNextId(), REMOTE, child.getNode()), + child.getProperties()); + } + + return rebaseAndDeriveProperties(node, child); + } + @Override public PlanWithProperties visitTableFinish(TableFinishNode node, PreferredProperties preferredProperties) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java index 26df1bccbef3..50ad56de998c 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/AddLocalExchanges.java @@ -49,6 +49,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableWriterNode; import io.prestosql.sql.planner.plan.TopNNode; @@ -191,6 +192,14 @@ public PlanWithProperties visitSort(SortNode node, StreamPreferredProperties par return planAndEnforceChildren(node, singleStream(), defaultParallelism(session)); } + @Override + public PlanWithProperties visitStatisticsWriterNode(StatisticsWriterNode node, StreamPreferredProperties context) + { + // analyze finish requires that all data be in one stream + // this node changes the input organization completely, so we do not pass through parent preferences + return planAndEnforceChildren(node, singleStream(), defaultParallelism(session)); + } + @Override public PlanWithProperties visitTableFinish(TableFinishNode node, StreamPreferredProperties parentPreferences) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java index 65cbb1c1220c..7057b2344e27 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/BeginTableWrite.java @@ -35,6 +35,7 @@ import io.prestosql.sql.planner.plan.ProjectNode; import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SimplePlanRewriter; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -116,6 +117,24 @@ public PlanNode visitDelete(DeleteNode node, RewriteContext context) node.getOutputSymbols()); } + @Override + public PlanNode visitStatisticsWriterNode(StatisticsWriterNode node, RewriteContext context) + { + PlanNode child = node.getSource(); + child = child.accept(this, context); + + StatisticsWriterNode.WriteStatisticsHandle analyzeHandle = + new StatisticsWriterNode.WriteStatisticsHandle(metadata.beginStatisticsCollection(session, ((StatisticsWriterNode.WriteStatisticsReference) node.getTarget()).getHandle())); + + return new StatisticsWriterNode( + node.getId(), + child, + analyzeHandle, + node.getRowCountSymbol(), + node.isRowCountEnabled(), + node.getDescriptor()); + } + @Override public PlanNode visitTableFinish(TableFinishNode node, RewriteContext context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java index 0b13a5a6d330..dd0a7e73dd30 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PropertyDerivations.java @@ -65,6 +65,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -364,6 +365,14 @@ public ActualProperties visitDistinctLimit(DistinctLimitNode node, List context) + { + return ActualProperties.builder() + .global(coordinatorSingleStreamPartition()) + .build(); + } + @Override public ActualProperties visitTableFinish(TableFinishNode node, List inputProperties) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneUnreferencedOutputs.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneUnreferencedOutputs.java index 1d4c1e41df0c..3e0b5ef191a9 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneUnreferencedOutputs.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/PruneUnreferencedOutputs.java @@ -58,6 +58,7 @@ import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; import io.prestosql.sql.planner.plan.StatisticAggregations; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -653,6 +654,19 @@ public PlanNode visitTableWriter(TableWriterNode node, RewriteContext> context) + { + PlanNode source = context.rewrite(node.getSource(), ImmutableSet.copyOf(node.getSource().getOutputSymbols())); + return new StatisticsWriterNode( + node.getId(), + source, + node.getTarget(), + node.getRowCountSymbol(), + node.isRowCountEnabled(), + node.getDescriptor()); + } + @Override public PlanNode visitTableFinish(TableFinishNode node, RewriteContext> context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java index 4bc7d6a33f7f..3ec90944eb5a 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/StreamPropertyDerivations.java @@ -52,6 +52,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -377,6 +378,14 @@ public StreamProperties visitAggregation(AggregationNode node, List node.getGroupingKeys().contains(symbol) ? Optional.of(symbol) : Optional.empty()); } + @Override + public StreamProperties visitStatisticsWriterNode(StatisticsWriterNode node, List inputProperties) + { + StreamProperties properties = Iterables.getOnlyElement(inputProperties); + // analyze finish only outputs row count + return properties.withUnspecifiedPartitioning(); + } + @Override public StreamProperties visitTableFinish(TableFinishNode node, List inputProperties) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/SymbolMapper.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/SymbolMapper.java index 55f7a331c255..63c5740abe77 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/SymbolMapper.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/SymbolMapper.java @@ -26,6 +26,7 @@ import io.prestosql.sql.planner.plan.PlanNodeId; import io.prestosql.sql.planner.plan.StatisticAggregations; import io.prestosql.sql.planner.plan.StatisticAggregationsDescriptor; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableWriterNode; import io.prestosql.sql.planner.plan.TopNNode; @@ -163,6 +164,17 @@ public TableWriterNode map(TableWriterNode node, PlanNode source, PlanNodeId new node.getStatisticsAggregationDescriptor().map(this::map)); } + public StatisticsWriterNode map(StatisticsWriterNode node, PlanNode source) + { + return new StatisticsWriterNode( + node.getId(), + source, + node.getTarget(), + node.getRowCountSymbol(), + node.isRowCountEnabled(), + node.getDescriptor().map(this::map)); + } + public TableFinishNode map(TableFinishNode node, PlanNode source) { return new TableFinishNode( diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/UnaliasSymbolReferences.java b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/UnaliasSymbolReferences.java index 7533157584f0..78fab3929edf 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/UnaliasSymbolReferences.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/optimizations/UnaliasSymbolReferences.java @@ -61,6 +61,7 @@ import io.prestosql.sql.planner.plan.SimplePlanRewriter; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -361,6 +362,14 @@ public PlanNode visitDelete(DeleteNode node, RewriteContext context) return new DeleteNode(node.getId(), context.rewrite(node.getSource()), node.getTarget(), canonicalize(node.getRowId()), node.getOutputSymbols()); } + @Override + public PlanNode visitStatisticsWriterNode(StatisticsWriterNode node, RewriteContext context) + { + PlanNode source = context.rewrite(node.getSource()); + SymbolMapper mapper = new SymbolMapper(mapping); + return mapper.map(node, source); + } + @Override public PlanNode visitTableFinish(TableFinishNode node, RewriteContext context) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanNode.java b/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanNode.java index 0b97ca58a5cf..0b630a31451a 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanNode.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanNode.java @@ -61,7 +61,9 @@ @JsonSubTypes.Type(value = ExplainAnalyzeNode.class, name = "explainAnalyze"), @JsonSubTypes.Type(value = ApplyNode.class, name = "apply"), @JsonSubTypes.Type(value = AssignUniqueId.class, name = "assignUniqueId"), - @JsonSubTypes.Type(value = LateralJoinNode.class, name = "lateralJoin")}) + @JsonSubTypes.Type(value = LateralJoinNode.class, name = "lateralJoin"), + @JsonSubTypes.Type(value = StatisticsWriterNode.class, name = "statisticsWriterNode"), +}) public abstract class PlanNode { private final PlanNodeId id; diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanVisitor.java b/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanVisitor.java index 7317d3c257ee..91c56f6af997 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanVisitor.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/plan/PlanVisitor.java @@ -134,6 +134,11 @@ public R visitTableFinish(TableFinishNode node, C context) return visitPlan(node, context); } + public R visitStatisticsWriterNode(StatisticsWriterNode node, C context) + { + return visitPlan(node, context); + } + public R visitUnion(UnionNode node, C context) { return visitPlan(node, context); diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/plan/StatisticsWriterNode.java b/presto-main/src/main/java/io/prestosql/sql/planner/plan/StatisticsWriterNode.java new file mode 100644 index 000000000000..be18624363ec --- /dev/null +++ b/presto-main/src/main/java/io/prestosql/sql/planner/plan/StatisticsWriterNode.java @@ -0,0 +1,172 @@ +/* + * 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 io.prestosql.sql.planner.plan; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import io.prestosql.metadata.AnalyzeTableHandle; +import io.prestosql.metadata.TableHandle; +import io.prestosql.sql.planner.Symbol; + +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class StatisticsWriterNode + extends PlanNode +{ + private final PlanNode source; + private final Symbol rowCountSymbol; + private final WriteStatisticsTarget target; + private final boolean rowCountEnabled; + private final StatisticAggregationsDescriptor descriptor; + + @JsonCreator + public StatisticsWriterNode( + @JsonProperty("id") PlanNodeId id, + @JsonProperty("source") PlanNode source, + @JsonProperty("target") WriteStatisticsTarget target, + @JsonProperty("rowCountSymbol") Symbol rowCountSymbol, + @JsonProperty("rowCountEnabled") boolean rowCountEnabled, + @JsonProperty("descriptor") StatisticAggregationsDescriptor descriptor) + { + super(id); + this.source = requireNonNull(source, "source is null"); + this.target = requireNonNull(target, "target is null"); + this.rowCountSymbol = requireNonNull(rowCountSymbol, "rowCountSymbol is null"); + this.rowCountEnabled = rowCountEnabled; + this.descriptor = requireNonNull(descriptor, "descriptor is null"); + } + + @JsonProperty + public PlanNode getSource() + { + return source; + } + + @JsonProperty + public WriteStatisticsTarget getTarget() + { + return target; + } + + @JsonProperty + public StatisticAggregationsDescriptor getDescriptor() + { + return descriptor; + } + + @JsonProperty + public Symbol getRowCountSymbol() + { + return rowCountSymbol; + } + + @JsonProperty + public boolean isRowCountEnabled() + { + return rowCountEnabled; + } + + @Override + public List getSources() + { + return ImmutableList.of(source); + } + + @Override + public List getOutputSymbols() + { + return ImmutableList.of(rowCountSymbol); + } + + @Override + public PlanNode replaceChildren(List newChildren) + { + return new StatisticsWriterNode( + getId(), + Iterables.getOnlyElement(newChildren), + target, + rowCountSymbol, + rowCountEnabled, + descriptor); + } + + @Override + public R accept(PlanVisitor visitor, C context) + { + return visitor.visitStatisticsWriterNode(this, context); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "@type") + @JsonSubTypes({ + @JsonSubTypes.Type(value = WriteStatisticsHandle.class, name = "WriteStatisticsHandle")}) + @SuppressWarnings({"EmptyClass", "ClassMayBeInterface"}) + public abstract static class WriteStatisticsTarget + { + @Override + public abstract String toString(); + } + + public static class WriteStatisticsHandle + extends WriteStatisticsTarget + { + private final AnalyzeTableHandle handle; + + @JsonCreator + public WriteStatisticsHandle(@JsonProperty("handle") AnalyzeTableHandle handle) + { + this.handle = requireNonNull(handle, "handle is null"); + } + + @JsonProperty + public AnalyzeTableHandle getHandle() + { + return handle; + } + + @Override + public String toString() + { + return handle.toString(); + } + } + + // only used during planning -- will not be serialized + public static class WriteStatisticsReference + extends WriteStatisticsTarget + { + private final TableHandle handle; + + public WriteStatisticsReference(TableHandle handle) + { + this.handle = requireNonNull(handle, "handle is null"); + } + + public TableHandle getHandle() + { + return handle; + } + + @Override + public String toString() + { + return handle.toString(); + } + } +} diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/planPrinter/PlanPrinter.java b/presto-main/src/main/java/io/prestosql/sql/planner/planPrinter/PlanPrinter.java index bf0330c9f6e9..b79ad607ef29 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/planPrinter/PlanPrinter.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/planPrinter/PlanPrinter.java @@ -90,6 +90,7 @@ import io.prestosql.sql.planner.plan.SpatialJoinNode; import io.prestosql.sql.planner.plan.StatisticAggregations; import io.prestosql.sql.planner.plan.StatisticAggregationsDescriptor; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -1121,6 +1122,15 @@ public Void visitTableWriter(TableWriterNode node, Integer indent) return processChildren(node, indent + 1); } + @Override + public Void visitStatisticsWriterNode(StatisticsWriterNode node, Integer indent) + { + print(indent, "- StatisticsWriterNode[%s] => [%s]", node.getTarget(), formatOutputs(node.getOutputSymbols())); + printPlanNodesStatsAndCost(indent + 2, node); + printStats(indent + 2, node.getId()); + return processChildren(node, indent + 1); + } + @Override public Void visitTableFinish(TableFinishNode node, Integer indent) { diff --git a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java index b68cf7d3feba..56a8175897b2 100644 --- a/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java +++ b/presto-main/src/main/java/io/prestosql/sql/planner/sanity/ValidateDependenciesChecker.java @@ -53,6 +53,8 @@ import io.prestosql.sql.planner.plan.SetOperationNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticAggregationsDescriptor; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -528,6 +530,22 @@ public Void visitMetadataDelete(MetadataDeleteNode node, Set boundSymbol return null; } + @Override + public Void visitStatisticsWriterNode(StatisticsWriterNode node, Set boundSymbols) + { + node.getSource().accept(this, boundSymbols); // visit child + + StatisticAggregationsDescriptor descriptor = node.getDescriptor(); + Set dependencies = ImmutableSet.builder() + .addAll(descriptor.getGrouping().values()) + .addAll(descriptor.getColumnStatistics().values()) + .addAll(descriptor.getTableStatistics().values()) + .build(); + List outputSymbols = node.getSource().getOutputSymbols(); + checkDependencies(dependencies, dependencies, "Invalid node. Dependencies (%s) not in source plan output (%s)", dependencies, outputSymbols); + return null; + } + @Override public Void visitTableFinish(TableFinishNode node, Set boundSymbols) { diff --git a/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java b/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java index 82d9a2061656..ad142d9658c3 100644 --- a/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java +++ b/presto-main/src/main/java/io/prestosql/testing/LocalQueryRunner.java @@ -71,6 +71,7 @@ import io.prestosql.execution.warnings.WarningCollector; import io.prestosql.index.IndexManager; import io.prestosql.memory.MemoryManagerConfig; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; import io.prestosql.metadata.HandleResolver; @@ -304,6 +305,7 @@ private LocalQueryRunner(Session defaultSession, FeaturesConfig featuresConfig, new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); this.planFragmenter = new PlanFragmenter(this.metadata, this.nodePartitioningManager, new QueryManagerConfig()); this.joinCompiler = new JoinCompiler(metadata, featuresConfig); diff --git a/presto-main/src/main/java/io/prestosql/testing/TestingMetadata.java b/presto-main/src/main/java/io/prestosql/testing/TestingMetadata.java index bc9fa5e860ca..5f6a8e1bf6e7 100644 --- a/presto-main/src/main/java/io/prestosql/testing/TestingMetadata.java +++ b/presto-main/src/main/java/io/prestosql/testing/TestingMetadata.java @@ -86,6 +86,12 @@ public ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTable return new TestingTableHandle(tableName); } + @Override + public ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSession session, SchemaTableName tableName, Map analyzeProperties) + { + return getTableHandle(session, tableName); + } + @Override public List getTableLayouts(ConnectorSession session, ConnectorTableHandle table, Constraint constraint, Optional> desiredColumns) { diff --git a/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java b/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java index 05d02ad5949f..ed4d152fdbf0 100644 --- a/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java +++ b/presto-main/src/main/java/io/prestosql/util/GraphvizPrinter.java @@ -48,6 +48,7 @@ import io.prestosql.sql.planner.plan.SemiJoinNode; import io.prestosql.sql.planner.plan.SortNode; import io.prestosql.sql.planner.plan.SpatialJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableFinishNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.TableWriterNode; @@ -96,7 +97,8 @@ private enum NodeType TABLE_WRITER, TABLE_FINISH, INDEX_SOURCE, - UNNEST + UNNEST, + ANALYZE_FINISH, } private static final Map NODE_COLORS = immutableEnumMap(ImmutableMap.builder() @@ -120,6 +122,7 @@ private enum NodeType .put(NodeType.INDEX_SOURCE, "dodgerblue3") .put(NodeType.UNNEST, "crimson") .put(NodeType.SAMPLE, "goldenrod4") + .put(NodeType.ANALYZE_FINISH, "plum") .build()); static { @@ -224,6 +227,13 @@ public Void visitTableWriter(TableWriterNode node, Void context) return node.getSource().accept(this, context); } + @Override + public Void visitStatisticsWriterNode(StatisticsWriterNode node, Void context) + { + printNode(node, format("StatisticsWriterNode[%s]", Joiner.on(", ").join(node.getOutputSymbols())), NODE_COLORS.get(NodeType.ANALYZE_FINISH)); + return node.getSource().accept(this, context); + } + @Override public Void visitTableFinish(TableFinishNode node, Void context) { diff --git a/presto-main/src/main/java/io/prestosql/util/StatementUtils.java b/presto-main/src/main/java/io/prestosql/util/StatementUtils.java index 97860f7542c9..bdeba52c81a8 100644 --- a/presto-main/src/main/java/io/prestosql/util/StatementUtils.java +++ b/presto-main/src/main/java/io/prestosql/util/StatementUtils.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableMap; import io.prestosql.spi.resourcegroups.QueryType; import io.prestosql.sql.tree.AddColumn; +import io.prestosql.sql.tree.Analyze; import io.prestosql.sql.tree.Call; import io.prestosql.sql.tree.Commit; import io.prestosql.sql.tree.CreateRole; @@ -77,6 +78,7 @@ private StatementUtils() {} builder.put(Query.class, QueryType.SELECT); builder.put(Explain.class, QueryType.EXPLAIN); + builder.put(Analyze.class, QueryType.ANALYZE); builder.put(CreateTableAsSelect.class, QueryType.INSERT); builder.put(Insert.class, QueryType.INSERT); diff --git a/presto-main/src/test/java/io/prestosql/execution/TestResetSessionTask.java b/presto-main/src/test/java/io/prestosql/execution/TestResetSessionTask.java index 7d27e2a2accf..f44723e8115b 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TestResetSessionTask.java +++ b/presto-main/src/test/java/io/prestosql/execution/TestResetSessionTask.java @@ -18,6 +18,7 @@ import io.prestosql.Session; import io.prestosql.block.BlockEncodingManager; import io.prestosql.execution.warnings.WarningCollector; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.Catalog; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; @@ -72,6 +73,7 @@ public TestResetSessionTask() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); metadata.getSessionPropertyManager().addSystemSessionProperty(stringProperty( diff --git a/presto-main/src/test/java/io/prestosql/execution/TestSetPathTask.java b/presto-main/src/test/java/io/prestosql/execution/TestSetPathTask.java index 90cac1f56bd2..e5e852b8655c 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TestSetPathTask.java +++ b/presto-main/src/test/java/io/prestosql/execution/TestSetPathTask.java @@ -16,6 +16,7 @@ import com.google.common.collect.ImmutableList; import io.prestosql.block.BlockEncodingManager; import io.prestosql.execution.warnings.WarningCollector; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; import io.prestosql.metadata.MetadataManager; @@ -70,6 +71,7 @@ public TestSetPathTask() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); } diff --git a/presto-main/src/test/java/io/prestosql/execution/TestSetRoleTask.java b/presto-main/src/test/java/io/prestosql/execution/TestSetRoleTask.java index 53f469d31ba5..e93beac922f7 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TestSetRoleTask.java +++ b/presto-main/src/test/java/io/prestosql/execution/TestSetRoleTask.java @@ -21,6 +21,7 @@ import com.google.common.collect.ImmutableMap; import io.prestosql.block.BlockEncodingManager; import io.prestosql.execution.warnings.WarningCollector; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; import io.prestosql.metadata.MetadataManager; @@ -78,6 +79,7 @@ public void setUp() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); catalogManager.registerCatalog(createBogusTestingCatalog(CATALOG_NAME)); diff --git a/presto-main/src/test/java/io/prestosql/execution/TestSetSessionTask.java b/presto-main/src/test/java/io/prestosql/execution/TestSetSessionTask.java index 5aeba6197982..824267881fc5 100644 --- a/presto-main/src/test/java/io/prestosql/execution/TestSetSessionTask.java +++ b/presto-main/src/test/java/io/prestosql/execution/TestSetSessionTask.java @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableMap; import io.prestosql.block.BlockEncodingManager; import io.prestosql.execution.warnings.WarningCollector; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.Catalog; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; @@ -84,6 +85,7 @@ public TestSetSessionTask() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); metadata.getSessionPropertyManager().addSystemSessionProperty(stringProperty( diff --git a/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java b/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java index ec6eecd15453..5ce0beed4a7f 100644 --- a/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java +++ b/presto-main/src/test/java/io/prestosql/metadata/AbstractMockMetadata.java @@ -101,6 +101,12 @@ public Optional getTableHandle(Session session, QualifiedObjectName throw new UnsupportedOperationException(); } + @Override + public Optional getTableHandleForStatisticsCollection(Session session, QualifiedObjectName tableName, Map analyzeProperties) + { + throw new UnsupportedOperationException(); + } + @Override public Optional getSystemTable(Session session, QualifiedObjectName tableName) { @@ -245,12 +251,30 @@ public Optional getInsertLayout(Session session, TableHandle tar throw new UnsupportedOperationException(); } + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(Session session, String catalogName, ConnectorTableMetadata tableMetadata) + { + throw new UnsupportedOperationException(); + } + @Override public TableStatisticsMetadata getStatisticsCollectionMetadata(Session session, String catalogName, ConnectorTableMetadata tableMetadata) { throw new UnsupportedOperationException(); } + @Override + public AnalyzeTableHandle beginStatisticsCollection(Session session, TableHandle tableHandle) + { + throw new UnsupportedOperationException(); + } + + @Override + public void finishStatisticsCollection(Session session, AnalyzeTableHandle tableHandle, Collection computedStatistics) + { + throw new UnsupportedOperationException(); + } + @Override public void beginQuery(Session session, Set connectors) { @@ -467,6 +491,12 @@ public ColumnPropertyManager getColumnPropertyManager() throw new UnsupportedOperationException(); } + @Override + public AnalyzePropertyManager getAnalyzePropertyManager() + { + throw new UnsupportedOperationException(); + } + @Override public void dropColumn(Session session, TableHandle tableHandle, ColumnHandle column) { diff --git a/presto-main/src/test/java/io/prestosql/metadata/TestInformationSchemaMetadata.java b/presto-main/src/test/java/io/prestosql/metadata/TestInformationSchemaMetadata.java index 2e9a2b5cb58b..47b1bdf8237c 100644 --- a/presto-main/src/test/java/io/prestosql/metadata/TestInformationSchemaMetadata.java +++ b/presto-main/src/test/java/io/prestosql/metadata/TestInformationSchemaMetadata.java @@ -99,6 +99,7 @@ public TestInformationSchemaMetadata() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); } diff --git a/presto-main/src/test/java/io/prestosql/sql/analyzer/TestAnalyzer.java b/presto-main/src/test/java/io/prestosql/sql/analyzer/TestAnalyzer.java index 0c8f4734ce4e..eb192b5301b5 100644 --- a/presto-main/src/test/java/io/prestosql/sql/analyzer/TestAnalyzer.java +++ b/presto-main/src/test/java/io/prestosql/sql/analyzer/TestAnalyzer.java @@ -25,6 +25,7 @@ import io.prestosql.execution.TaskManagerConfig; import io.prestosql.execution.warnings.WarningCollector; import io.prestosql.memory.MemoryManagerConfig; +import io.prestosql.metadata.AnalyzePropertyManager; import io.prestosql.metadata.Catalog; import io.prestosql.metadata.CatalogManager; import io.prestosql.metadata.ColumnPropertyManager; @@ -47,6 +48,7 @@ import io.prestosql.spi.connector.ConnectorTableMetadata; import io.prestosql.spi.connector.ConnectorTransactionHandle; import io.prestosql.spi.connector.SchemaTableName; +import io.prestosql.spi.session.PropertyMetadata; import io.prestosql.spi.transaction.IsolationLevel; import io.prestosql.spi.type.ArrayType; import io.prestosql.spi.type.TypeManager; @@ -60,6 +62,7 @@ import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; @@ -67,6 +70,8 @@ import static io.prestosql.connector.ConnectorId.createSystemTablesConnectorId; import static io.prestosql.metadata.ViewDefinition.ViewColumn; import static io.prestosql.operator.scalar.ApplyFunction.APPLY_FUNCTION; +import static io.prestosql.spi.session.PropertyMetadata.integerProperty; +import static io.prestosql.spi.session.PropertyMetadata.stringProperty; import static io.prestosql.spi.type.BigintType.BIGINT; import static io.prestosql.spi.type.DoubleType.DOUBLE; import static io.prestosql.spi.type.VarcharType.VARCHAR; @@ -1096,6 +1101,16 @@ public void testCreateTable() assertFails(DUPLICATE_PROPERTY, ".* Duplicate property: p1", "CREATE TABLE test (id bigint) WITH (p1 = 'p1', \"p1\" = 'p2')"); } + @Test + public void testAnalyze() + { + analyze("ANALYZE t1"); + analyze("ANALYZE t1 WITH (p1 = 'p1')"); + + assertFails(DUPLICATE_PROPERTY, ".* Duplicate property: p1", "ANALYZE t1 WITH (p1 = 'p1', p2 = 2, p1 = 'p3')"); + assertFails(DUPLICATE_PROPERTY, ".* Duplicate property: p1", "ANALYZE t1 WITH (p1 = 'p1', \"p1\" = 'p2')"); + } + @Test public void testCreateSchema() { @@ -1500,11 +1515,15 @@ public void setup() new SchemaPropertyManager(), new TablePropertyManager(), new ColumnPropertyManager(), + new AnalyzePropertyManager(), transactionManager); metadata.getFunctionRegistry().addFunctions(ImmutableList.of(APPLY_FUNCTION)); - catalogManager.registerCatalog(createTestingCatalog(TPCH_CATALOG, TPCH_CONNECTOR_ID)); + Catalog tpchTestCatalog = createTestingCatalog(TPCH_CATALOG, TPCH_CONNECTOR_ID); + catalogManager.registerCatalog(tpchTestCatalog); + metadata.getAnalyzePropertyManager().addProperties(TPCH_CONNECTOR_ID, tpchTestCatalog.getConnector(TPCH_CONNECTOR_ID).getAnalyzeProperties()); + catalogManager.registerCatalog(createTestingCatalog(SECOND_CATALOG, SECOND_CONNECTOR_ID)); catalogManager.registerCatalog(createTestingCatalog(THIRD_CATALOG, THIRD_CONNECTOR_ID)); @@ -1768,6 +1787,14 @@ public ConnectorSplitManager getSplitManager() { throw new UnsupportedOperationException(); } + + @Override + public List> getAnalyzeProperties() + { + return ImmutableList.of( + stringProperty("p1", "test string property", "", false), + integerProperty("p2", "test integer property", 0, false)); + } }; } } diff --git a/presto-main/src/test/java/io/prestosql/sql/planner/TestLogicalPlanner.java b/presto-main/src/test/java/io/prestosql/sql/planner/TestLogicalPlanner.java index 34e63e56ce7a..01941181acdd 100644 --- a/presto-main/src/test/java/io/prestosql/sql/planner/TestLogicalPlanner.java +++ b/presto-main/src/test/java/io/prestosql/sql/planner/TestLogicalPlanner.java @@ -33,6 +33,7 @@ import io.prestosql.sql.planner.plan.LateralJoinNode; import io.prestosql.sql.planner.plan.PlanNode; import io.prestosql.sql.planner.plan.SemiJoinNode; +import io.prestosql.sql.planner.plan.StatisticsWriterNode; import io.prestosql.sql.planner.plan.TableScanNode; import io.prestosql.sql.planner.plan.ValuesNode; import io.prestosql.sql.tree.LongLiteral; @@ -101,6 +102,21 @@ public class TestLogicalPlanner extends BasePlanTest { + @Test + public void testAnalyze() + { + assertDistributedPlan("ANALYZE orders", + anyTree( + node(StatisticsWriterNode.class, + anyTree( + exchange(REMOTE, GATHER, + node(AggregationNode.class, + anyTree( + exchange(REMOTE, GATHER, + node(AggregationNode.class, + tableScan("orders", ImmutableMap.of())))))))))); + } + @Test public void testAggregation() { diff --git a/presto-spi/src/main/java/io/prestosql/spi/StandardErrorCode.java b/presto-spi/src/main/java/io/prestosql/spi/StandardErrorCode.java index 5332cd6bbc2b..3b733b6bf892 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/StandardErrorCode.java +++ b/presto-spi/src/main/java/io/prestosql/spi/StandardErrorCode.java @@ -62,6 +62,7 @@ public enum StandardErrorCode INVALID_COLUMN_PROPERTY(0x0000_0027, USER_ERROR), QUERY_HAS_TOO_MANY_STAGES(0x0000_0028, USER_ERROR), INVALID_SPATIAL_PARTITIONING(0x0000_0029, USER_ERROR), + INVALID_ANALYZE_PROPERTY(0x0000_002A, USER_ERROR), GENERIC_INTERNAL_ERROR(0x0001_0000, INTERNAL_ERROR), TOO_MANY_REQUESTS_FAILED(0x0001_0001, INTERNAL_ERROR), diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java b/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java index 5ff343180ac6..b34680521894 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/Connector.java @@ -107,6 +107,14 @@ default List> getSchemaProperties() return emptyList(); } + /** + * @return the analyze properties for this connector + */ + default List> getAnalyzeProperties() + { + return emptyList(); + } + /** * @return the table properties for this connector */ diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorAnalyzeMetadata.java b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorAnalyzeMetadata.java new file mode 100644 index 000000000000..92c63ec2cf32 --- /dev/null +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorAnalyzeMetadata.java @@ -0,0 +1,40 @@ +/* + * 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 io.prestosql.spi.connector; + +import io.prestosql.spi.statistics.TableStatisticsMetadata; + +import static java.util.Objects.requireNonNull; + +public class ConnectorAnalyzeMetadata +{ + private final TableStatisticsMetadata statisticsMetadata; + private final ConnectorTableHandle tableHandle; + + public ConnectorAnalyzeMetadata(TableStatisticsMetadata statisticsMetadata, ConnectorTableHandle tableHandle) + { + this.statisticsMetadata = requireNonNull(statisticsMetadata, "statisticsMetadata is null"); + this.tableHandle = requireNonNull(tableHandle, "tableHandle is null"); + } + + public TableStatisticsMetadata getStatisticsMetadata() + { + return statisticsMetadata; + } + + public ConnectorTableHandle getTableHandle() + { + return tableHandle; + } +} diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorMetadata.java b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorMetadata.java index d196a5b514a3..49847968b98e 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorMetadata.java +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/ConnectorMetadata.java @@ -59,6 +59,15 @@ default boolean schemaExists(ConnectorSession session, String schemaName) */ ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTableName tableName); + /** + * Returns a table handle for the specified table name, or null if the connector does not contain the table. + * The returned table handle can contain information in analyzeProperties. + */ + default ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSession session, SchemaTableName tableName, Map analyzeProperties) + { + throw new PrestoException(NOT_SUPPORTED, "This connector does not support analyze"); + } + /** * Returns the system table for the specified table name, if one exists. * The system tables handled via {@link #getSystemTable} differ form those returned by {@link Connector#getSystemTables()}. @@ -288,11 +297,35 @@ default Optional getInsertLayout(ConnectorSession sessi /** * Describes statistics that must be collected during a write. */ - default TableStatisticsMetadata getStatisticsCollectionMetadata(ConnectorSession session, ConnectorTableMetadata tableMetadata) + default TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(ConnectorSession session, ConnectorTableMetadata tableMetadata) { return TableStatisticsMetadata.empty(); } + /** + * Describe statistics that must be collected during a statistics collection + */ + default TableStatisticsMetadata getStatisticsCollectionMetadata(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "ConnectorMetadata getTableHandleForStatisticsCollection() is implemented without getStatisticsCollectionMetadata()"); + } + + /** + * Begin statistics collection + */ + default ConnectorTableHandle beginStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "ConnectorMetadata getStatisticsCollectionMetadata() is implemented without beginStatisticsCollection()"); + } + + /** + * Finish statistics collection + */ + default void finishStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle, Collection computedStatistics) + { + throw new PrestoException(GENERIC_INTERNAL_ERROR, "ConnectorMetadata beginStatisticsCollection() is implemented without finishStatisticsCollection()"); + } + /** * Begin the atomic creation of a table with data. */ diff --git a/presto-spi/src/main/java/io/prestosql/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java b/presto-spi/src/main/java/io/prestosql/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java index 878246dccc69..4b7ee5bb2bc6 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java +++ b/presto-spi/src/main/java/io/prestosql/spi/connector/classloader/ClassLoaderSafeConnectorMetadata.java @@ -117,6 +117,14 @@ public Optional getInsertLayout(ConnectorSession sessio } } + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadataForWrite(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getStatisticsCollectionMetadataForWrite(session, tableMetadata); + } + } + @Override public TableStatisticsMetadata getStatisticsCollectionMetadata(ConnectorSession session, ConnectorTableMetadata tableMetadata) { @@ -125,6 +133,22 @@ public TableStatisticsMetadata getStatisticsCollectionMetadata(ConnectorSession } } + @Override + public ConnectorTableHandle beginStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.beginStatisticsCollection(session, tableHandle); + } + } + + @Override + public void finishStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle, Collection computedStatistics) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + delegate.finishStatisticsCollection(session, tableHandle, computedStatistics); + } + } + @Override public boolean schemaExists(ConnectorSession session, String schemaName) { @@ -149,6 +173,14 @@ public ConnectorTableHandle getTableHandle(ConnectorSession session, SchemaTable } } + @Override + public ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSession session, SchemaTableName tableName, Map analyzeProperties) + { + try (ThreadContextClassLoader ignored = new ThreadContextClassLoader(classLoader)) { + return delegate.getTableHandleForStatisticsCollection(session, tableName, analyzeProperties); + } + } + @Override public Optional getSystemTable(ConnectorSession session, SchemaTableName tableName) { diff --git a/presto-spi/src/main/java/io/prestosql/spi/resourcegroups/QueryType.java b/presto-spi/src/main/java/io/prestosql/spi/resourcegroups/QueryType.java index 7824b8e27744..1aac0ff59410 100644 --- a/presto-spi/src/main/java/io/prestosql/spi/resourcegroups/QueryType.java +++ b/presto-spi/src/main/java/io/prestosql/spi/resourcegroups/QueryType.java @@ -19,6 +19,7 @@ public enum QueryType DELETE, DESCRIBE, EXPLAIN, + ANALYZE, INSERT, SELECT } diff --git a/presto-tests/src/test/java/io/prestosql/tests/TestLocalQueryRunner.java b/presto-tests/src/test/java/io/prestosql/tests/TestLocalQueryRunner.java index f52b1802027a..a970447472ae 100644 --- a/presto-tests/src/test/java/io/prestosql/tests/TestLocalQueryRunner.java +++ b/presto-tests/src/test/java/io/prestosql/tests/TestLocalQueryRunner.java @@ -15,6 +15,10 @@ import org.testng.annotations.Test; +import static io.prestosql.testing.TestingAccessControlManager.TestingPrivilegeType.INSERT_TABLE; +import static io.prestosql.testing.TestingAccessControlManager.TestingPrivilegeType.SELECT_COLUMN; +import static io.prestosql.testing.TestingAccessControlManager.privilege; + public class TestLocalQueryRunner extends AbstractTestQueryFramework { @@ -28,4 +32,13 @@ public void testSimpleQuery() { assertQuery("SELECT * FROM nation"); } + + @Test + public void testAnalyzeAccessControl() + { + assertAccessAllowed("ANALYZE nation"); + assertAccessDenied("ANALYZE nation", "Cannot ANALYZE \\(missing insert privilege\\) table .*.nation.*", privilege("nation", INSERT_TABLE)); + assertAccessDenied("ANALYZE nation", "Cannot select from columns \\[.*] in table or view .*.nation", privilege("nation", SELECT_COLUMN)); + assertAccessDenied("ANALYZE nation", "Cannot select from columns \\[.*nationkey.*] in table or view .*.nation", privilege("nationkey", SELECT_COLUMN)); + } } diff --git a/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java b/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java index aee32d9dfcd7..bd9285742aea 100644 --- a/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java +++ b/presto-tpch/src/main/java/io/prestosql/plugin/tpch/TpchMetadata.java @@ -54,13 +54,16 @@ import io.prestosql.spi.predicate.NullableValue; import io.prestosql.spi.predicate.TupleDomain; import io.prestosql.spi.statistics.ColumnStatistics; +import io.prestosql.spi.statistics.ComputedStatistics; import io.prestosql.spi.statistics.DoubleRange; import io.prestosql.spi.statistics.Estimate; import io.prestosql.spi.statistics.TableStatistics; +import io.prestosql.spi.statistics.TableStatisticsMetadata; import io.prestosql.spi.type.Type; import io.prestosql.spi.type.VarcharType; import java.time.LocalDate; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Optional; @@ -72,6 +75,7 @@ import static io.airlift.tpch.OrderColumn.ORDER_STATUS; import static io.prestosql.plugin.tpch.util.PredicateUtils.convertToPredicate; import static io.prestosql.plugin.tpch.util.PredicateUtils.filterOutColumnFromPredicate; +import static io.prestosql.spi.statistics.TableStatisticType.ROW_COUNT; import static io.prestosql.spi.type.BigintType.BIGINT; import static io.prestosql.spi.type.DateType.DATE; import static io.prestosql.spi.type.DoubleType.DOUBLE; @@ -172,6 +176,12 @@ public TpchTableHandle getTableHandle(ConnectorSession session, SchemaTableName return new TpchTableHandle(tableName.getTableName(), scaleFactor); } + @Override + public ConnectorTableHandle getTableHandleForStatisticsCollection(ConnectorSession session, SchemaTableName tableName, Map analyzeProperties) + { + return getTableHandle(session, tableName); + } + @Override public List getTableLayouts( ConnectorSession session, @@ -412,6 +422,24 @@ private static double toDouble(Object value, Type columnType) throw new IllegalArgumentException("unsupported column type " + columnType); } + @Override + public TableStatisticsMetadata getStatisticsCollectionMetadata(ConnectorSession session, ConnectorTableMetadata tableMetadata) + { + return new TableStatisticsMetadata(ImmutableSet.of(), ImmutableSet.of(ROW_COUNT), ImmutableList.of()); + } + + @Override + public ConnectorTableHandle beginStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle) + { + return (TpchTableHandle) tableHandle; + } + + @Override + public void finishStatisticsCollection(ConnectorSession session, ConnectorTableHandle tableHandle, Collection computedStatistics) + { + // do nothing + } + @VisibleForTesting TpchColumnHandle toColumnHandle(TpchColumn column) {