diff --git a/core/trino-main/src/main/java/io/trino/connector/system/KillQueryProcedure.java b/core/trino-main/src/main/java/io/trino/connector/system/KillQueryProcedure.java index 779ed6ea37f9..f6ca99417434 100644 --- a/core/trino-main/src/main/java/io/trino/connector/system/KillQueryProcedure.java +++ b/core/trino-main/src/main/java/io/trino/connector/system/KillQueryProcedure.java @@ -92,8 +92,8 @@ public Procedure getProcedure() "runtime", "kill_query", ImmutableList.builder() - .add(new Argument("query_id", VARCHAR)) - .add(new Argument("message", VARCHAR)) + .add(new Argument("QUERY_ID", VARCHAR)) + .add(new Argument("MESSAGE", VARCHAR)) .build(), KILL_QUERY.bindTo(this)); } diff --git a/core/trino-main/src/main/java/io/trino/execution/CallTask.java b/core/trino-main/src/main/java/io/trino/execution/CallTask.java index 5aa5aa9f99fa..36f3a5ea68ce 100644 --- a/core/trino-main/src/main/java/io/trino/execution/CallTask.java +++ b/core/trino-main/src/main/java/io/trino/execution/CallTask.java @@ -127,7 +127,7 @@ public ListenableFuture execute( for (int i = 0; i < call.getArguments().size(); i++) { CallArgument argument = call.getArguments().get(i); if (argument.getName().isPresent()) { - String name = argument.getName().get(); + String name = argument.getName().get().getCanonicalValue(); if (names.put(name, argument) != null) { throw semanticException(INVALID_ARGUMENTS, argument, "Duplicate procedure argument: %s", name); } diff --git a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java index 4c4b7cfa0209..1a2937755c5d 100644 --- a/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java +++ b/core/trino-main/src/main/java/io/trino/sql/analyzer/StatementAnalyzer.java @@ -1127,7 +1127,7 @@ private Map processTableExecuteArguments(TableExecute node, if (anyNamed) { // all properties named for (CallArgument argument : arguments) { - if (argumentsMap.put(argument.getName().get(), argument.getValue()) != null) { + if (argumentsMap.put(argument.getName().get().getCanonicalValue(), argument.getValue()) != null) { throw semanticException(DUPLICATE_PROPERTY, argument, "Duplicate named argument: %s", argument.getName()); } } diff --git a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java index 1c9aba0b6821..ccfb20151d23 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java +++ b/core/trino-parser/src/main/java/io/trino/sql/parser/AstBuilder.java @@ -2698,7 +2698,7 @@ public Node visitPositionalArgument(SqlBaseParser.PositionalArgumentContext cont @Override public Node visitNamedArgument(SqlBaseParser.NamedArgumentContext context) { - return new CallArgument(getLocation(context), context.identifier().getText(), (Expression) visit(context.expression())); + return new CallArgument(getLocation(context), (Identifier) visit(context.identifier()), (Expression) visit(context.expression())); } @Override diff --git a/core/trino-parser/src/main/java/io/trino/sql/tree/CallArgument.java b/core/trino-parser/src/main/java/io/trino/sql/tree/CallArgument.java index 3fed6725216a..85467ad8e8e6 100644 --- a/core/trino-parser/src/main/java/io/trino/sql/tree/CallArgument.java +++ b/core/trino-parser/src/main/java/io/trino/sql/tree/CallArgument.java @@ -25,7 +25,7 @@ public final class CallArgument extends Node { - private final Optional name; + private final Optional name; private final Expression value; public CallArgument(Expression value) @@ -38,24 +38,24 @@ public CallArgument(NodeLocation location, Expression value) this(Optional.of(location), Optional.empty(), value); } - public CallArgument(String name, Expression value) + public CallArgument(Identifier name, Expression value) { this(Optional.empty(), Optional.of(name), value); } - public CallArgument(NodeLocation location, String name, Expression value) + public CallArgument(NodeLocation location, Identifier name, Expression value) { this(Optional.of(location), Optional.of(name), value); } - public CallArgument(Optional location, Optional name, Expression value) + public CallArgument(Optional location, Optional name, Expression value) { super(location); this.name = requireNonNull(name, "name is null"); this.value = requireNonNull(value, "value is null"); } - public Optional getName() + public Optional getName() { return name; } diff --git a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java index 945bd69b53ee..fc449210b4dd 100644 --- a/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java +++ b/core/trino-parser/src/test/java/io/trino/sql/parser/TestSqlParser.java @@ -1909,8 +1909,8 @@ public void testTableExecute() table, procedure, ImmutableList.of( - new CallArgument("bah", new LongLiteral("1")), - new CallArgument("wuh", new StringLiteral("clap"))), + new CallArgument(identifier("bah"), new LongLiteral("1")), + new CallArgument(identifier("wuh"), new StringLiteral("clap"))), Optional.of( new ComparisonExpression(ComparisonExpression.Operator.GREATER_THAN, new Identifier("age"), @@ -2577,8 +2577,8 @@ public void testCall() assertStatement("CALL foo()", new Call(QualifiedName.of("foo"), ImmutableList.of())); assertStatement("CALL foo(123, a => 1, b => 'go', 456)", new Call(QualifiedName.of("foo"), ImmutableList.of( new CallArgument(new LongLiteral("123")), - new CallArgument("a", new LongLiteral("1")), - new CallArgument("b", new StringLiteral("go")), + new CallArgument(identifier("a"), new LongLiteral("1")), + new CallArgument(identifier("b"), new StringLiteral("go")), new CallArgument(new LongLiteral("456"))))); } diff --git a/core/trino-spi/src/main/java/io/trino/spi/procedure/Procedure.java b/core/trino-spi/src/main/java/io/trino/spi/procedure/Procedure.java index c09c4f9bee5b..3ea556422e67 100644 --- a/core/trino-spi/src/main/java/io/trino/spi/procedure/Procedure.java +++ b/core/trino-spi/src/main/java/io/trino/spi/procedure/Procedure.java @@ -111,8 +111,22 @@ public Argument(String name, Type type) } public Argument(String name, Type type, boolean required, @Nullable Object defaultValue) + { + this(name, false, type, required, defaultValue); + } + + /** + * @deprecated Available for transition period only. After the transition period non-uppercase names will always be allowed. + */ + @Deprecated + public Argument(String name, boolean allowNonUppercaseName, Type type, boolean required, @Nullable Object defaultValue) { this.name = checkNotNullOrEmpty(name, "name"); + if (!allowNonUppercaseName && !name.equals(name.toUpperCase(ENGLISH))) { + throw new IllegalArgumentException("Argument name not uppercase. Previously argument names were matched incorrectly. " + + "This is now fixed and for backwards compatibility of CALL statements, the argument must be declared in uppercase. " + + "You can pass allowNonUppercaseName boolean flag if you want to register non-uppercase argument name."); + } this.type = requireNonNull(type, "type is null"); this.required = required; this.defaultValue = defaultValue; diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/metastore/procedure/FlushHiveMetastoreCacheProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/metastore/procedure/FlushHiveMetastoreCacheProcedure.java index 2108ea6155b8..d17fb6419aa2 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/metastore/procedure/FlushHiveMetastoreCacheProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/metastore/procedure/FlushHiveMetastoreCacheProcedure.java @@ -32,6 +32,7 @@ import static io.trino.spi.block.MethodHandleUtil.methodHandle; import static io.trino.spi.type.VarcharType.VARCHAR; import static java.lang.String.format; +import static java.util.Locale.ENGLISH; import static java.util.Objects.requireNonNull; public class FlushHiveMetastoreCacheProcedure @@ -39,20 +40,21 @@ public class FlushHiveMetastoreCacheProcedure { private static final String PROCEDURE_NAME = "flush_metadata_cache"; - private static final String PARAM_SCHEMA_NAME = "schema_name"; - private static final String PARAM_TABLE_NAME = "table_name"; - private static final String PARAM_PARTITION_COLUMN = "partition_column"; - private static final String PARAM_PARTITION_VALUE = "partition_value"; + private static final String PARAM_SCHEMA_NAME = "SCHEMA_NAME"; + private static final String PARAM_TABLE_NAME = "TABLE_NAME"; + private static final String PARAM_PARTITION_COLUMN = "PARTITION_COLUMN"; + private static final String PARAM_PARTITION_VALUE = "PARTITION_VALUE"; private static final String PROCEDURE_USAGE_EXAMPLES = format( "Valid usages:%n" + " - '%1$s()'%n" + " - %1$s(%2$s => ..., %3$s => ..., %4$s => ARRAY['...'], %5$s => ARRAY['...'])", PROCEDURE_NAME, - PARAM_SCHEMA_NAME, - PARAM_TABLE_NAME, - PARAM_PARTITION_COLUMN, - PARAM_PARTITION_VALUE); + // Use lowercase parameter names per convention. In the usage example the names are not delimited. + PARAM_SCHEMA_NAME.toLowerCase(ENGLISH), + PARAM_TABLE_NAME.toLowerCase(ENGLISH), + PARAM_PARTITION_COLUMN.toLowerCase(ENGLISH), + PARAM_PARTITION_VALUE.toLowerCase(ENGLISH)); private static final MethodHandle FLUSH_HIVE_METASTORE_CACHE = methodHandle( FlushHiveMetastoreCacheProcedure.class, @@ -79,7 +81,7 @@ public Procedure get() "system", PROCEDURE_NAME, ImmutableList.of( - new Procedure.Argument("$fake_first_parameter", VARCHAR, false, FAKE_PARAM_DEFAULT_VALUE), + new Procedure.Argument("$FAKE_FIRST_PARAMETER", VARCHAR, false, FAKE_PARAM_DEFAULT_VALUE), new Procedure.Argument(PARAM_SCHEMA_NAME, VARCHAR, false, null), new Procedure.Argument(PARAM_TABLE_NAME, VARCHAR, false, null), new Procedure.Argument(PARAM_PARTITION_COLUMN, new ArrayType(VARCHAR), false, null), diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/CreateEmptyPartitionProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/CreateEmptyPartitionProcedure.java index 044945a359c9..1a6bc7a1c429 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/CreateEmptyPartitionProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/CreateEmptyPartitionProcedure.java @@ -86,10 +86,10 @@ public Procedure get() "system", "create_empty_partition", ImmutableList.of( - new Argument("schema_name", VARCHAR), - new Argument("table_name", VARCHAR), - new Argument("partition_columns", new ArrayType(VARCHAR)), - new Argument("partition_values", new ArrayType(VARCHAR))), + new Argument("SCHEMA_NAME", VARCHAR), + new Argument("TABLE_NAME", VARCHAR), + new Argument("PARTITION_COLUMNS", new ArrayType(VARCHAR)), + new Argument("PARTITION_VALUES", new ArrayType(VARCHAR))), CREATE_EMPTY_PARTITION.bindTo(this)); } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/DropStatsProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/DropStatsProcedure.java index 6196686d0c6a..3b9fa2fccf32 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/DropStatsProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/DropStatsProcedure.java @@ -79,9 +79,9 @@ public Procedure get() "system", "drop_stats", ImmutableList.of( - new Argument("schema_name", VARCHAR), - new Argument("table_name", VARCHAR), - new Argument("partition_values", new ArrayType(new ArrayType(VARCHAR)), false, null)), + new Argument("SCHEMA_NAME", VARCHAR), + new Argument("TABLE_NAME", VARCHAR), + new Argument("PARTITION_VALUES", new ArrayType(new ArrayType(VARCHAR)), false, null)), DROP_STATS.bindTo(this)); } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/RegisterPartitionProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/RegisterPartitionProcedure.java index a081742ec548..b1db53d9ab94 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/RegisterPartitionProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/RegisterPartitionProcedure.java @@ -86,11 +86,11 @@ public Procedure get() "system", "register_partition", ImmutableList.of( - new Procedure.Argument("schema_name", VARCHAR), - new Procedure.Argument("table_name", VARCHAR), - new Procedure.Argument("partition_columns", new ArrayType(VARCHAR)), - new Procedure.Argument("partition_values", new ArrayType(VARCHAR)), - new Procedure.Argument("location", VARCHAR, false, null)), + new Procedure.Argument("SCHEMA_NAME", VARCHAR), + new Procedure.Argument("TABLE_NAME", VARCHAR), + new Procedure.Argument("PARTITION_COLUMNS", new ArrayType(VARCHAR)), + new Procedure.Argument("PARTITION_VALUES", new ArrayType(VARCHAR)), + new Procedure.Argument("LOCATION", VARCHAR, false, null)), REGISTER_PARTITION.bindTo(this)); } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/SyncPartitionMetadataProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/SyncPartitionMetadataProcedure.java index a6fc658cf9ff..2a6ecd3dbb18 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/SyncPartitionMetadataProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/SyncPartitionMetadataProcedure.java @@ -96,10 +96,10 @@ public Procedure get() "system", "sync_partition_metadata", ImmutableList.of( - new Argument("schema_name", VARCHAR), - new Argument("table_name", VARCHAR), - new Argument("mode", VARCHAR), - new Argument("case_sensitive", BOOLEAN, false, TRUE)), + new Argument("SCHEMA_NAME", VARCHAR), + new Argument("TABLE_NAME", VARCHAR), + new Argument("MODE", VARCHAR), + new Argument("CASE_SENSITIVE", BOOLEAN, false, TRUE)), SYNC_PARTITION_METADATA.bindTo(this)); } diff --git a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/UnregisterPartitionProcedure.java b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/UnregisterPartitionProcedure.java index 5f9db471c1a8..b68a0390e04d 100644 --- a/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/UnregisterPartitionProcedure.java +++ b/plugin/trino-hive/src/main/java/io/trino/plugin/hive/procedure/UnregisterPartitionProcedure.java @@ -70,10 +70,10 @@ public Procedure get() "system", "unregister_partition", ImmutableList.of( - new Procedure.Argument("schema_name", VARCHAR), - new Procedure.Argument("table_name", VARCHAR), - new Procedure.Argument("partition_columns", new ArrayType(VARCHAR)), - new Procedure.Argument("partition_values", new ArrayType(VARCHAR))), + new Procedure.Argument("SCHEMA_NAME", VARCHAR), + new Procedure.Argument("TABLE_NAME", VARCHAR), + new Procedure.Argument("PARTITION_COLUMNS", new ArrayType(VARCHAR)), + new Procedure.Argument("PARTITION_VALUES", new ArrayType(VARCHAR))), UNREGISTER_PARTITION.bindTo(this)); } diff --git a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java index e4dc1c4d77e4..b7fd9dc8db92 100644 --- a/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java +++ b/plugin/trino-hive/src/test/java/io/trino/plugin/hive/BaseHiveConnectorTest.java @@ -7891,6 +7891,13 @@ public void testOptimize() assertUpdate(optimizeEnabledSession, "ALTER TABLE " + tableName + " EXECUTE optimize(file_size_threshold => '10B')"); assertThat(getTableFiles(tableName)).hasSameElementsAs(compactedFiles); + // optimize with delimited procedure name + assertQueryFails(optimizeEnabledSession, "ALTER TABLE " + tableName + " EXECUTE \"optimize\"", "Procedure optimize not registered for catalog hive"); + assertUpdate(optimizeEnabledSession, "ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\""); + // optimize with delimited parameter name (and procedure name) + assertUpdate(optimizeEnabledSession, "ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\" (\"file_size_threshold\" => '10B')"); // TODO (https://github.com/trinodb/trino/issues/11326) this should fail + assertUpdate(optimizeEnabledSession, "ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\" (\"FILE_SIZE_THRESHOLD\" => '10B')"); + assertUpdate("DROP TABLE " + tableName); } diff --git a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/RollbackToSnapshotProcedure.java b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/RollbackToSnapshotProcedure.java index 2f44064d84a3..46b2965c356b 100644 --- a/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/RollbackToSnapshotProcedure.java +++ b/plugin/trino-iceberg/src/main/java/io/trino/plugin/iceberg/RollbackToSnapshotProcedure.java @@ -55,9 +55,9 @@ public Procedure get() "system", "rollback_to_snapshot", ImmutableList.of( - new Procedure.Argument("schema", VARCHAR), - new Procedure.Argument("table", VARCHAR), - new Procedure.Argument("snapshot_id", BIGINT)), + new Procedure.Argument("SCHEMA", VARCHAR), + new Procedure.Argument("TABLE", VARCHAR), + new Procedure.Argument("SNAPSHOT_ID", BIGINT)), ROLLBACK_TO_SNAPSHOT.bindTo(this)); } 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 4579f9e7a9a0..c29ff2d49f8e 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 @@ -3254,6 +3254,13 @@ public void testOptimize() assertThat(getAllDataFilesFromTableDirectory(tableName)) .containsExactlyInAnyOrderElementsOf(concat(initialFiles, updatedFiles)); + // optimize with delimited procedure name + assertQueryFails("ALTER TABLE " + tableName + " EXECUTE \"optimize\"", "Procedure optimize not registered for catalog iceberg"); + assertUpdate("ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\""); + // optimize with delimited parameter name (and procedure name) + assertUpdate("ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\" (\"file_size_threshold\" => '33B')"); // TODO (https://github.com/trinodb/trino/issues/11326) this should fail + assertUpdate("ALTER TABLE " + tableName + " EXECUTE \"OPTIMIZE\" (\"FILE_SIZE_THRESHOLD\" => '33B')"); + assertUpdate("DROP TABLE " + tableName); } diff --git a/plugin/trino-kudu/src/main/java/io/trino/plugin/kudu/procedures/RangePartitionProcedures.java b/plugin/trino-kudu/src/main/java/io/trino/plugin/kudu/procedures/RangePartitionProcedures.java index 171c49e9c139..f88ec6e6f687 100644 --- a/plugin/trino-kudu/src/main/java/io/trino/plugin/kudu/procedures/RangePartitionProcedures.java +++ b/plugin/trino-kudu/src/main/java/io/trino/plugin/kudu/procedures/RangePartitionProcedures.java @@ -50,9 +50,9 @@ public Procedure getAddPartitionProcedure() "system", "add_range_partition", ImmutableList.of( - new Argument("schema", VARCHAR), - new Argument("table", VARCHAR), - new Argument("range_bounds", VARCHAR)), + new Argument("SCHEMA", VARCHAR), + new Argument("TABLE", VARCHAR), + new Argument("RANGE_BOUNDS", VARCHAR)), ADD.bindTo(this)); } @@ -62,9 +62,9 @@ public Procedure getDropPartitionProcedure() "system", "drop_range_partition", ImmutableList.of( - new Argument("schema", VARCHAR), - new Argument("table", VARCHAR), - new Argument("range_bounds", VARCHAR)), + new Argument("SCHEMA", VARCHAR), + new Argument("TABLE", VARCHAR), + new Argument("RANGE_BOUNDS", VARCHAR)), DROP.bindTo(this)); } diff --git a/testing/trino-testing/src/main/java/io/trino/testing/TestingProcedures.java b/testing/trino-testing/src/main/java/io/trino/testing/TestingProcedures.java index bf37ee3442ad..0cfdf28faf0f 100644 --- a/testing/trino-testing/src/main/java/io/trino/testing/TestingProcedures.java +++ b/testing/trino-testing/src/main/java/io/trino/testing/TestingProcedures.java @@ -127,43 +127,56 @@ public void error() throw new RuntimeException("test error from procedure"); } + @UsedByGeneratedCode + public void names(ConnectorSession session, String x, String y, String z, String v) + { + tester.recordCalled("names", x, y, z, v); + } + public List getProcedures(String schema) { return ImmutableList.builder() .add(procedure(schema, "test_simple", "simple", ImmutableList.of())) + .add(procedure(schema, "test_lowercase_name", "simple", ImmutableList.of())) + .add(procedure(schema, "TEST_UPPERCASE_NAME", "simple", ImmutableList.of())) .add(procedure(schema, "test_args", "args", ImmutableList.of( - new Argument("x", BIGINT), - new Argument("y", DOUBLE), - new Argument("z", VARCHAR), - new Argument("q", BOOLEAN)))) + new Argument("X", BIGINT), + new Argument("Y", DOUBLE), + new Argument("Z", VARCHAR), + new Argument("Q", BOOLEAN)))) .add(procedure(schema, "test_nulls", "nulls", ImmutableList.of( - new Argument("x", BIGINT), - new Argument("y", VARCHAR)))) + new Argument("X", BIGINT), + new Argument("Y", VARCHAR)))) .add(procedure(schema, "test_arrays", "arrays", ImmutableList.of( - new Argument("x", new ArrayType(BIGINT)), - new Argument("y", new ArrayType(VARCHAR))))) + new Argument("X", new ArrayType(BIGINT)), + new Argument("Y", new ArrayType(VARCHAR))))) .add(procedure(schema, "test_nested", "nested", ImmutableList.of( - new Argument("x", new ArrayType(new ArrayType(BIGINT)))))) + new Argument("X", new ArrayType(new ArrayType(BIGINT)))))) .add(procedure(schema, "test_session_first", "sessionFirst", ImmutableList.of( - new Argument("x", BIGINT)))) + new Argument("X", BIGINT)))) .add(procedure(schema, "test_session_last", "sessionLast", ImmutableList.of( - new Argument("x", VARCHAR)))) + new Argument("X", VARCHAR)))) .add(procedure(schema, "test_optionals", "optionals", ImmutableList.of( - new Argument("x", VARCHAR, false, "hello")))) + new Argument("X", VARCHAR, false, "hello")))) .add(procedure(schema, "test_optionals2", "optionals2", ImmutableList.of( - new Argument("x", VARCHAR), - new Argument("y", VARCHAR, false, "world")))) + new Argument("X", VARCHAR), + new Argument("Y", VARCHAR, false, "world")))) .add(procedure(schema, "test_optionals3", "optionals3", ImmutableList.of( - new Argument("x", VARCHAR, false, "this"), - new Argument("y", VARCHAR, false, "is"), - new Argument("z", VARCHAR, false, "default")))) + new Argument("X", VARCHAR, false, "this"), + new Argument("Y", VARCHAR, false, "is"), + new Argument("Z", VARCHAR, false, "default")))) .add(procedure(schema, "test_optionals4", "optionals4", ImmutableList.of( - new Argument("x", VARCHAR), - new Argument("y", VARCHAR), - new Argument("z", VARCHAR, false, "z default"), - new Argument("v", VARCHAR, false, "v default")))) + new Argument("X", VARCHAR), + new Argument("Y", VARCHAR), + new Argument("Z", VARCHAR, false, "z default"), + new Argument("V", VARCHAR, false, "v default")))) .add(procedure(schema, "test_exception", "exception", ImmutableList.of())) .add(procedure(schema, "test_error", "error", ImmutableList.of())) + .add(procedure(schema, "test_argument_names", "names", ImmutableList.of( + new Argument("lower", true, VARCHAR, false, "a"), + new Argument("UPPER", true, VARCHAR, false, "b"), + new Argument("MixeD", true, VARCHAR, false, "c"), + new Argument("with space", true, VARCHAR, false, "d")))) .build(); } diff --git a/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCall.java b/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCall.java index 1f40918b21e5..1ec93d57e26e 100644 --- a/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCall.java +++ b/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCall.java @@ -111,16 +111,16 @@ public void testProcedureCall() assertCallThrows("CALL test_exception()", "exception", "test exception from procedure"); assertCallThrows("CALL test_error()", "error", "test error from procedure"); - assertCallFails("CALL test_args(null, 4.5, 'hello', true)", "Procedure argument cannot be null: x"); - assertCallFails("CALL test_args(123, null, 'hello', true)", "Procedure argument cannot be null: y"); - assertCallFails("CALL test_args(123, 4.5, 'hello', null)", "Procedure argument cannot be null: q"); + assertCallFails("CALL test_args(null, 4.5, 'hello', true)", "Procedure argument cannot be null: X"); + assertCallFails("CALL test_args(123, null, 'hello', true)", "Procedure argument cannot be null: Y"); + assertCallFails("CALL test_args(123, 4.5, 'hello', null)", "Procedure argument cannot be null: Q"); assertCallFails("CALL test_simple(123)", "line 1:1: Too many arguments for procedure"); - assertCallFails("CALL test_args(123, 4.5, 'hello')", "line 1:1: Required procedure argument 'q' is missing"); - assertCallFails("CALL test_args(x => 123, y => 4.5, q => true)", "line 1:1: Required procedure argument 'z' is missing"); + assertCallFails("CALL test_args(123, 4.5, 'hello')", "line 1:1: Required procedure argument 'Q' is missing"); + assertCallFails("CALL test_args(x => 123, y => 4.5, q => true)", "line 1:1: Required procedure argument 'Z' is missing"); assertCallFails("CALL test_args(123, 4.5, 'hello', q => true)", "line 1:1: Named and positional arguments cannot be mixed"); - assertCallFails("CALL test_args(x => 3, x => 4)", "line 1:24: Duplicate procedure argument: x"); - assertCallFails("CALL test_args(t => 404)", "line 1:16: Unknown argument name: t"); + assertCallFails("CALL test_args(x => 3, x => 4)", "line 1:24: Duplicate procedure argument: X"); + assertCallFails("CALL test_args(t => 404)", "line 1:16: Unknown argument name: T"); assertCallFails("CALL test_nulls('hello', null)", "line 1:17: Cannot cast type varchar(5) to bigint"); assertCallFails("CALL test_nulls(null, 123)", "line 1:23: Cannot cast type integer to varchar"); } @@ -150,10 +150,51 @@ public void testProcedureCallWithOptionals() assertCall("CALL test_optionals4(z => 'z val', v => 'v val', x => 'x val', y => 'y val')", "optionals4", "x val", "y val", "z val", "v val"); assertCall("CALL test_optionals4(v => 'v val', x => 'x val', y => 'y val', z => 'z val')", "optionals4", "x val", "y val", "z val", "v val"); - assertCallFails("CALL test_optionals2()", "line 1:1: Required procedure argument 'x' is missing"); - assertCallFails("CALL test_optionals4(z => 'cd')", "line 1:1: Required procedure argument 'x' is missing"); - assertCallFails("CALL test_optionals4(z => 'cd', v => 'value')", "line 1:1: Required procedure argument 'x' is missing"); - assertCallFails("CALL test_optionals4(y => 'cd', v => 'value')", "line 1:1: Required procedure argument 'x' is missing"); + assertCallFails("CALL test_optionals2()", "line 1:1: Required procedure argument 'X' is missing"); + assertCallFails("CALL test_optionals4(z => 'cd')", "line 1:1: Required procedure argument 'X' is missing"); + assertCallFails("CALL test_optionals4(z => 'cd', v => 'value')", "line 1:1: Required procedure argument 'X' is missing"); + assertCallFails("CALL test_optionals4(y => 'cd', v => 'value')", "line 1:1: Required procedure argument 'X' is missing"); + } + + @Test + public void testProcedureName() + { + assertCall("CALL test_lowercase_name()", "simple"); + assertCall("CALL TEST_LOWERCASE_NAME()", "simple"); + assertCall("CALL Test_Lowercase_NAME()", "simple"); + assertCall("CALL \"test_lowercase_name\"()", "simple"); + assertCall("CALL \"TEST_LOWERCASE_NAME\"()", "simple"); + assertCall("CALL \"Test_Lowercase_Name\"()", "simple"); + + assertCall("CALL test_uppercase_name()", "simple"); + assertCall("CALL TEST_UPPERCASE_NAME()", "simple"); + assertCall("CALL Test_Uppercase_NAME()", "simple"); + assertCall("CALL \"test_uppercase_name\"()", "simple"); + assertCall("CALL \"TEST_UPPERCASE_NAME\"()", "simple"); + assertCall("CALL \"Test_Uppercase_NAME\"()", "simple"); + } + + @Test + public void testNamedArguments() + { + assertCallFails("CALL test_argument_names(lower => 'a')", "line 1:26: Unknown argument name: LOWER"); + assertCallFails("CALL test_argument_names(LOWER => 'a')", "line 1:26: Unknown argument name: LOWER"); + assertCall("CALL test_argument_names(\"lower\" => 'a')", "names", "a", "b", "c", "d"); + assertCallFails("CALL test_argument_names(\"LOWER\" => 'a')", "line 1:26: Unknown argument name: LOWER"); + + assertCall("CALL test_argument_names(upper => 'b')", "names", "a", "b", "c", "d"); + assertCall("CALL test_argument_names(UPPER => 'b')", "names", "a", "b", "c", "d"); + assertCallFails("CALL test_argument_names(\"upper\" => 'b')", "line 1:26: Unknown argument name: upper"); + assertCall("CALL test_argument_names(\"UPPER\" => 'b')", "names", "a", "b", "c", "d"); + + assertCallFails("CALL test_argument_names(mixed => 'c')", "line 1:26: Unknown argument name: MIXED"); + assertCallFails("CALL test_argument_names(MixeD => 'c')", "line 1:26: Unknown argument name: MIXED"); + assertCallFails("CALL test_argument_names(MIXED => 'c')", "line 1:26: Unknown argument name: MIXED"); + assertCallFails("CALL test_argument_names(\"mixed\" => 'c')", "line 1:26: Unknown argument name: mixed"); + assertCall("CALL test_argument_names(\"MixeD\" => 'c')", "names", "a", "b", "c", "d"); + assertCallFails("CALL test_argument_names(\"MIXED\" => 'c')", "line 1:26: Unknown argument name: MIXED"); + + assertCall("CALL test_argument_names(\"with space\" => 'd')", "names", "a", "b", "c", "d"); } private void assertCall(@Language("SQL") String sql, String name, Object... arguments) diff --git a/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCreation.java b/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCreation.java index a32e65a0ab13..5a22853a5012 100644 --- a/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCreation.java +++ b/testing/trino-tests/src/test/java/io/trino/tests/TestProcedureCreation.java @@ -24,24 +24,23 @@ import static io.trino.spi.type.VarcharType.VARCHAR; import static org.assertj.core.api.Assertions.assertThatThrownBy; -@Test(singleThreaded = true) public class TestProcedureCreation { @Test public void shouldThrowExceptionWhenOptionalArgumentIsNotLast() { assertThatThrownBy(() -> createTestProcedure(ImmutableList.of( - new Procedure.Argument("name", VARCHAR, false, null), - new Procedure.Argument("name2", VARCHAR, true, null)))) + new Procedure.Argument("NAME", VARCHAR, false, null), + new Procedure.Argument("NAME2", VARCHAR, true, null)))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Optional arguments should follow required ones"); assertThatThrownBy(() -> createTestProcedure(ImmutableList.of( - new Procedure.Argument("name", VARCHAR, true, null), - new Procedure.Argument("name2", VARCHAR, true, null), - new Procedure.Argument("name3", VARCHAR, true, null), - new Procedure.Argument("name4", VARCHAR, false, null), - new Procedure.Argument("name5", VARCHAR, true, null)))) + new Procedure.Argument("NAME", VARCHAR, true, null), + new Procedure.Argument("NAME2", VARCHAR, true, null), + new Procedure.Argument("NAME3", VARCHAR, true, null), + new Procedure.Argument("NAME4", VARCHAR, false, null), + new Procedure.Argument("NAME5", VARCHAR, true, null)))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Optional arguments should follow required ones"); } @@ -50,10 +49,10 @@ public void shouldThrowExceptionWhenOptionalArgumentIsNotLast() public void shouldThrowExceptionWhenArgumentNameRepeates() { assertThatThrownBy(() -> createTestProcedure(ImmutableList.of( - new Procedure.Argument("name", VARCHAR, false, null), - new Procedure.Argument("name", VARCHAR, true, null)))) + new Procedure.Argument("NAME", VARCHAR, false, null), + new Procedure.Argument("NAME", VARCHAR, true, null)))) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Duplicate argument name: 'name'"); + .hasMessage("Duplicate argument name: 'NAME'"); } @Test @@ -99,9 +98,9 @@ public void shouldThrowExceptionWhenArgumentCountDoesntMatch() "schema", "name", ImmutableList.of( - new Procedure.Argument("name", VARCHAR, true, null), - new Procedure.Argument("name2", VARCHAR, true, null), - new Procedure.Argument("name3", VARCHAR, true, null)), + new Procedure.Argument("NAME", VARCHAR, true, null), + new Procedure.Argument("NAME2", VARCHAR, true, null), + new Procedure.Argument("NAME3", VARCHAR, true, null)), methodHandle(Procedures.class, "fun1", ConnectorSession.class, Object.class))) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Method parameter count must match arguments");