diff --git a/core/trino-main/src/main/java/io/trino/sql/planner/sanity/TableExecuteStructureValidator.java b/core/trino-main/src/main/java/io/trino/sql/planner/sanity/TableExecuteStructureValidator.java index 3b855cc263f6..389c1adfab13 100644 --- a/core/trino-main/src/main/java/io/trino/sql/planner/sanity/TableExecuteStructureValidator.java +++ b/core/trino-main/src/main/java/io/trino/sql/planner/sanity/TableExecuteStructureValidator.java @@ -18,6 +18,7 @@ import io.trino.sql.PlannerContext; import io.trino.sql.planner.AdaptivePlanner; import io.trino.sql.planner.plan.ExchangeNode; +import io.trino.sql.planner.plan.ExplainAnalyzeNode; import io.trino.sql.planner.plan.OutputNode; import io.trino.sql.planner.plan.PlanNode; import io.trino.sql.planner.plan.ProjectNode; @@ -65,6 +66,7 @@ private boolean isAllowedNode(PlanNode node) || node instanceof TableExecuteNode || node instanceof OutputNode || node instanceof ExchangeNode - || node instanceof TableFinishNode; + || node instanceof TableFinishNode + || node instanceof ExplainAnalyzeNode; } } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java index dbb7f20f19a2..5392dadaeb72 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/IcebergMetadata.java @@ -3576,9 +3576,12 @@ public TableStatistics getTableStatistics(ConnectorSession session, ConnectorTab } IcebergTableHandle originalHandle = (IcebergTableHandle) tableHandle; + + if (originalHandle.isRecordScannedFiles()) { + return TableStatistics.empty(); + } // Certain table handle attributes are not applicable to select queries (which need stats). // If this changes, the caching logic may here may need to be revised. - checkArgument(!originalHandle.isRecordScannedFiles(), "Unexpected scanned files recording set"); checkArgument(originalHandle.getMaxScannedFileSize().isEmpty(), "Unexpected max scanned file size set"); IcebergTableHandle cacheKey = new IcebergTableHandle( diff --git a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java index 4c818a7177f1..fe7d246e858a 100644 --- a/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java +++ b/plugin/trino-iceberg/src/test/java/io/trino/plugin/iceberg/BaseIcebergConnectorTest.java @@ -113,6 +113,7 @@ import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly; import static io.trino.SystemSessionProperties.DETERMINE_PARTITION_COUNT_FOR_WRITE_ENABLED; import static io.trino.SystemSessionProperties.ENABLE_DYNAMIC_FILTERING; +import static io.trino.SystemSessionProperties.IGNORE_STATS_CALCULATOR_FAILURES; import static io.trino.SystemSessionProperties.MAX_HASH_PARTITION_COUNT; import static io.trino.SystemSessionProperties.MAX_WRITER_TASK_COUNT; import static io.trino.SystemSessionProperties.SCALE_WRITERS; @@ -6613,6 +6614,34 @@ public void testExpireSnapshotsParameterValidation() "\\QRetention specified (33.00s) is shorter than the minimum retention configured in the system (7.00d). Minimum retention can be changed with iceberg.expire-snapshots.min-retention configuration property or iceberg.expire_snapshots_min_retention session property"); } + @Test + public void testExplainOptimize() + { + Session sessionWithIgnoreStatsCalculatorFailuresFalse = withoutIgnoreStatsCalculatorFailures(getSession()); + + String tableName = "test_explain_optimize" + randomNameSuffix(); + assertUpdate("CREATE TABLE " + tableName + " (key varchar, value integer) WITH (partitioning = ARRAY['key'])"); + assertUpdate("INSERT INTO " + tableName + " VALUES ('one', 1)", 1); + assertUpdate("INSERT INTO " + tableName + " VALUES ('two', 2)", 1); + + assertExplain(sessionWithIgnoreStatsCalculatorFailuresFalse, "EXPLAIN ALTER TABLE " + tableName + " EXECUTE OPTIMIZE", + ".*Output layout:.*"); + } + + @Test + public void testExplainAnalyzeOptimize() + { + Session sessionWithIgnoreStatsCalculatorFailuresFalse = withoutIgnoreStatsCalculatorFailures(getSession()); + + String tableName = "test_explain_analyze_optimize" + randomNameSuffix(); + assertUpdate("CREATE TABLE " + tableName + " (key varchar, value integer) WITH (partitioning = ARRAY['key'])"); + assertUpdate("INSERT INTO " + tableName + " VALUES ('one', 1)", 1); + assertUpdate("INSERT INTO " + tableName + " VALUES ('two', 2)", 1); + + assertExplain(sessionWithIgnoreStatsCalculatorFailuresFalse, "EXPLAIN ANALYZE ALTER TABLE " + tableName + " EXECUTE OPTIMIZE", + ".*Output layout:.*"); + } + @Test public void testRemoveOrphanFilesWithUnexpectedMissingManifest() throws Exception @@ -9324,6 +9353,13 @@ protected Session withoutSmallFileThreshold(Session session) .build(); } + private Session withoutIgnoreStatsCalculatorFailures(Session session) + { + return Session.builder(session) + .setSystemProperty(IGNORE_STATS_CALCULATOR_FAILURES, "false") + .build(); + } + private Session withSingleWriterPerTask(Session session) { return Session.builder(session)