diff --git a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 index 909779956193aa..e2980a7fdce770 100644 --- a/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 +++ b/fe/fe-core/src/main/antlr4/org/apache/doris/nereids/DorisParser.g4 @@ -53,7 +53,6 @@ statementBase | supportedDmlStatement #supportedDmlStatementAlias | supportedCreateStatement #supportedCreateStatementAlias | supportedAlterStatement #supportedAlterStatementAlias - | supportedOptimizeStatement #supportedOptimizeStatementAlias | materializedViewStatement #materializedViewStatementAlias | supportedJobStatement #supportedJobStatementAlias | constraintStatement #constraintStatementAlias @@ -273,6 +272,8 @@ supportedAlterStatement properties=propertyClause #alterStoragePolicy | ALTER TABLE tableName=multipartIdentifier alterTableClause (COMMA alterTableClause)* #alterTable + | ALTER TABLE tableName=multipartIdentifier EXECUTE actionName=identifier + LEFT_PAREN propertyItemList? RIGHT_PAREN partitionSpec? (WHERE whereExpression=booleanExpression)? #alterTableExecute | ALTER TABLE tableName=multipartIdentifier ADD ROLLUP addRollupClause (COMMA addRollupClause)* #alterTableAddRollup | ALTER TABLE tableName=multipartIdentifier DROP ROLLUP @@ -296,13 +297,6 @@ supportedAlterStatement passwordOption (COMMENT STRING_LITERAL)? #alterUser ; -supportedOptimizeStatement - : OPTIMIZE TABLE tableName=multipartIdentifier - (partitionSpec)? - (WHERE booleanExpression)? - properties=propertyClause #optimizeTable - ; - supportedDropStatement : DROP CATALOG RECYCLE BIN WHERE idType=STRING_LITERAL EQ id=INTEGER_VALUE #dropCatalogRecycleBin | DROP ENCRYPTKEY (IF EXISTS)? name=multipartIdentifier #dropEncryptkey diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/BaseIcebergAction.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/BaseIcebergAction.java index a0eba0782dc66e..ddf8a29b441b0e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/BaseIcebergAction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/BaseIcebergAction.java @@ -22,17 +22,17 @@ import org.apache.doris.datasource.iceberg.IcebergExternalTable; import org.apache.doris.info.PartitionNamesInfo; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.plans.commands.optimize.BaseOptimizeAction; +import org.apache.doris.nereids.trees.plans.commands.execute.BaseExecuteAction; import java.util.Map; import java.util.Optional; /** - * Abstract base class for Iceberg-specific OPTIMIZE TABLE actions. - * This class extends BaseOptimizeAction and provides Iceberg-specific - * functionality while inheriting common optimization action behavior. + * Abstract base class for Iceberg-specific EXECUTE TABLE actions. + * This class extends BaseExecuteAction and provides Iceberg-specific + * functionality while inheriting common execution action behavior. */ -public abstract class BaseIcebergAction extends BaseOptimizeAction { +public abstract class BaseIcebergAction extends BaseExecuteAction { protected final IcebergExternalTable icebergTable; diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergOptimizeActionFactory.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergExecuteActionFactory.java similarity index 91% rename from fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergOptimizeActionFactory.java rename to fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergExecuteActionFactory.java index 8434254ba34bae..d96934339509ef 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergOptimizeActionFactory.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/iceberg/action/IcebergExecuteActionFactory.java @@ -21,15 +21,15 @@ import org.apache.doris.datasource.iceberg.IcebergExternalTable; import org.apache.doris.info.PartitionNamesInfo; import org.apache.doris.nereids.trees.expressions.Expression; -import org.apache.doris.nereids.trees.plans.commands.optimize.OptimizeAction; +import org.apache.doris.nereids.trees.plans.commands.execute.ExecuteAction; import java.util.Map; import java.util.Optional; /** - * Factory for creating Iceberg-specific OPTIMIZE TABLE actions. + * Factory for creating Iceberg-specific EXECUTE TABLE actions. */ -public class IcebergOptimizeActionFactory { +public class IcebergExecuteActionFactory { // Iceberg procedure names (mapped to action types) public static final String ROLLBACK_TO_SNAPSHOT = "rollback_to_snapshot"; @@ -41,7 +41,7 @@ public class IcebergOptimizeActionFactory { public static final String REWRITE_DATA_FILES = "rewrite_data_files"; /** - * Create an Iceberg-specific OptimizeAction instance. + * Create an Iceberg-specific ExecuteAction instance. * * @param actionType the type of action to create (corresponds to * Iceberg procedure name) @@ -50,10 +50,10 @@ public class IcebergOptimizeActionFactory { * @param partitionNamesInfo partition information * @param whereCondition where condition for filtering * @param table the Iceberg table to operate on - * @return OptimizeAction instance that wraps Iceberg procedure calls + * @return ExecuteAction instance that wraps Iceberg procedure calls * @throws DdlException if action creation fails */ - public static OptimizeAction createAction(String actionType, Map properties, + public static ExecuteAction createAction(String actionType, Map properties, Optional partitionNamesInfo, Optional whereCondition, IcebergExternalTable table) throws DdlException { diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java index 5feda8b6240b0e..9e81e691ca31d9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java @@ -714,6 +714,7 @@ import org.apache.doris.nereids.trees.plans.commands.DropViewCommand; import org.apache.doris.nereids.trees.plans.commands.DropWorkloadGroupCommand; import org.apache.doris.nereids.trees.plans.commands.DropWorkloadPolicyCommand; +import org.apache.doris.nereids.trees.plans.commands.ExecuteActionCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; import org.apache.doris.nereids.trees.plans.commands.ExplainDictionaryCommand; @@ -728,7 +729,6 @@ import org.apache.doris.nereids.trees.plans.commands.KillQueryCommand; import org.apache.doris.nereids.trees.plans.commands.LoadCommand; import org.apache.doris.nereids.trees.plans.commands.LockTablesCommand; -import org.apache.doris.nereids.trees.plans.commands.OptimizeTableCommand; import org.apache.doris.nereids.trees.plans.commands.PauseJobCommand; import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.RecoverDatabaseCommand; @@ -7048,25 +7048,27 @@ public LogicalPlan visitUseDatabase(UseDatabaseContext ctx) { } @Override - public LogicalPlan visitOptimizeTable(DorisParser.OptimizeTableContext ctx) { - TableNameInfo tableName = new TableNameInfo(visitMultipartIdentifier(ctx.multipartIdentifier())); + public LogicalPlan visitAlterTableExecute(DorisParser.AlterTableExecuteContext ctx) { + TableNameInfo tableName = new TableNameInfo(visitMultipartIdentifier(ctx.tableName)); + String action = ctx.actionName.getText(); + + // Parse partition specification if present Optional partitionNamesInfo = Optional.empty(); if (ctx.partitionSpec() != null) { Pair> partitionSpec = visitPartitionSpec(ctx.partitionSpec()); partitionNamesInfo = Optional.of(new PartitionNamesInfo(partitionSpec.first, partitionSpec.second)); } - Optional whereCondition = ctx.booleanExpression() == null + + // Parse WHERE condition if present + Optional whereCondition = ctx.whereExpression == null ? Optional.empty() - : Optional.of((Expression) visit(ctx.booleanExpression())); - Map properties = ctx.properties == null - ? Maps.newHashMap() - : visitPropertyClause(ctx.properties); + : Optional.of((Expression) visit(ctx.whereExpression)); - return new OptimizeTableCommand( - tableName, - partitionNamesInfo, - whereCondition, - properties); + Map props = ctx.propertyItemList() == null + ? Maps.newHashMap() + : visitPropertyItemList(ctx.propertyItemList()); + return new ExecuteActionCommand( + tableName, action, props, partitionNamesInfo, whereCondition); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/OptimizeTableCommand.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteActionCommand.java similarity index 65% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/OptimizeTableCommand.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteActionCommand.java index f4f970fdba219d..b0dbff95ba4677 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/OptimizeTableCommand.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExecuteActionCommand.java @@ -30,8 +30,8 @@ import org.apache.doris.info.TableNameInfo; import org.apache.doris.nereids.trees.expressions.Expression; import org.apache.doris.nereids.trees.plans.PlanType; -import org.apache.doris.nereids.trees.plans.commands.optimize.OptimizeAction; -import org.apache.doris.nereids.trees.plans.commands.optimize.OptimizeActionFactory; +import org.apache.doris.nereids.trees.plans.commands.execute.ExecuteAction; +import org.apache.doris.nereids.trees.plans.commands.execute.ExecuteActionFactory; import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ResultSet; @@ -42,29 +42,38 @@ import java.util.Optional; /** - * OPTIMIZE TABLE tbl [PARTITION(p1, p2, ...)] [WHERE expr] PROPERTIES("action" - * = "xx", ...) + * ALTER TABLE table EXECUTE action("k" = "v", ...) [PARTITION (partition_list)] + * [WHERE condition] */ -public class OptimizeTableCommand extends Command implements ForwardWithSync { +public class ExecuteActionCommand extends Command implements ForwardWithSync { private final TableNameInfo tableNameInfo; - private final Optional partitionNamesInfo; - private final Optional whereClause; + private final String actionName; private final Map properties; - - public OptimizeTableCommand(TableNameInfo tableNameInfo, - Optional partitionNamesInfo, - Optional whereClause, - Map properties) { + private final Optional partitionNamesInfo; + private final Optional whereCondition; + + /** + * Constructor for ExecuteActionCommand. + * + * @param tableNameInfo table name information + * @param actionName name of the action to execute + * @param properties action properties as key-value pairs + * @param partitionNamesInfo optional partition information + * @param whereCondition optional where condition for filtering + */ + public ExecuteActionCommand(TableNameInfo tableNameInfo, String actionName, + Map properties, Optional partitionNamesInfo, + Optional whereCondition) { super(PlanType.OPTIMIZE_TABLE_COMMAND); this.tableNameInfo = Objects.requireNonNull(tableNameInfo, "tableNameInfo is null"); - this.partitionNamesInfo = Objects.requireNonNull(partitionNamesInfo, "partitionNamesInfo is null"); - this.whereClause = Objects.requireNonNull(whereClause, "whereClause is null"); + this.actionName = Objects.requireNonNull(actionName, "actionName is null"); this.properties = Objects.requireNonNull(properties, "properties is null"); + this.partitionNamesInfo = Objects.requireNonNull(partitionNamesInfo, "partitionNamesInfo is null"); + this.whereCondition = Objects.requireNonNull(whereCondition, "whereCondition is null"); } @Override public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { - // Get the table CatalogIf catalog = Env.getCurrentEnv().getCatalogMgr().getCatalog(tableNameInfo.getCtl()); if (catalog == null) { throw new AnalysisException("Catalog " + tableNameInfo.getCtl() + " does not exist"); @@ -81,44 +90,30 @@ public void run(ConnectContext ctx, StmtExecutor executor) throws Exception { } if (!(table instanceof ExternalTable)) { - throw new AnalysisException("OPTIMIZE TABLE is currently only supported for external tables"); + throw new AnalysisException("ALTER TABLE EXECUTE is currently only supported for external tables"); } - ExternalTable externalTable = (ExternalTable) table; - - // Get action type from properties - String actionType = properties.get("action"); - if (actionType == null || actionType.isEmpty()) { - throw new AnalysisException("OPTIMIZE TABLE requires 'action' property to be specified"); - } - - // Create and execute the appropriate action try { - OptimizeAction action = OptimizeActionFactory.createAction( - actionType, properties, partitionNamesInfo, whereClause, externalTable); + ExecuteAction action = ExecuteActionFactory.createAction( + actionName, properties, partitionNamesInfo, whereCondition, table); - if (!action.isSupported(externalTable)) { - throw new AnalysisException("Action '" + actionType + "' is not supported for this table engine"); + if (!action.isSupported(table)) { + throw new AnalysisException("Action '" + actionName + "' is not supported for this table engine"); } action.validate(tableNameInfo, ctx.getCurrentUserIdentity()); - - // Execute action and check for results - ResultSet resultSet = action.execute(externalTable); - - // If action returns results, send them to the client + ResultSet resultSet = action.execute(table); if (resultSet != null) { executor.sendResultSet(resultSet); } - } catch (UserException e) { - throw new DdlException("Failed to execute OPTIMIZE TABLE: " + e.getMessage(), e); + throw new DdlException("Failed to execute action: " + e.getMessage(), e); } } @Override public R accept(PlanVisitor visitor, C context) { - return visitor.visitOptimizeTableCommand(this, context); + return visitor.visitExecuteActionCommand(this, context); } @Override @@ -130,15 +125,19 @@ public TableNameInfo getTableNameInfo() { return tableNameInfo; } - public Optional getPartitionNamesInfo() { - return partitionNamesInfo; - } - - public Optional getWhereClause() { - return whereClause; + public String getActionName() { + return actionName; } public Map getProperties() { return properties; } + + public Optional getPartitionNamesInfo() { + return partitionNamesInfo; + } + + public Optional getWhereCondition() { + return whereCondition; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/BaseOptimizeAction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/BaseExecuteAction.java similarity index 95% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/BaseOptimizeAction.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/BaseExecuteAction.java index 9ee86bf6a636c5..840e5039dfa299 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/BaseOptimizeAction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/BaseExecuteAction.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.plans.commands.optimize; +package org.apache.doris.nereids.trees.plans.commands.execute; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.Column; @@ -44,9 +44,9 @@ import java.util.Optional; /** - * Abstract base class for all OPTIMIZE TABLE actions. + * Abstract base class for all EXECUTE TABLE actions. */ -public abstract class BaseOptimizeAction implements OptimizeAction { +public abstract class BaseExecuteAction implements ExecuteAction { protected final String actionType; protected final Map properties; @@ -59,7 +59,7 @@ public abstract class BaseOptimizeAction implements OptimizeAction { // ResultSet metadata if the action produces results protected final ResultSetMetaData resultSetMetaData; - protected BaseOptimizeAction(String actionType, Map properties, + protected BaseExecuteAction(String actionType, Map properties, Optional partitionNamesInfo, Optional whereCondition) { this.actionType = actionType; @@ -67,9 +67,6 @@ protected BaseOptimizeAction(String actionType, Map properties, this.partitionNamesInfo = partitionNamesInfo; this.whereCondition = whereCondition; - // Add OPTIMIZE TABLE specific allowed arguments - this.namedArguments.addAllowedArgument("action"); - // Register arguments specific to this action registerArguments(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeAction.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteAction.java similarity index 88% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeAction.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteAction.java index d1b1dfeef21ac7..66de1e2612f109 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeAction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteAction.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.plans.commands.optimize; +package org.apache.doris.nereids.trees.plans.commands.execute; import org.apache.doris.analysis.UserIdentity; import org.apache.doris.catalog.TableIf; @@ -29,12 +29,12 @@ import java.util.Optional; /** - * Interface for all OPTIMIZE TABLE actions in Doris. - * This provides a generic framework for implementing different optimization + * Interface for all EXECUTE TABLE actions in Doris. + * This provides a generic framework for implementing different execution * strategies across various table engines (internal tables, external tables, * etc.). */ -public interface OptimizeAction { +public interface ExecuteAction { /** * Validate the action parameters and permissions. * @@ -83,16 +83,16 @@ public interface OptimizeAction { Map getProperties(); /** - * Get partition information if specified. + * Get partition names info if specified. * - * @return optional partition names info + * @return partition names info */ Optional getPartitionNamesInfo(); /** - * Get WHERE condition if specified. + * Get where condition if specified. * - * @return optional where condition expression + * @return where condition */ Optional getWhereCondition(); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeActionFactory.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteActionFactory.java similarity index 75% rename from fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeActionFactory.java rename to fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteActionFactory.java index 08bb7c1642f0ff..33db949ed7f4bb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/optimize/OptimizeActionFactory.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/execute/ExecuteActionFactory.java @@ -15,13 +15,13 @@ // specific language governing permissions and limitations // under the License. -package org.apache.doris.nereids.trees.plans.commands.optimize; +package org.apache.doris.nereids.trees.plans.commands.execute; import org.apache.doris.catalog.TableIf; import org.apache.doris.common.DdlException; import org.apache.doris.datasource.ExternalTable; import org.apache.doris.datasource.iceberg.IcebergExternalTable; -import org.apache.doris.datasource.iceberg.action.IcebergOptimizeActionFactory; +import org.apache.doris.datasource.iceberg.action.IcebergExecuteActionFactory; import org.apache.doris.info.PartitionNamesInfo; import org.apache.doris.nereids.trees.expressions.Expression; @@ -29,41 +29,41 @@ import java.util.Optional; /** - * Factory for creating OPTIMIZE TABLE actions based on table type. + * Factory for creating EXECUTE TABLE actions based on table type. * This factory delegates to specific table engine factories to create * appropriate action instances, maintaining complete separation between - * the general OptimizeAction framework and specific table implementations. + * the general ExecuteAction framework and specific table implementations. */ -public class OptimizeActionFactory { +public class ExecuteActionFactory { /** - * Create an OptimizeAction instance based on the table type and action type. + * Create an ExecuteAction instance based on the table type and action type. * * @param actionType the type of action to create * @param properties action properties * @param partitionNamesInfo partition information * @param whereCondition where condition for filtering * @param table the table to operate on - * @return OptimizeAction instance + * @return ExecuteAction instance * @throws DdlException if action creation fails */ - public static OptimizeAction createAction(String actionType, Map properties, + public static ExecuteAction createAction(String actionType, Map properties, Optional partitionNamesInfo, Optional whereCondition, TableIf table) throws DdlException { // Delegate to specific table engine factories if (table instanceof IcebergExternalTable) { - return IcebergOptimizeActionFactory.createAction(actionType, properties, + return IcebergExecuteActionFactory.createAction(actionType, properties, partitionNamesInfo, whereCondition, (IcebergExternalTable) table); } else if (table instanceof ExternalTable) { // Handle other external table types in the future - throw new DdlException("Optimize actions are not supported for table type: " + throw new DdlException("Execute actions are not supported for table type: " + table.getClass().getSimpleName()); } else { // Handle internal tables in the future // TODO: Implement internal table action factory - throw new DdlException("Optimize actions for internal tables are not yet supported"); + throw new DdlException("Execute actions for internal tables are not yet supported"); } } @@ -75,7 +75,7 @@ public static OptimizeAction createAction(String actionType, Map */ public static String[] getSupportedActions(TableIf table) { if (table instanceof IcebergExternalTable) { - return IcebergOptimizeActionFactory.getSupportedActions(); + return IcebergExecuteActionFactory.getSupportedActions(); } // Add support for other table types in the future return new String[0]; diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java index 0a2d76e8e624ab..17e694644de1c1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/visitor/CommandVisitor.java @@ -131,6 +131,7 @@ import org.apache.doris.nereids.trees.plans.commands.DropWorkloadGroupCommand; import org.apache.doris.nereids.trees.plans.commands.DropWorkloadPolicyCommand; import org.apache.doris.nereids.trees.plans.commands.EmptyCommand; +import org.apache.doris.nereids.trees.plans.commands.ExecuteActionCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainDictionaryCommand; import org.apache.doris.nereids.trees.plans.commands.ExportCommand; @@ -144,7 +145,6 @@ import org.apache.doris.nereids.trees.plans.commands.KillQueryCommand; import org.apache.doris.nereids.trees.plans.commands.LoadCommand; import org.apache.doris.nereids.trees.plans.commands.LockTablesCommand; -import org.apache.doris.nereids.trees.plans.commands.OptimizeTableCommand; import org.apache.doris.nereids.trees.plans.commands.PauseJobCommand; import org.apache.doris.nereids.trees.plans.commands.PauseMTMVCommand; import org.apache.doris.nereids.trees.plans.commands.RecoverDatabaseCommand; @@ -655,8 +655,8 @@ default R visitAlterTableCommand(AlterTableCommand alterTableCommand, C context) return visitCommand(alterTableCommand, context); } - default R visitOptimizeTableCommand(OptimizeTableCommand optimizeTableCommand, C context) { - return visitCommand(optimizeTableCommand, context); + default R visitExecuteActionCommand(ExecuteActionCommand command, C context) { + return visitCommand(command, context); } default R visitShowGrantsCommand(ShowGrantsCommand showGrantsCommand, C context) { diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java index fdcda7f1b742be..371c09064012d1 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/parser/NereidsParserTest.java @@ -41,9 +41,9 @@ import org.apache.doris.nereids.trees.plans.commands.CreateTableCommand; import org.apache.doris.nereids.trees.plans.commands.CreateViewCommand; import org.apache.doris.nereids.trees.plans.commands.DropTableCommand; +import org.apache.doris.nereids.trees.plans.commands.ExecuteActionCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand; import org.apache.doris.nereids.trees.plans.commands.ExplainCommand.ExplainLevel; -import org.apache.doris.nereids.trees.plans.commands.OptimizeTableCommand; import org.apache.doris.nereids.trees.plans.commands.ReplayCommand; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; import org.apache.doris.nereids.trees.plans.logical.LogicalCTE; @@ -601,38 +601,104 @@ public void testTruncateTable() { } @Test - public void testOptimizeTable() { + public void testAlterTableExecute() { NereidsParser nereidsParser = new NereidsParser(); - // Basic optimize table - String sql = "optimize table t1 properties('action' = 'compact')"; + // Basic ALTER TABLE EXECUTE with rewrite_data_files action + String sql = "ALTER TABLE t1 EXECUTE rewrite_data_files(\"target-file-size-bytes\" = \"134217728\")"; LogicalPlan logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + ExecuteActionCommand cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rewrite_data_files", cmd.getActionName()); + Assertions.assertEquals("134217728", cmd.getProperties().get("target-file-size-bytes")); - // Optimize table with partition - sql = "optimize table t1 partition(p1, p2) properties('action' = 'compact')"; + // ALTER TABLE EXECUTE with expire_snapshots multiple properties + sql = "ALTER TABLE t1 EXECUTE expire_snapshots(\"older_than\" = \"2024-01-01 00:00:00\", \"retain_last\" = \"5\")"; logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); - - // Optimize table with where clause - sql = "optimize table t1 where id > 100 properties('action' = 'compact')"; + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("expire_snapshots", cmd.getActionName()); + Assertions.assertEquals("2024-01-01 00:00:00", cmd.getProperties().get("older_than")); + Assertions.assertEquals("5", cmd.getProperties().get("retain_last")); + + // ALTER TABLE EXECUTE with set_current_snapshot using ref parameter + sql = "ALTER TABLE t1 EXECUTE set_current_snapshot(\"ref\" = \"main\")"; logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); - - // Optimize table with partition and where clause - sql = "optimize table t1 partition(p1) where id > 100 properties('action' = 'compact')"; + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("set_current_snapshot", cmd.getActionName()); + Assertions.assertEquals("main", cmd.getProperties().get("ref")); + Assertions.assertFalse(cmd.getWhereCondition().isPresent()); + + // ALTER TABLE EXECUTE with WHERE clause - simple condition + sql = "ALTER TABLE t1 EXECUTE rewrite_data_files(\"target-file-size-bytes\" = \"134217728\") WHERE id > 100"; logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); - - // Optimize table with catalog and database - sql = "optimize table catalog1.db1.t1 properties('action' = 'compact')"; + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rewrite_data_files", cmd.getActionName()); + Assertions.assertEquals("134217728", cmd.getProperties().get("target-file-size-bytes")); + Assertions.assertTrue(cmd.getWhereCondition().isPresent()); + + // ALTER TABLE EXECUTE with WHERE clause - complex condition + sql = "ALTER TABLE t1 EXECUTE expire_snapshots(\"older_than\" = \"2024-01-01 00:00:00\") WHERE partition_col = 'value' AND date_col < '2024-01-01'"; logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); - - // Optimize table with multiple properties - sql = "optimize table t1 properties('action' = 'compact', 'max_files' = '10')"; + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("expire_snapshots", cmd.getActionName()); + Assertions.assertEquals("2024-01-01 00:00:00", cmd.getProperties().get("older_than")); + Assertions.assertTrue(cmd.getWhereCondition().isPresent()); + + // ALTER TABLE EXECUTE with WHERE clause - no properties + sql = "ALTER TABLE t1 EXECUTE rollback_to_snapshot(\"snapshot_id\" = \"3051729675574597004\") WHERE status = 'active'"; + logicalPlan = nereidsParser.parseSingle(sql); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rollback_to_snapshot", cmd.getActionName()); + Assertions.assertEquals("3051729675574597004", cmd.getProperties().get("snapshot_id")); + Assertions.assertTrue(cmd.getWhereCondition().isPresent()); + + // ALTER TABLE EXECUTE with partition specification - single partition + sql = "ALTER TABLE t1 EXECUTE rewrite_data_files(\"target-file-size-bytes\" = \"134217728\") PARTITION (p1)"; + logicalPlan = nereidsParser.parseSingle(sql); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rewrite_data_files", cmd.getActionName()); + Assertions.assertEquals("134217728", cmd.getProperties().get("target-file-size-bytes")); + Assertions.assertTrue(cmd.getPartitionNamesInfo().isPresent()); + Assertions.assertEquals(1, cmd.getPartitionNamesInfo().get().getPartitionNames().size()); + Assertions.assertEquals("p1", cmd.getPartitionNamesInfo().get().getPartitionNames().get(0)); + Assertions.assertFalse(cmd.getPartitionNamesInfo().get().isTemp()); + + // ALTER TABLE EXECUTE with partition specification - multiple partitions + sql = "ALTER TABLE t1 EXECUTE expire_snapshots(\"older_than\" = \"2024-01-01 00:00:00\") PARTITIONS (p1, p2, p3)"; + logicalPlan = nereidsParser.parseSingle(sql); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("expire_snapshots", cmd.getActionName()); + Assertions.assertEquals("2024-01-01 00:00:00", cmd.getProperties().get("older_than")); + Assertions.assertTrue(cmd.getPartitionNamesInfo().isPresent()); + Assertions.assertEquals(3, cmd.getPartitionNamesInfo().get().getPartitionNames().size()); + Assertions.assertFalse(cmd.getPartitionNamesInfo().get().isTemp()); + + // ALTER TABLE EXECUTE with temporary partition specification + sql = "ALTER TABLE t1 EXECUTE rewrite_data_files(\"target-file-size-bytes\" = \"134217728\") TEMPORARY PARTITION (temp_p1)"; + logicalPlan = nereidsParser.parseSingle(sql); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rewrite_data_files", cmd.getActionName()); + Assertions.assertTrue(cmd.getPartitionNamesInfo().isPresent()); + Assertions.assertTrue(cmd.getPartitionNamesInfo().get().isTemp()); + Assertions.assertEquals("temp_p1", cmd.getPartitionNamesInfo().get().getPartitionNames().get(0)); + + // ALTER TABLE EXECUTE with partition and WHERE clause + sql = "ALTER TABLE t1 EXECUTE rewrite_data_files(\"target-file-size-bytes\" = \"134217728\") PARTITION (p1) WHERE id > 100"; logicalPlan = nereidsParser.parseSingle(sql); - Assertions.assertInstanceOf(OptimizeTableCommand.class, logicalPlan); + Assertions.assertInstanceOf(ExecuteActionCommand.class, logicalPlan); + cmd = (ExecuteActionCommand) logicalPlan; + Assertions.assertEquals("rewrite_data_files", cmd.getActionName()); + Assertions.assertTrue(cmd.getPartitionNamesInfo().isPresent()); + Assertions.assertEquals("p1", cmd.getPartitionNamesInfo().get().getPartitionNames().get(0)); + Assertions.assertTrue(cmd.getWhereCondition().isPresent()); } @Test diff --git a/regression-test/data/external_table_p0/iceberg/action/test_iceberg_optimize_actions.out b/regression-test/data/external_table_p0/iceberg/action/test_iceberg_execute_actions.out similarity index 100% rename from regression-test/data/external_table_p0/iceberg/action/test_iceberg_optimize_actions.out rename to regression-test/data/external_table_p0/iceberg/action/test_iceberg_execute_actions.out diff --git a/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_optimize_actions.groovy b/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy similarity index 78% rename from regression-test/suites/external_table_p0/iceberg/action/test_iceberg_optimize_actions.groovy rename to regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy index 0cd818fe175037..94810040e8e11e 100644 --- a/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_optimize_actions.groovy +++ b/regression-test/suites/external_table_p0/iceberg/action/test_iceberg_execute_actions.groovy @@ -42,7 +42,7 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex return } - String catalog_name = "test_iceberg_optimize_actions_ddl" + String catalog_name = "test_iceberg_execute_actions_ddl" String db_name = "test_db" String rest_port = context.config.otherConfigs.get("iceberg_rest_uri_port") String minio_port = context.config.otherConfigs.get("iceberg_minio_port") @@ -223,8 +223,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Execute rollback to the earliest snapshot sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_rollback - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "${rollbackEarliestSnapshotId}") + ALTER TABLE ${catalog_name}.${db_name}.test_rollback + EXECUTE rollback_to_snapshot("snapshot_id" = "${rollbackEarliestSnapshotId}") """ qt_after_rollback_to_snapshot """SELECT * FROM test_rollback ORDER BY id""" @@ -257,8 +257,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Execute timestamp-based rollback sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_rollback_timestamp - PROPERTIES("action" = "rollback_to_timestamp", "timestamp" = "${formattedSnapshotTime}") + ALTER TABLE ${catalog_name}.${db_name}.test_rollback_timestamp + EXECUTE rollback_to_timestamp("timestamp" = "${formattedSnapshotTime}") """ qt_after_rollback_to_timestamp """SELECT * FROM test_rollback_timestamp ORDER BY id""" @@ -286,8 +286,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Execute set current snapshot by snapshot ID sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_current_snapshot - PROPERTIES("action" = "set_current_snapshot", "snapshot_id" = "${targetCurrentSnapshotId}") + ALTER TABLE ${catalog_name}.${db_name}.test_current_snapshot + EXECUTE set_current_snapshot("snapshot_id" = "${targetCurrentSnapshotId}") """ qt_after_set_current_snapshot_by_snapshotid """SELECT * FROM test_current_snapshot ORDER BY id""" @@ -301,16 +301,16 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test setting current snapshot by branch reference qt_before_set_current_snapshot_by_branch """SELECT * FROM test_current_snapshot ORDER BY id""" sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_current_snapshot - PROPERTIES("action" = "set_current_snapshot", "ref" = "dev_branch") + ALTER TABLE ${catalog_name}.${db_name}.test_current_snapshot + EXECUTE set_current_snapshot("ref" = "dev_branch") """ qt_after_set_current_snapshot_by_branch """SELECT * FROM test_current_snapshot ORDER BY id""" // Test setting current snapshot by tag reference qt_before_set_current_snapshot_by_tag """SELECT * FROM test_current_snapshot ORDER BY id""" sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_current_snapshot - PROPERTIES("action" = "set_current_snapshot", "ref" = "dev_tag") + ALTER TABLE ${catalog_name}.${db_name}.test_current_snapshot + EXECUTE set_current_snapshot("ref" = "dev_tag") """ qt_after_set_current_snapshot_by_tag """SELECT * FROM test_current_snapshot ORDER BY id""" @@ -338,15 +338,15 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Step 1: Rollback to earliest snapshot to create test scenario sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_cherrypick - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "${cherrypickEarliestSnapshotId}") + ALTER TABLE ${catalog_name}.${db_name}.test_cherrypick + EXECUTE rollback_to_snapshot("snapshot_id" = "${cherrypickEarliestSnapshotId}") """ qt_rollback_snapshot """SELECT * FROM test_cherrypick ORDER BY id""" // Step 2: Cherrypick changes from the latest snapshot sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_cherrypick - PROPERTIES("action" = "cherrypick_snapshot", "snapshot_id" = "${cherrypickLatestSnapshotId}") + ALTER TABLE ${catalog_name}.${db_name}.test_cherrypick + EXECUTE cherrypick_snapshot("snapshot_id" = "${cherrypickLatestSnapshotId}") """ qt_after_cherrypick_snapshot """SELECT * FROM test_cherrypick ORDER BY id""" @@ -381,8 +381,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex qt_before_fast_forword_branch """SELECT * FROM test_fast_forward@branch(feature_branch) ORDER BY id""" sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.test_fast_forward - PROPERTIES("action" = "fast_forward", "branch" = "feature_branch", "to" = "main") + ALTER TABLE ${catalog_name}.${db_name}.test_fast_forward + EXECUTE fast_forward("branch" = "feature_branch", "to" = "main") """ qt_after_fast_forword_branch """SELECT * FROM test_fast_forward@branch(feature_branch) ORDER BY id""" @@ -390,24 +390,24 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots action test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "2024-01-01T00:00:00") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "2024-01-01T00:00:00") """ exception "Iceberg expire_snapshots procedure is not implemented yet" } // Test rewrite_data_files action qt_test_rewrite_data_files_results """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rewrite_data_files", "target-file-size-bytes" = "134217728") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rewrite_data_files + ("target-file-size-bytes" = "134217728") """ // Test validation - missing required property test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + () """ exception "Missing required argument: snapshot_id" } @@ -415,8 +415,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test validation - negative snapshot_id test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "-123") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + ("snapshot_id" = "-123") """ exception "snapshot_id must be positive, got: -123" } @@ -424,8 +424,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test validation - zero snapshot_id test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "cherrypick_snapshot", "snapshot_id" = "0") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE cherrypick_snapshot + ("snapshot_id" = "0") """ exception "snapshot_id must be positive, got: 0" } @@ -433,8 +433,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test validation - empty snapshot_id test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "set_current_snapshot", "snapshot_id" = "") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE set_current_snapshot + ("snapshot_id" = "") """ exception "Invalid snapshot_id format:" } @@ -442,8 +442,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test validation - missing timestamp for rollback_to_timestamp test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_timestamp") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_timestamp + () """ exception "Missing required argument: timestamp" } @@ -451,8 +451,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with invalid older_than timestamp test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "not-a-timestamp") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "not-a-timestamp") """ exception "Invalid older_than format" } @@ -460,8 +460,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with negative timestamp test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "-1000") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "-1000") """ exception "older_than timestamp must be non-negative" } @@ -469,8 +469,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test validation - retain_last must be at least 1 test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "retain_last" = "0") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("retain_last" = "0") """ exception "retain_last must be positive, got: 0" } @@ -478,8 +478,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with invalid retain_last format test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "retain_last" = "not-a-number") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("retain_last" = "not-a-number") """ exception "Invalid retain_last format: not-a-number" } @@ -487,8 +487,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with negative retain_last test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "retain_last" = "-5") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("retain_last" = "-5") """ exception "retain_last must be positive, got: -5" } @@ -496,8 +496,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with neither older_than nor retain_last test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + () """ exception "At least one of 'older_than' or 'retain_last' must be specified" } @@ -505,8 +505,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with valid timestamp format (milliseconds) test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "1640995200000") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "1640995200000") """ exception "Iceberg expire_snapshots procedure is not implemented yet" } @@ -514,8 +514,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with valid ISO datetime test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "2024-01-01T12:30:45") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "2024-01-01T12:30:45") """ exception "Iceberg expire_snapshots procedure is not implemented yet" } @@ -523,8 +523,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test expire_snapshots with valid retain_last and older_than test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "expire_snapshots", "older_than" = "2024-01-01T00:00:00", "retain_last" = "5") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "2024-01-01T00:00:00", "retain_last" = "5") """ exception "Iceberg expire_snapshots procedure is not implemented yet" } @@ -532,8 +532,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test unknown action test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "unknown_action") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE unknown_action + () """ exception "Unsupported Iceberg procedure: unknown_action." } @@ -541,17 +541,16 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test missing action property test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("some_param" = "value") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE """ - exception "OPTIMIZE TABLE requires 'action' property to be specified" + exception "mismatched input ''" } // Test unknown property for specific action test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "123", "unknown_param" = "value") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + ("snapshot_id" = "123", "unknown_param" = "value") """ exception "Unknown argument: unknown_param" } @@ -559,8 +558,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test rewrite_data_files with invalid target-file-size-bytes test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rewrite_data_files", "target-file-size-bytes" = "0") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rewrite_data_files + ("target-file-size-bytes" = "0") """ exception "target-file-size-bytes must be positive, got: 0" } @@ -568,8 +567,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test rewrite_data_files with invalid file size format test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rewrite_data_files", "target-file-size-bytes" = "not-a-number") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rewrite_data_files + ("target-file-size-bytes" = "not-a-number") """ exception "Invalid target-file-size-bytes format: not-a-number" } @@ -577,8 +576,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test set_current_snapshot with both snapshot_id and ref test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "set_current_snapshot", "snapshot_id" = "123", "ref" = "main") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE set_current_snapshot + ("snapshot_id" = "123", "ref" = "main") """ exception "snapshot_id and ref are mutually exclusive, only one can be provided" } @@ -586,8 +585,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test set_current_snapshot with neither snapshot_id nor ref test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "set_current_snapshot") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE set_current_snapshot + () """ exception "Either snapshot_id or ref must be provided" } @@ -595,8 +594,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test very large snapshot_id (within Long range) test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "9223372036854775807") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + ("snapshot_id" = "9223372036854775807") """ exception "Snapshot 9223372036854775807 not found in table" } @@ -604,8 +603,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test snapshot_id exceeding Long.MAX_VALUE test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = "99999999999999999999") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + ("snapshot_id" = "99999999999999999999") """ exception "Invalid snapshot_id format: 99999999999999999999" } @@ -613,8 +612,8 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test whitespace handling in parameters test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "rollback_to_snapshot", "snapshot_id" = " 123456789 ") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE rollback_to_snapshot + ("snapshot_id" = " 123456789 ") """ exception "Snapshot 123456789 not found in table" } @@ -622,9 +621,18 @@ suite("test_iceberg_optimize_actions_ddl", "p0,external,doris,external_docker,ex // Test case sensitivity in action names test { sql """ - OPTIMIZE TABLE ${catalog_name}.${db_name}.${table_name} - PROPERTIES("action" = "ROLLBACK_TO_SNAPSHOT", "snapshot_id" = "123456789") + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE ROLLBACK_TO_SNAPSHOT + ("snapshot_id" = "123456789") """ exception "Snapshot 123456789 not found in table" } + + // Test with multiple partitions + test { + sql """ + ALTER TABLE ${catalog_name}.${db_name}.${table_name} EXECUTE expire_snapshots + ("older_than" = "2024-01-01T00:00:00") PARTITIONS (p1, p2, p3) + """ + exception "Action 'expire_snapshots' does not support partition specification" + } } \ No newline at end of file