From 08be0478da5a36946d62f8d90b8840866950f86a Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Tue, 23 Nov 2021 16:45:28 +0100 Subject: [PATCH] [jOOQ/jOOQ#6492] Add support for computed columns - Add DataType.generatedAlwaysAs(Field) - Add code generation support for: - H2 - PostgreSQL - Added parser support - Added code generation tests - Added DDL support --- .../java/org/jooq/codegen/JavaGenerator.java | 32 ++++- .../AbstractElementContainerDefinition.java | 1 - .../org/jooq/meta/DataTypeDefinition.java | 10 ++ .../jooq/meta/DefaultDataTypeDefinition.java | 21 ++- .../org/jooq/meta/h2/H2TableDefinition.java | 6 +- .../postgres/PostgresTableDefinition.java | 4 +- jOOQ/src/main/java/org/jooq/DataType.java | 38 +++++ .../java/org/jooq/impl/AbstractDataType.java | 16 +++ .../java/org/jooq/impl/AbstractDataTypeX.java | 136 ++++++++++++++++-- .../java/org/jooq/impl/ArrayDataType.java | 5 +- .../java/org/jooq/impl/ConvertedDataType.java | 7 + .../java/org/jooq/impl/DataTypeProxy.java | 36 ++++- .../java/org/jooq/impl/DefaultDataType.java | 60 +++++++- .../java/org/jooq/impl/DomainDataType.java | 1 + .../src/main/java/org/jooq/impl/Keywords.java | 3 +- .../java/org/jooq/impl/MultisetDataType.java | 5 +- .../main/java/org/jooq/impl/ParserImpl.java | 123 +++++++++------- .../java/org/jooq/impl/RecordDataType.java | 5 +- jOOQ/src/main/java/org/jooq/impl/Tools.java | 47 +++++- 19 files changed, 471 insertions(+), 85 deletions(-) diff --git a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java index e810bd8ea8..cb29078b9c 100644 --- a/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java +++ b/jOOQ-codegen/src/main/java/org/jooq/codegen/JavaGenerator.java @@ -8489,6 +8489,7 @@ public class JavaGenerator extends AbstractGenerator { false, false, null, + null, baseType ) + ".getArrayDataType()"; } @@ -8504,6 +8505,7 @@ public class JavaGenerator extends AbstractGenerator { type.isNullable(), type.isIdentity(), type.isReadonly(), + type.getGeneratedAlwaysAs(), type.getDefaultValue(), type.getQualifiedUserType() ); @@ -8749,7 +8751,21 @@ public class JavaGenerator extends AbstractGenerator { return type; } - protected String getTypeReference(Database db, SchemaDefinition schema, JavaWriter out, String t, int p, int s, int l, boolean n, boolean i, boolean r, String d, Name u) { + protected String getTypeReference( + Database db, + SchemaDefinition schema, + JavaWriter out, + String t, + int p, + int s, + int l, + boolean n, + boolean i, + boolean r, + String g, + String d, + Name u + ) { StringBuilder sb = new StringBuilder(); if (db.getArray(schema, u) != null) { @@ -8808,6 +8824,8 @@ public class JavaGenerator extends AbstractGenerator { + + if (d != null) dataType = dataType.defaultValue((Field) DSL.field(d, dataType)); @@ -8863,6 +8881,18 @@ public class JavaGenerator extends AbstractGenerator { + + + + + + + + + + + + // [#5291] Some dialects report valid SQL expresions (e.g. PostgreSQL), others // report actual values (e.g. MySQL). if (dataType.defaulted()) { diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractElementContainerDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractElementContainerDefinition.java index 3138963497..dae7d22219 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/AbstractElementContainerDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/AbstractElementContainerDefinition.java @@ -50,7 +50,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; // ... -import org.jooq.meta.jaxb.SyntheticReadonlyColumnType; import org.jooq.meta.jaxb.SyntheticReadonlyRowidType; import org.jooq.tools.JooqLogger; import org.jooq.tools.StringUtils; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/DataTypeDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/DataTypeDefinition.java index 5d51d6b72b..ab0ac4a377 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/DataTypeDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/DataTypeDefinition.java @@ -115,6 +115,16 @@ public interface DataTypeDefinition { */ boolean isReadonly(); + /** + * Whether this data type is computed. + */ + boolean isComputed(); + + /** + * The computed column expression. + */ + String getGeneratedAlwaysAs(); + /** * Whether this data type is an identity. */ diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/DefaultDataTypeDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultDataTypeDefinition.java index 569ba139dd..a32350f81f 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/DefaultDataTypeDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/DefaultDataTypeDefinition.java @@ -73,6 +73,7 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition { private final String binding; private final boolean nullable; private boolean readonly; + private String generatedAlwaysAs; private boolean identity; private final String defaultValue; private final int length; @@ -188,10 +189,10 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition { } public DefaultDataTypeDefinition(Database database, SchemaDefinition schema, String typeName, Number length, Number precision, Number scale, Boolean nullable, String defaultValue, boolean isIdentity, Name userType, String converter, String binding, String javaType) { - this(database, schema, typeName, length, precision, scale, nullable, false, defaultValue, isIdentity, userType, converter, binding, javaType); + this(database, schema, typeName, length, precision, scale, nullable, false, null, defaultValue, isIdentity, userType, converter, binding, javaType); } - public DefaultDataTypeDefinition(Database database, SchemaDefinition schema, String typeName, Number length, Number precision, Number scale, Boolean nullable, boolean readonly, String defaultValue, boolean identity, Name userType, String converter, String binding, String javaType) { + public DefaultDataTypeDefinition(Database database, SchemaDefinition schema, String typeName, Number length, Number precision, Number scale, Boolean nullable, boolean readonly, String generatedAlwaysAs, String defaultValue, boolean identity, Name userType, String converter, String binding, String javaType) { this.database = database; this.schema = schema; @@ -221,6 +222,7 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition { this.scale = scale == null ? 0 : scale.intValue(); this.nullable = nullable == null ? true : nullable.booleanValue(); this.readonly = readonly; + this.generatedAlwaysAs = generatedAlwaysAs; this.defaultValue = defaultValue; this.identity = identity; } @@ -254,6 +256,21 @@ public class DefaultDataTypeDefinition implements DataTypeDefinition { return readonly; } + @Override + public final boolean isComputed() { + return getGeneratedAlwaysAs() != null; + } + + @Override + public final String getGeneratedAlwaysAs() { + return generatedAlwaysAs; + } + + public final DefaultDataTypeDefinition generatedAlwaysAs(String g) { + this.generatedAlwaysAs = g; + return this; + } + public final DefaultDataTypeDefinition identity(boolean i) { this.identity = i; return this; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2TableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2TableDefinition.java index d8ca738d81..2140e7b955 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2TableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/h2/H2TableDefinition.java @@ -109,6 +109,7 @@ public class H2TableDefinition extends AbstractTableDefinition { COLUMNS.NUMERIC_PRECISION.decode(maxP, zero(), COLUMNS.NUMERIC_PRECISION).as(COLUMNS.NUMERIC_PRECISION), COLUMNS.NUMERIC_SCALE.decode(maxS, zero(), COLUMNS.NUMERIC_SCALE).as(COLUMNS.NUMERIC_SCALE), COLUMNS.IS_NULLABLE, + COLUMNS.IS_COMPUTED, COLUMNS.COLUMN_DEFAULT, COLUMNS.REMARKS, COLUMNS.SEQUENCE_NAME, @@ -132,6 +133,7 @@ public class H2TableDefinition extends AbstractTableDefinition { null != record.get(COLUMNS.SEQUENCE_NAME) || defaultString(record.get(COLUMNS.COLUMN_DEFAULT)).trim().toLowerCase().startsWith("nextval"); + boolean isComputed = record.get(COLUMNS.IS_COMPUTED, boolean.class); // [#7644] H2 puts DATETIME_PRECISION in NUMERIC_SCALE column boolean isTimestamp = record.get(COLUMNS.TYPE_NAME).trim().toLowerCase().startsWith("timestamp"); @@ -153,9 +155,9 @@ public class H2TableDefinition extends AbstractTableDefinition { ? 0 : record.get(COLUMNS.NUMERIC_SCALE), record.get(COLUMNS.IS_NULLABLE, boolean.class), - isIdentity ? null : record.get(COLUMNS.COLUMN_DEFAULT), + isIdentity || isComputed ? null : record.get(COLUMNS.COLUMN_DEFAULT), userType - ); + ).generatedAlwaysAs(isComputed ? record.get(COLUMNS.COLUMN_DEFAULT) : null); result.add(new DefaultColumnDefinition( getDatabase().getTable(getSchema(), getName()), diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableDefinition.java b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableDefinition.java index 37e8468f37..6b8fe380f2 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableDefinition.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/postgres/PostgresTableDefinition.java @@ -51,7 +51,6 @@ import static org.jooq.meta.postgres.pg_catalog.Tables.PG_ATTRIBUTE; import static org.jooq.meta.postgres.pg_catalog.Tables.PG_CLASS; import static org.jooq.meta.postgres.pg_catalog.Tables.PG_DESCRIPTION; import static org.jooq.meta.postgres.pg_catalog.Tables.PG_NAMESPACE; -import static org.jooq.util.postgres.PostgresDSL.oid; import java.sql.SQLException; import java.util.ArrayList; @@ -124,6 +123,7 @@ public class PostgresTableDefinition extends AbstractTableDefinition { COLUMNS.NUMERIC_SCALE, (when(isIdentity, inline("YES"))).as(COLUMNS.IS_IDENTITY), COLUMNS.IS_NULLABLE, + COLUMNS.GENERATION_EXPRESSION, (when(isIdentity, inline(null, String.class)).else_(COLUMNS.COLUMN_DEFAULT)).as(COLUMNS.COLUMN_DEFAULT), coalesce(COLUMNS.DOMAIN_SCHEMA, udtSchema).as(COLUMNS.UDT_SCHEMA), coalesce(COLUMNS.DOMAIN_NAME, COLUMNS.UDT_NAME).as(COLUMNS.UDT_NAME), @@ -166,7 +166,7 @@ public class PostgresTableDefinition extends AbstractTableDefinition { record.get(COLUMNS.UDT_SCHEMA), record.get(COLUMNS.UDT_NAME) ) - ); + ).generatedAlwaysAs(record.get(COLUMNS.GENERATION_EXPRESSION)); ColumnDefinition column = new DefaultColumnDefinition( getDatabase().getTable(getSchema(), getName()), diff --git a/jOOQ/src/main/java/org/jooq/DataType.java b/jOOQ/src/main/java/org/jooq/DataType.java index 79c4808810..cf3dd1d1ab 100644 --- a/jOOQ/src/main/java/org/jooq/DataType.java +++ b/jOOQ/src/main/java/org/jooq/DataType.java @@ -443,6 +443,44 @@ public interface DataType extends Named { */ boolean readonly(); + /** + * Whether this column is computed. + *

+ * This feature is implemented in commercial distributions only. + */ + boolean computed(); + + /** + * Set the computed column expression of this data type. + *

+ * This implicitly sets {@link #readonly()} to true. + *

+ * This feature is implemented in commercial distributions only. + */ + @NotNull + @Support({ POSTGRES }) + DataType generatedAlwaysAs(T generatedAlwaysAsValue); + + /** + * Set the computed column expression of this data type. + *

+ * This implicitly sets {@link #readonly()} to true. + *

+ * This feature is implemented in commercial distributions only. + */ + @NotNull + @Support({ POSTGRES }) + DataType generatedAlwaysAs(Field generatedAlwaysAsValue); + + /** + * Get the computed column expression of this data type, if any. + *

+ * This feature is implemented in commercial distributions only. + */ + @Nullable + @Support({ POSTGRES }) + Field generatedAlwaysAs(); + /** * Synonym for {@link #nullable(boolean)}, passing true as an * argument. diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java index 56303a2031..e24cdbe59c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDataType.java @@ -145,6 +145,22 @@ implements @Override public abstract DataType readonly(boolean r); + @Override + public final boolean computed() { + return generatedAlwaysAs() != null; + } + + @Override + public final DataType generatedAlwaysAs(T g) { + return generatedAlwaysAs(Tools.field(g, this)); + } + + @Override + public abstract DataType generatedAlwaysAs(Field generatedAlwaysAsValue); + + @Override + public abstract Field generatedAlwaysAs(); + @Override public abstract DataType collation(Collation c); diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractDataTypeX.java b/jOOQ/src/main/java/org/jooq/impl/AbstractDataTypeX.java index 64a4eb19fe..9458afb392 100644 --- a/jOOQ/src/main/java/org/jooq/impl/AbstractDataTypeX.java +++ b/jOOQ/src/main/java/org/jooq/impl/AbstractDataTypeX.java @@ -47,8 +47,6 @@ import org.jooq.Field; import org.jooq.Name; import org.jooq.Nullability; -import org.jetbrains.annotations.NotNull; - /** * @author Lukas Eder */ @@ -67,6 +65,7 @@ abstract class AbstractDataTypeX extends AbstractDataType { Integer newLength, Nullability newNullability, boolean newReadonly, + Field newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, @@ -75,46 +74,161 @@ abstract class AbstractDataTypeX extends AbstractDataType { @Override public final DataType nullability(Nullability n) { - return construct(precision0(), scale0(), length0(), n, readonly(), collation(), characterSet(), !n.nullable() && identity(), defaultValue()); + return construct( + precision0(), + scale0(), + length0(), + n, + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + !n.nullable() && identity(), + defaultValue() + ); } @Override public final DataType readonly(boolean r) { - return construct(precision0(), scale0(), length0(), nullability(), r, collation(), characterSet(), identity(), defaultValue()); + return construct( + precision0(), + scale0(), + length0(), + nullability(), + r, + generatedAlwaysAs(), + collation(), + characterSet(), + identity(), + defaultValue() + ); + } + + @Override + public final DataType generatedAlwaysAs(Field g) { + return construct( + precision0(), + scale0(), + length0(), + nullability(), + readonly(), + g, + collation(), + characterSet(), + identity(), + defaultValue() + ); } @Override public final DataType collation(Collation c) { - return construct(precision0(), scale0(), length0(), nullability(), readonly(), c, characterSet(), identity(), defaultValue()); + return construct( + precision0(), + scale0(), + length0(), + nullability(), + readonly(), + generatedAlwaysAs(), + c, + characterSet(), + identity(), + defaultValue() + ); } @Override public final DataType characterSet(CharacterSet c) { - return construct(precision0(), scale0(), length0(), nullability(), readonly(), collation(), c, identity(), defaultValue()); + return construct( + precision0(), + scale0(), + length0(), + nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + c, + identity(), + defaultValue() + ); } @Override public final DataType identity(boolean i) { - return construct(precision0(), scale0(), length0(), i ? NOT_NULL : nullability(), readonly(), collation(), characterSet(), i, defaultValue()); + return construct( + precision0(), + scale0(), + length0(), + i ? NOT_NULL : nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + i, + defaultValue() + ); } @Override public final DataType default_(Field d) { - return construct(precision0(), scale0(), length0(), nullability(), readonly(), collation(), characterSet(), identity(), d); + return construct( + precision0(), + scale0(), + length0(), + nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + identity(), + d + ); } @Override final AbstractDataTypeX precision1(Integer p, Integer s) { - return construct(p, s, length0(), nullability(), readonly(), collation(), characterSet(), identity(), defaultValue()); + return construct( + p, + s, + length0(), + nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + identity(), + defaultValue() + ); } @Override final AbstractDataTypeX scale1(Integer s) { - return construct(precision0(), s, length0(), nullability(), readonly(), collation(), characterSet(), identity(), defaultValue()); + return construct( + precision0(), + s, + length0(), + nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + identity(), + defaultValue() + ); } @Override final AbstractDataTypeX length1(Integer l) { - return construct(precision0(), scale0(), l, nullability(), readonly(), collation(), characterSet(), identity(), defaultValue()); + return construct( + precision0(), + scale0(), + l, + nullability(), + readonly(), + generatedAlwaysAs(), + collation(), + characterSet(), + identity(), + defaultValue() + ); } } diff --git a/jOOQ/src/main/java/org/jooq/impl/ArrayDataType.java b/jOOQ/src/main/java/org/jooq/impl/ArrayDataType.java index f080d60437..80c49213b4 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ArrayDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ArrayDataType.java @@ -70,12 +70,13 @@ final class ArrayDataType extends DefaultDataType { Integer length, Nullability nullability, boolean readonly, + Field generatedAlwaysAs, Collation collation, CharacterSet characterSet, boolean identity, Field defaultValue ) { - super(t, precision, scale, length, nullability, readonly, collation, characterSet, identity, defaultValue); + super(t, precision, scale, length, nullability, readonly, generatedAlwaysAs, collation, characterSet, identity, defaultValue); this.elementType = elementType; } @@ -88,6 +89,7 @@ final class ArrayDataType extends DefaultDataType { Integer newLength, Nullability newNullability, boolean newReadonly, + Field newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, @@ -101,6 +103,7 @@ final class ArrayDataType extends DefaultDataType { newLength, newNullability, newReadonly, + newGeneratedAlwaysAs, newCollation, newCharacterSet, newIdentity, diff --git a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java index d9ce963f18..bb1fe4e16c 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/ConvertedDataType.java @@ -82,6 +82,7 @@ final class ConvertedDataType extends AbstractDataTypeX { Integer newLength, Nullability newNullability, boolean newReadonly, + Field newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, @@ -93,6 +94,7 @@ final class ConvertedDataType extends AbstractDataTypeX { newLength, newNullability, newReadonly, + (Field) newGeneratedAlwaysAs, newCollation, newCharacterSet, newIdentity, @@ -155,6 +157,11 @@ final class ConvertedDataType extends AbstractDataTypeX { return delegate.readonly(); } + @Override + public final Field generatedAlwaysAs() { + return (Field) delegate.generatedAlwaysAs(); + } + @Override public final Collation collation() { return delegate.collation(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DataTypeProxy.java b/jOOQ/src/main/java/org/jooq/impl/DataTypeProxy.java index 3572bf782d..a026bf9590 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DataTypeProxy.java +++ b/jOOQ/src/main/java/org/jooq/impl/DataTypeProxy.java @@ -63,13 +63,14 @@ final class DataTypeProxy extends AbstractDataType { private final Integer overrideLength; private final Nullability overrideNullability; private final Boolean overrideReadonly; + private final Field overrideGeneratedAlwaysAs; private final Collation overrideCollation; private final CharacterSet overrideCharacterSet; private final Boolean overrideIdentity; private final Field overrideDefaultValue; DataTypeProxy(AbstractDataType type) { - this(type, null, null, null, null, null, null, null, null, null); + this(type, null, null, null, null, null, null, null, null, null, null); } private DataTypeProxy( @@ -79,6 +80,7 @@ final class DataTypeProxy extends AbstractDataType { Integer overrideLength, Nullability overrideNullability, Boolean overrideReadonly, + Field overrideGeneratedAlwaysAs, Collation overrideCollation, CharacterSet overrideCharacterSet, Boolean overrideIdentity, @@ -92,6 +94,7 @@ final class DataTypeProxy extends AbstractDataType { this.overrideLength = overrideLength; this.overrideNullability = overrideNullability; this.overrideReadonly = overrideReadonly; + this.overrideGeneratedAlwaysAs = overrideGeneratedAlwaysAs; this.overrideCollation = overrideCollation; this.overrideCharacterSet = overrideCharacterSet; this.overrideIdentity = overrideIdentity; @@ -150,6 +153,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, n, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, overrideIdentity, @@ -171,6 +175,29 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, r, + overrideGeneratedAlwaysAs, + overrideCollation, + overrideCharacterSet, + overrideIdentity, + overrideDefaultValue + ); + } + + @Override + public final Field generatedAlwaysAs() { + return defaultIfNull(overrideGeneratedAlwaysAs, type.generatedAlwaysAs()); + } + + @Override + public final DataType generatedAlwaysAs(Field g) { + return new DataTypeProxy<>( + this, + overridePrecision, + overrideScale, + overrideLength, + overrideNullability, + overrideReadonly, + g, overrideCollation, overrideCharacterSet, overrideIdentity, @@ -192,6 +219,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, c, overrideCharacterSet, overrideIdentity, @@ -213,6 +241,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, c, overrideIdentity, @@ -234,6 +263,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, i, @@ -255,6 +285,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, overrideIdentity, @@ -306,6 +337,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, overrideIdentity, @@ -327,6 +359,7 @@ final class DataTypeProxy extends AbstractDataType { overrideLength, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, overrideIdentity, @@ -348,6 +381,7 @@ final class DataTypeProxy extends AbstractDataType { l, overrideNullability, overrideReadonly, + overrideGeneratedAlwaysAs, overrideCollation, overrideCharacterSet, overrideIdentity, diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java index 6167acf07a..9369bf3a4b 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDataType.java @@ -51,9 +51,37 @@ import static org.jooq.impl.CommentImpl.NO_COMMENT; import static org.jooq.impl.DSL.unquotedName; import static org.jooq.impl.DefaultBinding.binding; import static org.jooq.impl.SQLDataType.BIGINT; -import static org.jooq.impl.SQLDataType.*; +import static org.jooq.impl.SQLDataType.BINARY; import static org.jooq.impl.SQLDataType.BIT; +import static org.jooq.impl.SQLDataType.BLOB; +import static org.jooq.impl.SQLDataType.BOOLEAN; +import static org.jooq.impl.SQLDataType.CHAR; +import static org.jooq.impl.SQLDataType.CLOB; +import static org.jooq.impl.SQLDataType.DATE; +import static org.jooq.impl.SQLDataType.DECIMAL; +import static org.jooq.impl.SQLDataType.DOUBLE; +import static org.jooq.impl.SQLDataType.FLOAT; +import static org.jooq.impl.SQLDataType.INTEGER; +import static org.jooq.impl.SQLDataType.LONGNVARCHAR; +import static org.jooq.impl.SQLDataType.LONGVARBINARY; +import static org.jooq.impl.SQLDataType.LONGVARCHAR; +import static org.jooq.impl.SQLDataType.NCHAR; +import static org.jooq.impl.SQLDataType.NCLOB; +import static org.jooq.impl.SQLDataType.NUMERIC; +import static org.jooq.impl.SQLDataType.NVARCHAR; import static org.jooq.impl.SQLDataType.OTHER; +import static org.jooq.impl.SQLDataType.REAL; +import static org.jooq.impl.SQLDataType.RECORD; +import static org.jooq.impl.SQLDataType.RESULT; +import static org.jooq.impl.SQLDataType.SMALLINT; +import static org.jooq.impl.SQLDataType.TIME; +import static org.jooq.impl.SQLDataType.TIMESTAMP; +import static org.jooq.impl.SQLDataType.TIMESTAMPWITHTIMEZONE; +import static org.jooq.impl.SQLDataType.TIMEWITHTIMEZONE; +import static org.jooq.impl.SQLDataType.TINYINT; +import static org.jooq.impl.SQLDataType.VARBINARY; +import static org.jooq.impl.SQLDataType.VARCHAR; +import static org.jooq.impl.SQLDataType.XML; import static org.jooq.tools.reflect.Reflect.wrapper; import java.math.BigDecimal; @@ -221,6 +249,7 @@ public class DefaultDataType extends AbstractDataTypeX { private final Nullability nullability; private final boolean readonly; + private final Field generatedAlwaysAs; private final Collation collation; private final CharacterSet characterSet; private final boolean identity; @@ -290,10 +319,10 @@ public class DefaultDataType extends AbstractDataTypeX { } DefaultDataType(SQLDialect dialect, DataType sqlDataType, Class type, Binding binding, Name qualifiedTypeName, String typeName, String castTypeName, Integer precision, Integer scale, Integer length, Nullability nullability, Field defaultValue) { - this(dialect, sqlDataType, type, binding, qualifiedTypeName, typeName, castTypeName, precision, scale, length, nullability, false, null, null, false, defaultValue); + this(dialect, sqlDataType, type, binding, qualifiedTypeName, typeName, castTypeName, precision, scale, length, nullability, false, null, null, null, false, defaultValue); } - DefaultDataType(SQLDialect dialect, DataType sqlDataType, Class type, Binding binding, Name qualifiedTypeName, String typeName, String castTypeName, Integer precision, Integer scale, Integer length, Nullability nullability, boolean readonly, Collation collation, CharacterSet characterSet, boolean identity, Field defaultValue) { + DefaultDataType(SQLDialect dialect, DataType sqlDataType, Class type, Binding binding, Name qualifiedTypeName, String typeName, String castTypeName, Integer precision, Integer scale, Integer length, Nullability nullability, boolean readonly, Field generatedAlwaysAs, Collation collation, CharacterSet characterSet, boolean identity, Field defaultValue) { super(qualifiedTypeName, NO_COMMENT); // Initialise final instance members @@ -313,6 +342,7 @@ public class DefaultDataType extends AbstractDataTypeX { this.nullability = nullability; this.readonly = readonly; + this.generatedAlwaysAs = generatedAlwaysAs; this.collation = collation; this.characterSet = characterSet; this.identity = identity; @@ -353,12 +383,25 @@ public class DefaultDataType extends AbstractDataTypeX { Integer newLength, Nullability newNullability, boolean newReadonly, + Field newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, Field newDefaultValue ) { - return new DefaultDataType<>(this, newPrecision, newScale, newLength, newNullability, newReadonly, newCollation, newCharacterSet, newIdentity, newDefaultValue); + return new DefaultDataType<>( + this, + newPrecision, + newScale, + newLength, + newNullability, + newReadonly, + newGeneratedAlwaysAs, + newCollation, + newCharacterSet, + newIdentity, + newDefaultValue + ); } /** @@ -371,6 +414,7 @@ public class DefaultDataType extends AbstractDataTypeX { Integer length, Nullability nullability, boolean readonly, + Field generatedAlwaysAs, Collation collation, CharacterSet characterSet, boolean identity, @@ -389,6 +433,7 @@ public class DefaultDataType extends AbstractDataTypeX { this.nullability = nullability; this.readonly = readonly; + this.generatedAlwaysAs = generatedAlwaysAs; this.collation = collation; this.characterSet = characterSet; this.identity = identity; @@ -428,6 +473,11 @@ public class DefaultDataType extends AbstractDataTypeX { return readonly; } + @Override + public final Field generatedAlwaysAs() { + return generatedAlwaysAs; + } + @Override public final Collation collation() { return collation; @@ -484,7 +534,7 @@ public class DefaultDataType extends AbstractDataTypeX { // ... and then, set them back to the original value // [#2710] TODO: Remove this logic along with cached data types - return dataType.construct(precision, scale, length, nullability, readonly, collation, characterSet, identity, defaultValue); + return dataType.construct(precision, scale, length, nullability, readonly, generatedAlwaysAs, collation, characterSet, identity, defaultValue); } // If this is already the dialect's specific data type, return this diff --git a/jOOQ/src/main/java/org/jooq/impl/DomainDataType.java b/jOOQ/src/main/java/org/jooq/impl/DomainDataType.java index 057dc63879..ec0b9de671 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DomainDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/DomainDataType.java @@ -65,6 +65,7 @@ final class DomainDataType extends DefaultDataType { baseType.lengthDefined() ? baseType.length() : null, baseType.nullability(), baseType.readonly(), + baseType.generatedAlwaysAs(), null, // TODO: Collation null, // TODO: CharacterSet (?) false, diff --git a/jOOQ/src/main/java/org/jooq/impl/Keywords.java b/jOOQ/src/main/java/org/jooq/impl/Keywords.java index f3ccefc010..5e6a49c630 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Keywords.java +++ b/jOOQ/src/main/java/org/jooq/impl/Keywords.java @@ -60,6 +60,7 @@ final class Keywords { static final Keyword K_ALTER_INDEX = keyword("alter index"); static final Keyword K_ALTER_SCHEMA = keyword("alter schema"); static final Keyword K_ALTER_TABLE = keyword("alter table"); + static final Keyword K_ALWAYS = keyword("always"); static final Keyword K_AND = keyword("and"); static final Keyword K_ARRAY = keyword("array"); static final Keyword K_AS = keyword("as"); @@ -184,7 +185,7 @@ final class Keywords { static final Keyword K_FOR_PORTION_OF = keyword("for portion of"); static final Keyword K_FROM = keyword("from"); static final Keyword K_FUNCTION = keyword("function"); - static final Keyword K_GENERATED_BY_DEFAULT_AS_IDENTITY = keyword("generated by default as identity"); + static final Keyword K_GENERATED = keyword("generated"); static final Keyword K_GLOBAL_TEMPORARY = keyword("global temporary"); static final Keyword K_GOTO = keyword("goto"); static final Keyword K_GRANT = keyword("grant"); diff --git a/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java b/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java index a8d9930559..a910dd6fa9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/MultisetDataType.java @@ -85,12 +85,13 @@ final class MultisetDataType extends DefaultDataType Integer length, Nullability nullability, boolean readonly, + Field> generatedAlwaysAs, Collation collation, CharacterSet characterSet, boolean identity, Field> defaultValue ) { - super(t, precision, scale, length, nullability, readonly, collation, characterSet, identity, defaultValue); + super(t, precision, scale, length, nullability, readonly, generatedAlwaysAs, collation, characterSet, identity, defaultValue); this.row = row; this.recordType = recordType; @@ -104,6 +105,7 @@ final class MultisetDataType extends DefaultDataType Integer newLength, Nullability newNullability, boolean newReadonly, + Field> newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, @@ -118,6 +120,7 @@ final class MultisetDataType extends DefaultDataType newLength, newNullability, newReadonly, + newGeneratedAlwaysAs, newCollation, newCharacterSet, newIdentity, diff --git a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java index 6348fbbab6..1e928246da 100644 --- a/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/ParserImpl.java @@ -4225,6 +4225,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { List indexes = new ArrayList<>(); boolean primary = false; boolean identity = false; + boolean computed = false; boolean readonly = false; // Three valued boolean: @@ -4308,11 +4309,13 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { constraints, primary, identity, + computed, readonly ); primary = inlineConstraints.primary; identity = inlineConstraints.identity; + computed = inlineConstraints.computed; if (ctas) fields.add(field(fieldName)); @@ -4520,7 +4523,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { return storageStep; } - private static final /* record */ class ParseInlineConstraints { private final DataType type; private final Comment fieldComment; private final boolean primary; private final boolean identity; private final boolean readonly; public ParseInlineConstraints(DataType type, Comment fieldComment, boolean primary, boolean identity, boolean readonly) { this.type = type; this.fieldComment = fieldComment; this.primary = primary; this.identity = identity; this.readonly = readonly; } public DataType type() { return type; } public Comment fieldComment() { return fieldComment; } public boolean primary() { return primary; } public boolean identity() { return identity; } public boolean readonly() { return readonly; } @Override public boolean equals(Object o) { if (!(o instanceof ParseInlineConstraints)) return false; ParseInlineConstraints other = (ParseInlineConstraints) o; if (!java.util.Objects.equals(this.type, other.type)) return false; if (!java.util.Objects.equals(this.fieldComment, other.fieldComment)) return false; if (!java.util.Objects.equals(this.primary, other.primary)) return false; if (!java.util.Objects.equals(this.identity, other.identity)) return false; if (!java.util.Objects.equals(this.readonly, other.readonly)) return false; return true; } @Override public int hashCode() { return java.util.Objects.hash(this.type, this.fieldComment, this.primary, this.identity, this.readonly); } @Override public String toString() { return new StringBuilder("ParseInlineConstraints[").append("type=").append(this.type).append(", fieldComment=").append(this.fieldComment).append(", primary=").append(this.primary).append(", identity=").append(this.identity).append(", readonly=").append(this.readonly).append("]").toString(); } } + private static final /* record */ class ParseInlineConstraints { private final DataType type; private final Comment fieldComment; private final boolean primary; private final boolean identity; private final boolean computed; private final boolean readonly; public ParseInlineConstraints(DataType type, Comment fieldComment, boolean primary, boolean identity, boolean computed, boolean readonly) { this.type = type; this.fieldComment = fieldComment; this.primary = primary; this.identity = identity; this.computed = computed; this.readonly = readonly; } public DataType type() { return type; } public Comment fieldComment() { return fieldComment; } public boolean primary() { return primary; } public boolean identity() { return identity; } public boolean computed() { return computed; } public boolean readonly() { return readonly; } @Override public boolean equals(Object o) { if (!(o instanceof ParseInlineConstraints)) return false; ParseInlineConstraints other = (ParseInlineConstraints) o; if (!java.util.Objects.equals(this.type, other.type)) return false; if (!java.util.Objects.equals(this.fieldComment, other.fieldComment)) return false; if (!java.util.Objects.equals(this.primary, other.primary)) return false; if (!java.util.Objects.equals(this.identity, other.identity)) return false; if (!java.util.Objects.equals(this.computed, other.computed)) return false; if (!java.util.Objects.equals(this.readonly, other.readonly)) return false; return true; } @Override public int hashCode() { return java.util.Objects.hash(this.type, this.fieldComment, this.primary, this.identity, this.computed, this.readonly); } @Override public String toString() { return new StringBuilder("ParseInlineConstraints[").append("type=").append(this.type).append(", fieldComment=").append(this.fieldComment).append(", primary=").append(this.primary).append(", identity=").append(this.identity).append(", computed=").append(this.computed).append(", readonly=").append(this.readonly).append("]").toString(); } } private final ParseInlineConstraints parseInlineConstraints( Name fieldName, @@ -4528,6 +4531,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { List constraints, boolean primary, boolean identity, + boolean computed, boolean readonly ) { boolean nullable = false; @@ -4538,6 +4542,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { Comment fieldComment = null; identity |= type.identity(); + computed |= type.computed(); readonly |= type.readonly(); for (;;) { @@ -4595,65 +4600,36 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { continue; } - else if (!identity && parseKeywordIf("GENERATED")) { - if (!parseKeywordIf("ALWAYS")) { + else if (!computed && parseKeywordIf("AS") && requireProEdition()) { + + + + + + + } + else if (!identity && !computed && parseKeywordIf("GENERATED")) { + boolean always; + if (!(always = parseKeywordIf("ALWAYS"))) { parseKeyword("BY DEFAULT"); // TODO: Ignored keyword from Oracle parseKeywordIf("ON NULL"); } - parseKeyword("AS IDENTITY"); + if (always ? parseKeywordIf("AS IDENTITY") : parseKeyword("AS IDENTITY")) { + parseIdentityOptionIf(); + type = type.identity(true); + identity = true; + } + else if (parseKeyword("AS") && requireProEdition()) { - // TODO: Ignored identity options from Oracle - if (parseIf('(')) { - boolean identityOption = false; - for (;;) { - if (identityOption) - parseIf(','); - if (parseKeywordIf("START WITH")) { - if (!parseKeywordIf("LIMIT VALUE")) - parseUnsignedIntegerOrBindVariable(); - identityOption = true; - continue; - } - else if (parseKeywordIf("INCREMENT BY") - || parseKeywordIf("MAXVALUE") - || parseKeywordIf("MINVALUE") - || parseKeywordIf("CACHE")) { - parseUnsignedIntegerOrBindVariable(); - identityOption = true; - continue; - } - else if (parseKeywordIf("NOMAXVALUE") - || parseKeywordIf("NOMINVALUE") - || parseKeywordIf("CYCLE") - || parseKeywordIf("NOCYCLE") - || parseKeywordIf("NOCACHE") - || parseKeywordIf("ORDER") - || parseKeywordIf("NOORDER")) { - identityOption = true; - continue; - } - else if (parseSignedIntegerLiteralIf() != null) { - identityOption = true; - continue; - } - if (identityOption) - break; - else - throw unsupportedClause(); - } - - parse(')'); } - type = type.identity(true); defaultValue = true; - identity = true; continue; } } @@ -4749,7 +4725,56 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { break; } - return new ParseInlineConstraints(type, fieldComment, primary, identity, readonly); + return new ParseInlineConstraints(type, fieldComment, primary, identity, computed, readonly); + } + + private final void parseIdentityOptionIf() { + + // TODO: Ignored identity options from Oracle + if (parseIf('(')) { + boolean identityOption = false; + + for (;;) { + if (identityOption) + parseIf(','); + + if (parseKeywordIf("START WITH")) { + if (!parseKeywordIf("LIMIT VALUE")) + parseUnsignedIntegerOrBindVariable(); + identityOption = true; + continue; + } + else if (parseKeywordIf("INCREMENT BY") + || parseKeywordIf("MAXVALUE") + || parseKeywordIf("MINVALUE") + || parseKeywordIf("CACHE")) { + parseUnsignedIntegerOrBindVariable(); + identityOption = true; + continue; + } + else if (parseKeywordIf("NOMAXVALUE") + || parseKeywordIf("NOMINVALUE") + || parseKeywordIf("CYCLE") + || parseKeywordIf("NOCYCLE") + || parseKeywordIf("NOCACHE") + || parseKeywordIf("ORDER") + || parseKeywordIf("NOORDER")) { + identityOption = true; + continue; + } + else if (parseSignedIntegerLiteralIf() != null) { + identityOption = true; + continue; + } + + if (identityOption) + break; + else + throw unsupportedClause(); + } + + parse(')'); + } } private final boolean parseSerialIf() { @@ -5227,7 +5252,7 @@ final class DefaultParseContext extends AbstractScope implements ParseContext { DataType type = parseDataType(); int p = list == null ? -1 : list.size(); - ParseInlineConstraints inline = parseInlineConstraints(fieldName, type, list, false, false, false); + ParseInlineConstraints inline = parseInlineConstraints(fieldName, type, list, false, false, false, false); Field result = field(fieldName, inline.type, inline.fieldComment); if (list != null) diff --git a/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java b/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java index 7279d81d99..2d890d3501 100644 --- a/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java +++ b/jOOQ/src/main/java/org/jooq/impl/RecordDataType.java @@ -86,12 +86,13 @@ final class RecordDataType extends DefaultDataType { Integer length, Nullability nullability, boolean readonly, + Field generatedAlwaysAs, Collation collation, CharacterSet characterSet, boolean identity, Field defaultValue ) { - super(t, precision, scale, length, nullability, readonly, collation, characterSet, identity, defaultValue); + super(t, precision, scale, length, nullability, readonly, generatedAlwaysAs, collation, characterSet, identity, defaultValue); this.row = row; } @@ -104,6 +105,7 @@ final class RecordDataType extends DefaultDataType { Integer newLength, Nullability newNullability, boolean newReadonly, + Field newGeneratedAlwaysAs, Collation newCollation, CharacterSet newCharacterSet, boolean newIdentity, @@ -117,6 +119,7 @@ final class RecordDataType extends DefaultDataType { newLength, newNullability, newReadonly, + newGeneratedAlwaysAs, newCollation, newCharacterSet, newIdentity, diff --git a/jOOQ/src/main/java/org/jooq/impl/Tools.java b/jOOQ/src/main/java/org/jooq/impl/Tools.java index a0c30a253c..97a31cffde 100644 --- a/jOOQ/src/main/java/org/jooq/impl/Tools.java +++ b/jOOQ/src/main/java/org/jooq/impl/Tools.java @@ -53,7 +53,7 @@ import static java.util.stream.Collectors.joining; import static org.jooq.SQLDialect.DERBY; // ... import static org.jooq.SQLDialect.FIREBIRD; -import static org.jooq.SQLDialect.HSQLDB; +import static org.jooq.SQLDialect.*; // ... // ... import static org.jooq.SQLDialect.MARIADB; @@ -120,6 +120,7 @@ import static org.jooq.impl.Identifiers.QUOTES; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER; import static org.jooq.impl.Identifiers.QUOTE_END_DELIMITER_ESCAPED; import static org.jooq.impl.Identifiers.QUOTE_START_DELIMITER; +import static org.jooq.impl.Keywords.K_ALWAYS; import static org.jooq.impl.Keywords.K_AS; import static org.jooq.impl.Keywords.K_ATOMIC; import static org.jooq.impl.Keywords.K_AUTOINCREMENT; @@ -127,6 +128,7 @@ import static org.jooq.impl.Keywords.K_AUTO_INCREMENT; import static org.jooq.impl.Keywords.K_BEGIN; import static org.jooq.impl.Keywords.K_BEGIN_CATCH; import static org.jooq.impl.Keywords.K_BEGIN_TRY; +import static org.jooq.impl.Keywords.K_BY; import static org.jooq.impl.Keywords.K_CHARACTER_SET; import static org.jooq.impl.Keywords.K_COLLATE; import static org.jooq.impl.Keywords.K_DECLARE; @@ -145,7 +147,7 @@ import static org.jooq.impl.Keywords.K_EXEC; import static org.jooq.impl.Keywords.K_EXECUTE_BLOCK; import static org.jooq.impl.Keywords.K_EXECUTE_IMMEDIATE; import static org.jooq.impl.Keywords.K_EXECUTE_STATEMENT; -import static org.jooq.impl.Keywords.K_GENERATED_BY_DEFAULT_AS_IDENTITY; +import static org.jooq.impl.Keywords.K_GENERATED; import static org.jooq.impl.Keywords.K_IDENTITY; import static org.jooq.impl.Keywords.K_IF; import static org.jooq.impl.Keywords.K_INT; @@ -158,7 +160,6 @@ import static org.jooq.impl.Keywords.K_PRIMARY_KEY; import static org.jooq.impl.Keywords.K_RAISE; import static org.jooq.impl.Keywords.K_RAISERROR; import static org.jooq.impl.Keywords.K_SERIAL; -import static org.jooq.impl.Keywords.K_SERIAL2; import static org.jooq.impl.Keywords.K_SERIAL4; import static org.jooq.impl.Keywords.K_SERIAL8; import static org.jooq.impl.Keywords.K_SQLSTATE; @@ -5096,6 +5097,9 @@ final class Tools { * keywords before the [ NOT ] NULL constraint. */ static final void toSQLDDLTypeDeclarationIdentityBeforeNull(Context ctx, DataType type) { + if (REQUIRE_IDENTITY_AFTER_NULL.contains(ctx.dialect())) + return; + if (type.identity()) { switch (ctx.family()) { @@ -5109,7 +5113,7 @@ final class Tools { case CUBRID: ctx.sql(' ').visit(K_AUTO_INCREMENT); break; - case HSQLDB: ctx.sql(' ').visit(K_GENERATED_BY_DEFAULT_AS_IDENTITY).sql('(').visit(K_START_WITH).sql(" 1)"); break; + case HSQLDB: ctx.sql(' ').visit(K_GENERATED).sql(' ').visit(K_BY).sql(' ').visit(K_DEFAULT).sql(' ').visit(K_AS).sql(' ').visit(K_IDENTITY).sql('(').visit(K_START_WITH).sql(" 1)"); break; case SQLITE: ctx.sql(' ').visit(K_PRIMARY_KEY).sql(' ').visit(K_AUTOINCREMENT); break; case POSTGRES: switch (ctx.dialect()) { @@ -5119,23 +5123,38 @@ final class Tools { case POSTGRES: - ctx.sql(' ').visit(K_GENERATED_BY_DEFAULT_AS_IDENTITY); break; + ctx.sql(' ').visit(K_GENERATED).sql(' ').visit(K_BY).sql(' ').visit(K_DEFAULT).sql(' ').visit(K_AS).sql(' ').visit(K_IDENTITY); break; } break; case DERBY: case FIREBIRD: - case YUGABYTE: ctx.sql(' ').visit(K_GENERATED_BY_DEFAULT_AS_IDENTITY); break; + case YUGABYTE: ctx.sql(' ').visit(K_GENERATED).sql(' ').visit(K_BY).sql(' ').visit(K_DEFAULT).sql(' ').visit(K_AS).sql(' ').visit(K_IDENTITY); break; } } + + + + + + + + + + } + private static final Set REQUIRE_IDENTITY_AFTER_NULL = SQLDialect.supportedBy(H2, MARIADB, MYSQL); + /** * If a type is an identity type, some dialects require the relevant * keywords after the [ NOT ] NULL constraint. */ static final void toSQLDDLTypeDeclarationIdentityAfterNull(Context ctx, DataType type) { + if (!REQUIRE_IDENTITY_AFTER_NULL.contains(ctx.dialect())) + return; + if (type.identity()) { // [#5062] H2's (and others') AUTO_INCREMENT flag is syntactically located *after* NULL flags. @@ -5143,7 +5162,7 @@ final class Tools { - case H2: ctx.sql(' ').visit(K_GENERATED_BY_DEFAULT_AS_IDENTITY); break; + case H2: ctx.sql(' ').visit(K_GENERATED).sql(' ').visit(K_BY).sql(' ').visit(K_DEFAULT).sql(' ').visit(K_AS).sql(' ').visit(K_IDENTITY); break; @@ -5154,6 +5173,20 @@ final class Tools { case MYSQL: ctx.sql(' ').visit(K_AUTO_INCREMENT); break; } } + + + + + + + + + + + + + + } private static final void toSQLDDLTypeDeclarationDefault(Context ctx, DataType type) {