This is an automated email from the ASF dual-hosted git repository. amashenkov pushed a commit to branch ignite-22161 in repository https://gitbox.apache.org/repos/asf/ignite-3.git
commit bfaf586b4ae8c8c803ea68e24d88378b4a2f3289 Author: amashenkov <[email protected]> AuthorDate: Fri May 3 17:05:34 2024 +0300 wip. --- .../catalog/commands/CreateTableCommand.java | 21 +++++-- .../internal/sql/engine/ItCreateTableDdlTest.java | 25 ++++++++ .../internal/sql/engine/sql/SqlDdlParserTest.java | 69 +++++++++++++++++++++- 3 files changed, 110 insertions(+), 5 deletions(-) diff --git a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java index 565cdce0bf..0a2ef4a8d4 100644 --- a/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java +++ b/modules/catalog/src/main/java/org/apache/ignite/internal/catalog/commands/CreateTableCommand.java @@ -35,6 +35,7 @@ import java.util.Set; import org.apache.ignite.internal.catalog.Catalog; import org.apache.ignite.internal.catalog.CatalogCommand; import org.apache.ignite.internal.catalog.CatalogValidationException; +import org.apache.ignite.internal.catalog.commands.DefaultValue.FunctionCall; import org.apache.ignite.internal.catalog.commands.DefaultValue.Type; import org.apache.ignite.internal.catalog.descriptors.CatalogColumnCollation; import org.apache.ignite.internal.catalog.descriptors.CatalogHashIndexDescriptor; @@ -176,10 +177,22 @@ public class CreateTableCommand extends AbstractTableCommand { primaryKey.validate(columns); for (ColumnParams column : columns) { - boolean partOfPk = primaryKey.columns().contains(column.name()); - if (!partOfPk && column.defaultValueDefinition().type == Type.FUNCTION_CALL) { - throw new CatalogValidationException( - format("Functional defaults are not supported for non-primary key columns [col={}].", column.name())); + DefaultValue defaultValue = column.defaultValueDefinition(); + + if (defaultValue.type == Type.FUNCTION_CALL) { + boolean partOfPk = primaryKey.columns().contains(column.name()); + if (!partOfPk) { + throw new CatalogValidationException( + format("Functional defaults are not supported for non-primary key columns [col={}].", column.name())); + } + + // TODO: fix function validation. + String functionName = ((FunctionCall) defaultValue).functionName(); + if (!"gen_random_uuid".equalsIgnoreCase(functionName)) { + throw new CatalogValidationException( + format("Functional default contains unsupported function: [col={}, functionName={}]", + column.name(), functionName)); + } } } diff --git a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java index 27869fc632..57a0336ebf 100644 --- a/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java +++ b/modules/sql-engine/src/integrationTest/java/org/apache/ignite/internal/sql/engine/ItCreateTableDdlTest.java @@ -141,6 +141,15 @@ public class ItCreateTableDdlTest extends BaseSqlIntegrationTest { assertThat(result, hasSize(2)); // both rows are inserted without conflict } + @Test + public void pkWithInvalidFunctionalDefault() { + assertThrowsSqlException( + STMT_VALIDATION_ERR, + "Functional default contains unsupported function: [col=ID, functionName=INVALID_FUNC]", + () -> sql("create table t (id varchar default invalid_func primary key, val int)") + ); + } + @Test public void undefinedColumnsInPrimaryKey() { assertThrowsSqlException( @@ -332,6 +341,22 @@ public class ItCreateTableDdlTest extends BaseSqlIntegrationTest { assertEquals("Id0", colocationColumns.get(0).name()); } + @Test + public void literalAsColumDefault() { + sql("CREATE TABLE T0(" + + "id BIGINT DEFAULT 1, " + + "valdate DATE DEFAULT DATE '2001-12-21'," + + "valtime TIME DEFAULT TIME '11:22:33.444'," + + "valts TIMESTAMP DEFAULT TIMESTAMP '2001-12-21 11:22:33.444'," + + "valstr VARCHAR DEFAULT 'string'," + + "valbin VARBINARY DEFAULT x'ff'" + + ")"); + + List<Column> columns = unwrapTableViewInternal(table("T0")).schemaView().lastKnownSchema().columns(); + + assertEquals(6, columns.size()); + } + @Test public void doNotAllowFunctionsInNonPkColumns() { assertThrowsSqlException( diff --git a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java index 7cbab7832e..5d476acecc 100644 --- a/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java +++ b/modules/sql-engine/src/test/java/org/apache/ignite/internal/sql/engine/sql/SqlDdlParserTest.java @@ -34,10 +34,14 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.calcite.schema.ColumnStrategy; import org.apache.calcite.sql.SqlBasicCall; +import org.apache.calcite.sql.SqlBinaryStringLiteral; +import org.apache.calcite.sql.SqlCharStringLiteral; import org.apache.calcite.sql.SqlIdentifier; import org.apache.calcite.sql.SqlKind; import org.apache.calcite.sql.SqlLiteral; import org.apache.calcite.sql.SqlNode; +import org.apache.calcite.sql.SqlNumericLiteral; +import org.apache.calcite.sql.SqlUnknownLiteral; import org.apache.calcite.sql.ddl.SqlColumnDeclaration; import org.apache.ignite.lang.ErrorGroups.Sql; import org.hamcrest.CustomMatcher; @@ -109,6 +113,69 @@ public class SqlDdlParserTest extends AbstractDdlParserTest { ); } + /** + * Parsing of CREATE TABLE with a literal as a default expression. + */ + @Test + public void createTableWithDefaultLiteral() { + String query = "CREATE TABLE my_table(" + + "id BIGINT DEFAULT 1, " + + "valdate DATE DEFAULT DATE '2001-12-21'," + + "valdate2 DATE DEFAULT '2001-12-21'," + + "valtime TIME DEFAULT TIME '11:22:33.444'," + + "valtime2 TIME DEFAULT '11:22:33.444'," + + "valts TIMESTAMP DEFAULT TIMESTAMP '2001-12-21 11:22:33.444'," + + "valts2 TIMESTAMP DEFAULT '2001-12-21 11:22:33.444'," + + "valbin VARBINARY DEFAULT x'ff'," + + "valstr VARCHAR DEFAULT 'string'" + + ")"; + + SqlNode node = parse(query); + + assertThat(node, instanceOf(IgniteSqlCreateTable.class)); + + IgniteSqlCreateTable createTable = (IgniteSqlCreateTable) node; + + assertThat(createTable.name().names, is(List.of("MY_TABLE"))); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("ID", SqlNumericLiteral.class, "1")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALSTR", SqlCharStringLiteral.class, "string")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALBIN", SqlBinaryStringLiteral.class, "11111111")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALDATE2", SqlCharStringLiteral.class, "2001-12-21")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALTIME2", SqlCharStringLiteral.class, "11:22:33.444")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALTS2", SqlCharStringLiteral.class, "2001-12-21 11:22:33.444")); + // TODO: should we fix literal type somehow? + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALDATE", SqlUnknownLiteral.class, "2001-12-21")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALTIME", SqlUnknownLiteral.class, "11:22:33.444")); + assertThat(createTable.columnList(), hasColumnWithLiteralAsDefault("VALTS", SqlUnknownLiteral.class, "2001-12-21 11:22:33.444")); + + + expectUnparsed(node, "CREATE TABLE \"MY_TABLE\" " + + "(\"ID\" BIGINT DEFAULT (1), " + + "\"VALDATE\" DATE DEFAULT (DATE '2001-12-21'), " + + "\"VALDATE2\" DATE DEFAULT ('2001-12-21'), " + + "\"VALTIME\" TIME DEFAULT (TIME '11:22:33.444'), " + + "\"VALTIME2\" TIME DEFAULT ('11:22:33.444'), " + + "\"VALTS\" TIMESTAMP DEFAULT (TIMESTAMP '2001-12-21 11:22:33.444'), " + + "\"VALTS2\" TIMESTAMP DEFAULT ('2001-12-21 11:22:33.444'), " + + "\"VALBIN\" VARBINARY DEFAULT (X'FF'), " + + "\"VALSTR\" VARCHAR DEFAULT ('string'))" + ); + } + + private Matcher<Iterable<? super SqlColumnDeclaration>> hasColumnWithLiteralAsDefault( + String columnName, + Class<? extends SqlLiteral> literalType, + String literalValue + ) { + return hasItem(ofTypeMatching( + "Column with literal as default: columnName=" + columnName, + SqlColumnDeclaration.class, + col -> columnName.equals(col.name.getSimple()) + && literalType.isInstance(col.expression) + && literalValue.equals(((SqlLiteral) col.expression).toValue()) + )); + } + /** * Parsing of CREATE TABLE statement with quoted identifiers. */ @@ -634,7 +701,7 @@ public class SqlDdlParserTest extends AbstractDdlParserTest { && ((SqlBasicCall) bc.getOperandList().get(0)).getOperandList().get(0) instanceof SqlIdentifier && ((SqlIdentifier) ((SqlBasicCall) bc.getOperandList().get(0)).getOperandList().get(0)).isSimple() && ((SqlIdentifier) ((SqlBasicCall) bc.getOperandList().get(0)).getOperandList().get(0)) - .getSimple().equals("COL3")))); + .getSimple().equals("COL3")))); expectUnparsed(node, "CREATE INDEX \"MY_INDEX\" ON \"MY_TABLE\" (" + "\"COL1\" NULLS FIRST, \"COL2\" NULLS LAST, \"COL3\" DESC NULLS FIRST)"
