diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java index a85f3f7a6e..954d7c789a 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/SybaseAnywhereDialect.java @@ -28,6 +28,8 @@ import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory; import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.exec.spi.JdbcOperation; +import static org.hibernate.type.SqlTypes.*; + /** * SQL Dialect for Sybase Anywhere * (Tested on ASA 8.x) @@ -35,31 +37,48 @@ import org.hibernate.sql.exec.spi.JdbcOperation; public class SybaseAnywhereDialect extends SybaseDialect { public SybaseAnywhereDialect() { - this( DatabaseVersion.make( 8 ), false ); + this( DatabaseVersion.make( 8 ) ); + } + + public SybaseAnywhereDialect(DatabaseVersion version) { + this(version, null); } public SybaseAnywhereDialect(DialectResolutionInfo info){ - this( - info, - info.getDriverName() != null && info.getDriverName().contains( "jTDS" ) - ); + this( info.makeCopy(), info ); registerKeywords( info ); } - public SybaseAnywhereDialect(DatabaseVersion version, boolean jtdsDriver) { - super( version, jtdsDriver ); + public SybaseAnywhereDialect(DatabaseVersion version, DialectResolutionInfo info) { + super( version, info ); + } - registerColumnType( Types.BIGINT, "bigint" ); - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "time" ); - registerColumnType( Types.TIMESTAMP, "timestamp" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp with time zone" ); + @Override + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case DATE: + return "date"; + case TIME: + return "time"; + case TIMESTAMP: + return "timestamp"; + case TIMESTAMP_WITH_TIMEZONE: + return "timestamp with time zone"; - registerColumnType( Types.VARCHAR, "long varchar)" ); - registerColumnType( Types.NVARCHAR, "long nvarchar)" ); + //these types hold up to 2 GB + case LONGVARCHAR: + return "long varchar"; + case LONGNVARCHAR: + return "long nvarchar"; + case LONGVARBINARY: + return "long binary"; - //note: 'binary' is actually a synonym for 'varbinary' - registerColumnType( Types.VARBINARY, "long binary)" ); + case NCLOB: + return "ntext"; + + default: + return super.columnType(jdbcTypeCode); + } } @Override @@ -150,7 +169,6 @@ public class SybaseAnywhereDialect extends SybaseDialect { } @Override - @SuppressWarnings("deprecation") public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) { return getVersion().isBefore( 10 ) ? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames ) diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java index 0d0d372b31..bd4c2c2bc6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/AbstractTransactSQLDialect.java @@ -10,6 +10,7 @@ import org.hibernate.LockMode; import org.hibernate.LockOptions; import org.hibernate.dialect.function.CastingConcatFunction; import org.hibernate.dialect.function.TransactSQLStrFunction; +import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; import org.hibernate.query.NullOrdering; import org.hibernate.cfg.Environment; import org.hibernate.dialect.function.CommonFunctionFactory; @@ -41,39 +42,53 @@ import java.sql.Types; import java.util.Iterator; import java.util.Map; +import static org.hibernate.type.SqlTypes.*; + /** * An abstract base class for Sybase and MS SQL Server dialects. * * @author Gavin King */ public abstract class AbstractTransactSQLDialect extends Dialect { - public AbstractTransactSQLDialect() { - super(); - - registerColumnType( Types.BOOLEAN, "bit" ); - - //'tinyint' is an unsigned type in Sybase and - //SQL Server, holding values in the range 0-255 - //see HHH-6779 - registerColumnType( Types.TINYINT, "smallint" ); - - //it's called 'int' not 'integer' - registerColumnType( Types.INTEGER, "int" ); - - //note that 'real' is double precision on SQL Server, single precision on Sybase - //but 'float' is single precision on Sybase, double precision on SQL Server - - registerColumnType( Types.DATE, "datetime" ); - registerColumnType( Types.TIME, "datetime" ); - registerColumnType( Types.TIMESTAMP, "datetime" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "datetime" ); - - registerColumnType( Types.BLOB, "image" ); - registerColumnType( Types.CLOB, "text" ); + public AbstractTransactSQLDialect(DatabaseVersion version, DialectResolutionInfo info) { + super(version, info); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); } + @Override + protected String columnType(int jdbcTypeCode) { + // note that 'real' is double precision on SQL Server, single precision on Sybase + // but 'float' is single precision on Sybase, double precision on SQL Server + switch(jdbcTypeCode) { + case BOOLEAN: + return "bit"; + + case TINYINT: + //'tinyint' is an unsigned type in Sybase and + //SQL Server, holding values in the range 0-255 + //see HHH-6779 + return "smallint"; + case INTEGER: + //it's called 'int' not 'integer' + return "int"; + + case DATE: + case TIME: + case TIMESTAMP: + case TIME_WITH_TIMEZONE: + return "datetime"; + + case BLOB: + return "image"; + case CLOB: + return "text"; + + default: + return super.columnType(jdbcTypeCode); + } + } + @Override public JdbcType resolveSqlTypeDescriptor( String columnTypeName, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java index 9113fc9ada..ece325a7ca 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/CockroachDialect.java @@ -8,11 +8,12 @@ package org.hibernate.dialect; import java.sql.DatabaseMetaData; import java.sql.SQLException; -import java.sql.Types; import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.TimeZone; @@ -55,6 +56,7 @@ import jakarta.persistence.TemporalType; import static org.hibernate.query.TemporalUnit.DAY; import static org.hibernate.query.TemporalUnit.NATIVE; +import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -70,7 +72,6 @@ public class CockroachDialect extends Dialect { // * no support for java.sql.Clob - private final DatabaseVersion version; private final PostgreSQLDriverKind driverKind; public CockroachDialect() { @@ -88,45 +89,56 @@ public class CockroachDialect extends Dialect { } public CockroachDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) { - super(); - - this.version = version; + super(version); this.driverKind = driverKind; + } - registerColumnType( Types.TINYINT, "smallint" ); //no tinyint - - //use 'string' instead of 'varchar' - registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "string($l)"); - registerColumnType( Types.VARCHAR, "string"); - - //no binary/varbinary - registerColumnType( Types.VARBINARY, "bytes" ); - registerColumnType( Types.BINARY, "bytes" ); - - //no clob - registerColumnType( Types.CLOB, "string" ); - - //no nchar/nvarchar - registerColumnType( Types.NCHAR, "string($l)" ); - registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "string($l)" ); - registerColumnType( Types.NVARCHAR, "string"); - - //no nclob - registerColumnType( Types.NCLOB, "string" ); - - registerColumnType( SqlTypes.UUID, "uuid" ); - registerColumnType( SqlTypes.INTERVAL_SECOND, "interval second($s)" ); - - // Prefer jsonb if possible - if ( getVersion().isSameOrAfter( 20, 0 ) ) { - registerColumnType( SqlTypes.INET, "inet" ); - registerColumnType( SqlTypes.JSON, "jsonb" ); - } - else { - registerColumnType( SqlTypes.JSON, "json" ); + @Override + protected List getSupportedJdbcTypeCodes() { + List typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + typeCodes.addAll( List.of(UUID, INTERVAL_SECOND, GEOMETRY, JSON) ); + if ( getVersion().isSameOrAfter( 20 ) ) { + typeCodes.add(INET); } + return typeCodes; + } - registerColumnType( SqlTypes.GEOMETRY, "geometry" ); + @Override + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case TINYINT: + return "smallint"; //no tinyint + + case CHAR: + case NCHAR: + case VARCHAR: + case NVARCHAR: + return "string($l)"; + + case NCLOB: + case CLOB: + return "string"; + + case BINARY: + case VARBINARY: + case BLOB: + return "bytes"; + + case INET: + return "inet"; + case UUID: + return "uuid"; + case GEOMETRY: + return "geometry"; + case INTERVAL_SECOND: + return "interval second($s)"; + + case JSON: + // Prefer jsonb if possible + return getVersion().isSameOrAfter( 20 ) ? "jsonb" : "json"; + default: + return super.columnType(jdbcTypeCode); + } } @Override @@ -148,21 +160,21 @@ public class CockroachDialect extends Dialect { int precision, int scale, JdbcTypeRegistry jdbcTypeRegistry) { - if ( jdbcTypeCode == SqlTypes.OTHER ) { + if ( jdbcTypeCode == OTHER ) { switch ( columnTypeName ) { case "uuid": - jdbcTypeCode = SqlTypes.UUID; + jdbcTypeCode = UUID; break; case "json": case "jsonb": - jdbcTypeCode = SqlTypes.JSON; + jdbcTypeCode = JSON; break; case "inet": - jdbcTypeCode = SqlTypes.INET; + jdbcTypeCode = INET; break; case "geometry": case "geography": - jdbcTypeCode = SqlTypes.GEOMETRY; + jdbcTypeCode = GEOMETRY; break; } } @@ -189,11 +201,6 @@ public class CockroachDialect extends Dialect { } } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public void initializeFunctionRegistry(QueryEngine queryEngine) { super.initializeFunctionRegistry(queryEngine); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java index e4e7ca9db3..eda7ffe36a 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DB2Dialect.java @@ -59,6 +59,8 @@ import java.sql.Types; import jakarta.persistence.TemporalType; +import static org.hibernate.type.SqlTypes.*; + /** * An SQL dialect for DB2. * @@ -75,8 +77,6 @@ public class DB2Dialect extends Dialect { private static final String FOR_SHARE_SKIP_LOCKED_SQL = FOR_SHARE_SQL + SKIP_LOCKED_SQL; private static final String FOR_UPDATE_SKIP_LOCKED_SQL = FOR_UPDATE_SQL + SKIP_LOCKED_SQL; - private final DatabaseVersion version; - private final LimitHandler limitHandler; private final UniqueDelegate uniqueDelegate; @@ -90,37 +90,7 @@ public class DB2Dialect extends Dialect { } public DB2Dialect(DatabaseVersion version) { - super(); - this.version = version; - - registerColumnType( Types.TINYINT, "smallint" ); //no tinyint - - //HHH-12827: map them both to the same type to avoid problems with schema update - //Note that 31 is the maximum precision DB2 supports -// registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); - registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); - - if ( getVersion().isBefore( 11 ) ) { - registerColumnType( Types.BINARY, 254, "char($l) for bit data" ); //should use 'binary' since version 11 - registerColumnType( Types.BINARY, "varchar($l) for bit data" ); //should use 'binary' since version 11 - - registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" ); //should use 'varbinary' since version 11 - - //prior to DB2 11, the 'boolean' type existed, - //but was not allowed as a column type - registerColumnType( Types.BOOLEAN, "smallint" ); - } - registerColumnType( Types.VARBINARY, "blob($l)" ); - - registerColumnType( Types.BLOB, "blob($l)" ); - registerColumnType( Types.CLOB, "clob($l)" ); - - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p)" ); - registerColumnType( Types.TIME_WITH_TIMEZONE, "time" ); - - // The long varchar data type was deprecated in DB2 and shouldn't be used anymore - registerColumnType( Types.VARCHAR, "clob($l)" ); - registerColumnType( Types.NVARCHAR, "nclob($l)" ); + super(version); //not keywords, at least not in DB2 11, //but perhaps they were in older versions? @@ -142,6 +112,51 @@ public class DB2Dialect extends Dialect { : DB2LimitHandler.INSTANCE; } + @Override + protected String columnType(int jdbcTypeCode) { + if ( getVersion().isBefore( 11 ) ) { + switch (jdbcTypeCode) { + case BOOLEAN: + // prior to DB2 11, the 'boolean' type existed, + // but was not allowed as a column type + return "smallint"; + case BINARY: // should use 'binary' since version 11 + case VARBINARY: // should use 'varbinary' since version 11 + return "varchar($l) for bit data"; + } + } + + switch (jdbcTypeCode) { + case TINYINT: + // no tinyint + return "smallint"; + case NUMERIC: + // HHH-12827: map them both to the same type to avoid problems with schema update + // Note that 31 is the maximum precision DB2 supports + return super.columnType(DECIMAL); + case BLOB: + return "blob($l)"; + case CLOB: + return "clob($l)"; + case TIMESTAMP_WITH_TIMEZONE: + return "timestamp($p)"; + case TIME_WITH_TIMEZONE: + return "time"; + default: + return super.columnType(jdbcTypeCode); + } + } + + @Override + protected void registerDefaultColumnTypes(int maxVarcharLength, int maxNVarcharLength, int maxVarBinaryLength) { + // Note: the 'long varchar' data type was deprecated in DB2 and shouldn't be used anymore + super.registerDefaultColumnTypes(maxVarcharLength, maxNVarcharLength, maxVarBinaryLength); + if ( getVersion().isBefore( 11 ) ) { + // should use 'binary' since version 11 + registerColumnType( BINARY, 254, "char($l) for bit data" ); + } + } + protected UniqueDelegate createUniqueDelegate() { return new DB2UniqueDelegate( this ); } @@ -151,11 +166,6 @@ public class DB2Dialect extends Dialect { return 32_672; } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public int getDefaultDecimalPrecision() { //this is the maximum allowed in DB2 @@ -541,11 +551,11 @@ public class DB2Dialect extends Dialect { final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeDescriptorRegistry(); - if ( version.isBefore( 11 ) ) { + if ( getVersion().isBefore( 11 ) ) { jdbcTypeRegistry.addDescriptor( Types.BOOLEAN, SmallIntJdbcType.INSTANCE ); // Binary literals were only added in 11. See https://www.ibm.com/support/knowledgecenter/SSEPGG_11.1.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000731.html#d79816e393 jdbcTypeRegistry.addDescriptor( Types.VARBINARY, VarbinaryJdbcType.INSTANCE_WITHOUT_LITERALS ); - if ( version.isBefore( 9, 7 ) ) { + if ( getVersion().isBefore( 9, 7 ) ) { jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java index 2f232ffc8c..ca6f71249c 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/DerbyDialect.java @@ -70,6 +70,7 @@ import java.sql.Types; import jakarta.persistence.TemporalType; import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.useArgType; +import static org.hibernate.type.SqlTypes.*; /** * Hibernate Dialect for Apache Derby / Cloudscape 10 @@ -91,8 +92,6 @@ public class DerbyDialect extends Dialect { // * can't select a parameter unless wrapped // in a cast or function call - private final DatabaseVersion version; - private final LimitHandler limitHandler; public DerbyDialect(DialectResolutionInfo info) { @@ -105,35 +104,7 @@ public class DerbyDialect extends Dialect { } public DerbyDialect(DatabaseVersion version) { - super(); - this.version = version; - - if ( getVersion().isBefore( 10, 7 ) ) { - registerColumnType( Types.BOOLEAN, "smallint" ); //no boolean before 10.7 - } - registerColumnType( Types.TINYINT, "smallint" ); //no tinyint - registerColumnType( Types.CHAR, 254, "char($l)" ); - - //HHH-12827: map them both to the same type to avoid problems with schema update - //Note that 31 is the maximum precision Derby supports -// registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); - registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); - - registerColumnType( Types.BINARY, 254, "char($l) for bit data" ); - registerColumnType( Types.BINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" ); - registerColumnType( Types.BINARY, "long varchar for bit data" ); - registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "varchar($l) for bit data" ); - registerColumnType( Types.VARBINARY, 32_700,"long varchar for bit data" ); - registerColumnType( Types.VARBINARY, "blob($l)" ); - - registerColumnType( Types.BLOB, "blob($l)" ); - registerColumnType( Types.CLOB, "clob($l)" ); - - registerColumnType( Types.TIMESTAMP, "timestamp" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp" ); - - registerColumnType( Types.VARCHAR, 32_700, "long varchar" ); - registerColumnType( Types.VARCHAR, "clob($l)" ); + super(version); registerDerbyKeywords(); @@ -144,6 +115,52 @@ public class DerbyDialect extends Dialect { getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); } + @Override + protected String columnType(int jdbcTypeCode) { + if ( jdbcTypeCode == BOOLEAN && getVersion().isBefore( 10, 7 ) ) { + return "smallint"; + } + + switch (jdbcTypeCode) { + case TINYINT: + //no tinyint + return "smallint"; + + case NUMERIC: + // HHH-12827: map them both to the same type to avoid problems with schema update + // Note that 31 is the maximum precision Derby supports + return super.columnType(DECIMAL); + + case VARBINARY: + return "varchar($l) for bit data"; + + case BLOB: + return "blob($l)"; + case CLOB: + return "clob($l)"; + + case TIMESTAMP: + case TIMESTAMP_WITH_TIMEZONE: + return "timestamp"; + + default: + return super.columnType(jdbcTypeCode); + } + } + + @Override + protected void registerDefaultColumnTypes(int maxVarcharLength, int maxNVarcharLength, int maxVarBinaryLength) { + super.registerDefaultColumnTypes(maxVarcharLength, maxNVarcharLength, maxVarBinaryLength); + + //long vachar is the right type to use for lengths between 32_672 and 32_700 + registerColumnType( VARBINARY, 32_700,"long varchar for bit data" ); + registerColumnType( VARCHAR, 32_700, "long varchar" ); + + registerColumnType( BINARY, 254, "char($l) for bit data" ); + registerColumnType( BINARY, 32_672, "varchar($l) for bit data" ); + registerColumnType( BINARY, 32_700, "long varchar for bit data" ); + } + @Override public int getMaxVarcharLength() { return 32_672; @@ -173,11 +190,6 @@ public class DerbyDialect extends Dialect { : Types.BOOLEAN; } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public NationalizationSupport getNationalizationSupport() { return NationalizationSupport.IMPLICIT; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index d634e03d13..be21f8dbc2 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -163,6 +163,7 @@ import jakarta.persistence.TemporalType; import static java.lang.Math.ceil; import static java.lang.Math.log; +import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END; import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE; import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME; @@ -223,23 +224,37 @@ public abstract class Dialect implements ConversionContext { private final SizeStrategy sizeStrategy; + private final DatabaseVersion version; + // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + /** + * @deprecated provide a {@link DatabaseVersion} + */ + @Deprecated protected Dialect() { - this(true); + this(null, null); } - protected Dialect(boolean autoRegisterColumnTypes) { + protected Dialect(DatabaseVersion version) { + this( version, null ); + } + + protected Dialect(DatabaseVersion version, DialectResolutionInfo info) { + this.version = version; uniqueDelegate = new DefaultUniqueDelegate( this ); sizeStrategy = new SizeStrategyImpl(); - if (autoRegisterColumnTypes) { - registerDefaultColumnTypes(); - } + registerDefaultColumnTypes(info); // pass the info back down to the subclass in case it needs it (MySQL) registerHibernateTypes(); registerDefaultKeywords(); } - protected void registerDefaultColumnTypes() { + /** + * Register ANSI-standard column types using the length limits defined + * by {@link #getMaxVarcharLength()}, {@link #getMaxNVarcharLength()}, + * and {@link #getMaxVarbinaryLength()}. + */ + protected void registerDefaultColumnTypes(DialectResolutionInfo info) { registerDefaultColumnTypes( getMaxVarcharLength(), getMaxNVarcharLength(), getMaxVarbinaryLength() ); } @@ -249,55 +264,181 @@ public abstract class Dialect implements ConversionContext { * {@code Dialect} by calling {@link #registerColumnType(int,String)} * from the constructor. *

- * Note that {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR} + * This method is aware of the notion of a maximum length for each of + * the types {@link Types#VARCHAR}, {@link Types#NVARCHAR}, and + * {@link Types#VARBINARY}, usually the limits defined by + * {@link #getMaxVarcharLength()}, {@link #getMaxNVarcharLength()}, + * and {@link #getMaxVarbinaryLength()}, and registers "long" types + * for lengths exceeding the limits. + *

+ * The "long" types {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR} * and {@link Types#LONGVARBINARY} are considered synonyms for their * non-{@code LONG} counterparts, with the only difference being that * a different default length is used: {@link org.hibernate.Length#LONG} - * instead of {@link org.hibernate.Length#DEFAULT}. Concrete dialects - * should usually avoid registering mappings for these JDBC types. + * instead of {@link org.hibernate.Length#DEFAULT}. + *

+ * Any registrations made by this method may be overridden by calling + * {@link #registerColumnType(int, String)} explicitly. Alternatively, + * the registrations may be customized by overriding + * {@link #getSupportedJdbcTypeCodes()} and {@link #columnType(int)}. * * @param maxVarcharLength the maximum length of the {@link Types#VARCHAR} type * @param maxNVarcharLength the maximum length of the {@link Types#NVARCHAR} type * @param maxVarBinaryLength the maximum length of the {@link Types#VARBINARY} type */ protected void registerDefaultColumnTypes(int maxVarcharLength, int maxNVarcharLength, int maxVarBinaryLength) { - registerColumnType( Types.BOOLEAN, "boolean" ); + for ( int typeCode : getSupportedJdbcTypeCodes() ) { + switch (typeCode) { + case VARCHAR: + registerColumnType( typeCode, maxVarcharLength, columnType(typeCode) ); + registerColumnType( typeCode, columnType(LONGVARCHAR) ); + break; + case NVARCHAR: + registerColumnType( typeCode, maxNVarcharLength, columnType(typeCode) ); + registerColumnType( typeCode, columnType(LONGNVARCHAR) ); + break; + case VARBINARY: + registerColumnType( typeCode, maxVarBinaryLength, columnType(typeCode) ); + registerColumnType( typeCode, columnType(LONGVARBINARY) ); + break; + default: + registerColumnType( typeCode, columnType(typeCode) ); + } + } + } - registerColumnType( Types.TINYINT, "tinyint" ); - registerColumnType( Types.SMALLINT, "smallint" ); - registerColumnType( Types.INTEGER, "integer" ); - registerColumnType( Types.BIGINT, "bigint" ); + /** + * A list of JDBC types that we expect to be supported on all databases. + */ + private static final List ANSI_SQL_TYPES = List.of( + BOOLEAN, + TINYINT, SMALLINT, INTEGER, BIGINT, + REAL, FLOAT, DOUBLE, + NUMERIC, DECIMAL, + DATE, + TIME, TIME_WITH_TIMEZONE, + TIMESTAMP, TIMESTAMP_WITH_TIMEZONE, + CHAR, VARCHAR, CLOB, + NCHAR, NVARCHAR, NCLOB, + BINARY, VARBINARY, BLOB + ); - registerColumnType( Types.REAL, "real" ); - registerColumnType( Types.FLOAT, "float($p)" ); - registerColumnType( Types.DOUBLE, "double precision" ); + /** + * The JDBC type codes of types supported by this SQL dialect, from the lists + * defined by {@link Types} and {@link SqlTypes}. + *

+ * This method may be overridden by concrete {@code Dialect}s as an alternative + * to calling {@link #registerColumnType(int, String)}. In this case, + * {@link #columnType(int)} should also be overridden. + *

+ * Note that {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR} and + * {@link Types#LONGVARBINARY} are considered synonyms for their + * non-{@code LONG} counterparts, and should not be included in the returned + * array. + * + * @return an array of types from {@link SqlTypes} + * + * @see SqlTypes + * @see #columnType(int) + */ + protected List getSupportedJdbcTypeCodes() { + return ANSI_SQL_TYPES; + } - //these are pretty much synonyms, but are considered - //separate types by the ANSI spec, and in some dialects - registerColumnType( Types.NUMERIC, "numeric($p,$s)" ); - registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); + /** + * The column type name for a given JDBC type code defined in {@link Types} or + * {@link SqlTypes}. This default implementation returns the ANSI-standard type + * name. + *

+ * This method may be overridden by concrete {@code Dialect}s as an alternative + * to calling {@link #registerColumnType(int,String)}. + * + * @param jdbcTypeCode a JDBC type code + * @return a column type name, with $l, $p, $s placeholders for length, precision, scale + * + * @see SqlTypes + * @see #getSupportedJdbcTypeCodes() + */ + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case BOOLEAN: + return "boolean"; - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "time" ); - registerColumnType( Types.TIMESTAMP, "timestamp($p)" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p) with time zone" ); - // type included here for completeness but note that - // very few databases support it, and the general - // advice is to caution against its use (for reasons, - // check the comments in the Postgres documentation). - registerColumnType( Types.TIME_WITH_TIMEZONE, "time with time zone" ); + case TINYINT: + return "tinyint"; + case SMALLINT: + return "smallint"; + case INTEGER: + return "integer"; + case BIGINT: + return "bigint"; - registerColumnType( Types.BINARY, "binary($l)" ); - registerColumnType( Types.VARBINARY, maxVarBinaryLength, "varbinary($l)" ); - registerColumnType( Types.BLOB, "blob" ); + case FLOAT: + // this is the floating point type we prefer! + return "float($p)"; + case REAL: + // this type has very unclear semantics in ANSI SQL, + // so we avoid it and prefer float with an explicit + // precision + return "real"; + case DOUBLE: + // this is just a more verbose way to write float(19) + return "double precision"; - registerColumnType( Types.CHAR, "char($l)" ); - registerColumnType( Types.VARCHAR, maxVarcharLength, "varchar($l)" ); - registerColumnType( Types.CLOB, "clob" ); + // these are pretty much synonyms, but are considered + // separate types by the ANSI spec, and in some dialects + case NUMERIC: + return "numeric($p,$s)"; + case DECIMAL: + return "decimal($p,$s)"; - registerColumnType( Types.NCHAR, "nchar($l)" ); - registerColumnType( Types.NVARCHAR, maxNVarcharLength, "nvarchar($l)" ); - registerColumnType( Types.NCLOB, "nclob" ); + case DATE: + return "date"; + case TIME: + return "time"; + case TIME_WITH_TIMEZONE: + // type included here for completeness but note that + // very few databases support it, and the general + // advice is to caution against its use (for reasons, + // check the comments in the Postgres documentation). + return "time with time zone"; + case TIMESTAMP: + return "timestamp($p)"; + case TIMESTAMP_WITH_TIMEZONE: + return "timestamp($p) with time zone"; + + case CHAR: + return "char($l)"; + case VARCHAR: + return "varchar($l)"; + case CLOB: + return "clob"; + + case NCHAR: + return "nchar($l)"; + case NVARCHAR: + return "nvarchar($l)"; + case NCLOB: + return "nclob"; + + case BINARY: + return "binary($l)"; + case VARBINARY: + return "varbinary($l)"; + case BLOB: + return "blob"; + + // by default use the LOB mappings for the "long" types + case LONGVARCHAR: + return columnType(CLOB); + case LONGNVARCHAR: + return columnType(NCLOB); + case LONGVARBINARY: + return columnType(BLOB); + + default: + throw new IllegalArgumentException("unknown type: " + jdbcTypeCode); + } } protected void registerHibernateTypes() { @@ -353,7 +494,9 @@ public abstract class Dialect implements ConversionContext { } } - public abstract DatabaseVersion getVersion(); + public DatabaseVersion getVersion() { + return version; + } public JdbcType resolveSqlTypeDescriptor( String columnTypeName, diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java index 00369653b8..7e9243cb91 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/H2Dialect.java @@ -7,6 +7,8 @@ package org.hibernate.dialect; import java.sql.Types; +import java.util.ArrayList; +import java.util.List; import org.hibernate.PessimisticLockException; import org.hibernate.boot.model.TypeContributions; @@ -63,6 +65,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import jakarta.persistence.TemporalType; import static org.hibernate.query.TemporalUnit.SECOND; +import static org.hibernate.type.SqlTypes.*; /** * A dialect compatible with the H2 database. @@ -77,8 +80,6 @@ public class H2Dialect extends Dialect { private final boolean cascadeConstraints; private final boolean useLocalTime; - private final DatabaseVersion version; - private final boolean supportsTuplesInSubqueries; private final SequenceInformationExtractor sequenceInformationExtractor; private final String querySequenceString; @@ -88,27 +89,13 @@ public class H2Dialect extends Dialect { registerKeywords( info ); } - private static DatabaseVersion parseVersion(DialectResolutionInfo info) { - return DatabaseVersion.make( info.getMajor(), info.getMinor(), parseBuildId( info ) ); - } - - private static int parseBuildId(DialectResolutionInfo info) { - final String databaseVersion = info.getDatabaseVersion(); - if ( databaseVersion == null ) { - return 0; - } - - final String[] bits = databaseVersion.split("[. ]"); - return bits.length > 2 ? Integer.parseInt( bits[2] ) : 0; - } - public H2Dialect() { this( SimpleDatabaseVersion.ZERO_VERSION ); } public H2Dialect(DatabaseVersion version) { - super(); - this.version = version; + super(version); + // https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440 limitHandler = version.isSameOrAfter( 1, 4, 195 ) ? OffsetFetchLimitHandler.INSTANCE @@ -128,12 +115,6 @@ public class H2Dialect extends Dialect { // http://code.google.com/p/h2database/issues/detail?id=235 getDefaultProperties().setProperty( AvailableSettings.NON_CONTEXTUAL_LOB_CREATION, "true" ); - registerColumnType( Types.VARCHAR, "varchar" ); - registerColumnType( Types.NVARCHAR, "varchar" ); - registerColumnType( Types.VARBINARY, "varbinary" ); - - registerColumnType( SqlTypes.ARRAY, "array" ); - if ( version.isSameOrAfter( 1, 4, 32 ) ) { this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 ) ? SequenceInformationExtractorLegacyImpl.INSTANCE @@ -151,14 +132,51 @@ public class H2Dialect extends Dialect { else { this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE; this.querySequenceString = null; - if ( version.isBefore( 2 ) ) { - // prior to version 2.0, H2 reported NUMERIC columns as DECIMAL, - // which caused problems for schema update tool - registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); - } } } + private static DatabaseVersion parseVersion(DialectResolutionInfo info) { + return DatabaseVersion.make( info.getMajor(), info.getMinor(), parseBuildId( info ) ); + } + + private static int parseBuildId(DialectResolutionInfo info) { + final String databaseVersion = info.getDatabaseVersion(); + if ( databaseVersion == null ) { + return 0; + } + + final String[] bits = databaseVersion.split("[. ]"); + return bits.length > 2 ? Integer.parseInt( bits[2] ) : 0; + } + + @Override + protected String columnType(int jdbcTypeCode) { + if ( jdbcTypeCode == NUMERIC && getVersion().isBefore(2) ) { + // prior to version 2.0, H2 reported NUMERIC columns as DECIMAL, + // which caused problems for schema update tool + return super.columnType(DECIMAL); + } + + switch (jdbcTypeCode) { + case LONGVARCHAR: + case LONGNVARCHAR: + return "varchar"; + case LONGVARBINARY: + return "varbinary"; + case ARRAY: + return "array"; + default: + return super.columnType(jdbcTypeCode); + } + } + + @Override + protected List getSupportedJdbcTypeCodes() { + List typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + typeCodes.add(ARRAY); + return typeCodes; + } + @Override public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) { super.contributeTypes( typeContributions, serviceRegistry ); @@ -166,11 +184,11 @@ public class H2Dialect extends Dialect { final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration() .getJdbcTypeDescriptorRegistry(); - if ( version.isSameOrAfter( 1, 4, 197 ) ) { + if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) { jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); - if ( version.isSameOrAfter( 1, 4, 198 ) ) { - jdbcTypeRegistry.addDescriptorIfAbsent( DurationIntervalSecondJdbcType.INSTANCE ); - } + } + if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) { + jdbcTypeRegistry.addDescriptorIfAbsent( DurationIntervalSecondJdbcType.INSTANCE ); } } @@ -179,11 +197,6 @@ public class H2Dialect extends Dialect { return getVersion().isSame( 1, 4, 200 ); } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public void initializeFunctionRegistry(QueryEngine queryEngine) { super.initializeFunctionRegistry( queryEngine ); @@ -235,7 +248,7 @@ public class H2Dialect extends Dialect { CommonFunctionFactory.median( queryEngine ); CommonFunctionFactory.stddevPopSamp( queryEngine ); CommonFunctionFactory.varPopSamp( queryEngine ); - if ( version.isSame( 1, 4, 200 ) ) { + if ( getVersion().isSame( 1, 4, 200 ) ) { // See https://github.com/h2database/h2database/issues/2518 CommonFunctionFactory.format_toChar( queryEngine ); } @@ -524,7 +537,7 @@ public class H2Dialect extends Dialect { @Override public void appendDatetimeFormat(SqlAppender appender, String format) { - if ( version.isSame( 1, 4, 200 ) ) { + if ( getVersion().isSame( 1, 4, 200 ) ) { // See https://github.com/h2database/h2database/issues/2518 appender.appendSql( OracleDialect.datetimeFormat( format, true, true ).result() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java index 7bc531dab3..8e743724b1 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/HSQLDialect.java @@ -71,6 +71,10 @@ import org.jboss.logging.Logger; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.type.SqlTypes.BLOB; +import static org.hibernate.type.SqlTypes.CLOB; +import static org.hibernate.type.SqlTypes.NCLOB; +import static org.hibernate.type.SqlTypes.NUMERIC; /** * An SQL dialect compatible with HyperSQL (HSQLDB) version 1.8 and above. @@ -85,11 +89,6 @@ public class HSQLDialect extends Dialect { HSQLDialect.class.getName() ); - /** - * version is 180 for 1.8.0 or 200 for 2.0.0 - */ - private final DatabaseVersion version; - public HSQLDialect(DialectResolutionInfo info) { this( info.makeCopy() ); registerKeywords( info ); @@ -100,41 +99,48 @@ public class HSQLDialect extends Dialect { } public HSQLDialect(DatabaseVersion version) { - super(); + super( version.isSame( 1, 8 ) ? reflectedVersion( version ) : version ); - if ( version.isSame( 1, 8 ) ) { - version = reflectedVersion( version ); - } - - this.version = version; - - //Note that all floating point types are synonyms for 'double' - - //Note that the HSQL type 'longvarchar' and 'longvarbinary' - //are synonyms for 'varchar(16M)' and 'varbinary(16M)' respectively. - //Using these types though results in schema validation issue like described in HHH-9693 - - //HSQL has no 'nclob' type, but 'clob' is Unicode - //(See HHH-10364) - registerColumnType( Types.NCLOB, "clob" ); - - if ( this.version.isBefore( 2 ) ) { - //Older versions of HSQL did not accept - //precision for the 'numeric' type - registerColumnType( Types.NUMERIC, "numeric" ); - - //Older versions of HSQL had no lob support - registerColumnType( Types.BLOB, "longvarbinary" ); - registerColumnType( Types.CLOB, "longvarchar" ); - } - - if ( this.version.isSameOrAfter( 2, 5 ) ) { + if ( getVersion().isSameOrAfter( 2, 5 ) ) { registerKeyword( "period" ); } getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); } + @Override + protected String columnType(int jdbcTypeCode) { + // Note that all floating point types are synonyms for 'double' + + // Note that the HSQL type 'longvarchar' and 'longvarbinary' are + // synonyms for 'varchar(16M)' and 'varbinary(16M)' respectively. + // But using these types results in schema validation issue as + // described in HHH-9693. + + //HSQL has no 'nclob' type, but 'clob' is Unicode (See HHH-10364) + if ( jdbcTypeCode==NCLOB ) { + jdbcTypeCode = CLOB; + } + + if ( getVersion().isBefore( 2 ) ) { + switch (jdbcTypeCode) { + case NUMERIC: + // Older versions of HSQL did not accept + // precision for the 'numeric' type + return "numeric"; + + // Older versions of HSQL had no lob support + case BLOB: + return "longvarbinary"; + case CLOB: + return "longvarchar"; + + } + } + + return super.columnType(jdbcTypeCode); + } + private static DatabaseVersion reflectedVersion(DatabaseVersion version) { try { final Class props = ReflectHelper.classForName("org.hsqldb.persist.HsqlDatabaseProperties"); @@ -152,11 +158,6 @@ public class HSQLDialect extends Dialect { } } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public void initializeFunctionRegistry(QueryEngine queryEngine) { super.initializeFunctionRegistry( queryEngine ); @@ -208,13 +209,13 @@ public class HSQLDialect extends Dialect { CommonFunctionFactory.addMonths( queryEngine ); CommonFunctionFactory.monthsBetween( queryEngine ); - if ( version.isSameOrAfter( 2 ) ) { + if ( getVersion().isSameOrAfter( 2 ) ) { //SYSDATE is similar to LOCALTIMESTAMP but it returns the timestamp when it is called CommonFunctionFactory.sysdate( queryEngine ); } // from v. 2.2.0 ROWNUM() is supported in all modes as the equivalent of Oracle ROWNUM - if ( version.isSameOrAfter( 2, 2 ) ) { + if ( getVersion().isSameOrAfter( 2, 2 ) ) { CommonFunctionFactory.rownum( queryEngine ); } } @@ -357,7 +358,7 @@ public class HSQLDialect extends Dialect { @Override public String getForUpdateString() { - if ( version.isSameOrAfter( 2 ) ) { + if ( getVersion().isSameOrAfter( 2 ) ) { return " for update"; } else { @@ -367,8 +368,8 @@ public class HSQLDialect extends Dialect { @Override public LimitHandler getLimitHandler() { - return version.isBefore( 2 ) ? LegacyHSQLLimitHandler.INSTANCE - : version.isBefore( 2, 5 ) ? LimitOffsetLimitHandler.INSTANCE + return getVersion().isBefore( 2 ) ? LegacyHSQLLimitHandler.INSTANCE + : getVersion().isBefore( 2, 5 ) ? LimitOffsetLimitHandler.INSTANCE : OffsetFetchLimitHandler.INSTANCE; } @@ -387,7 +388,7 @@ public class HSQLDialect extends Dialect { @Override public boolean supportsColumnCheck() { - return version.isSameOrAfter( 2 ); + return getVersion().isSameOrAfter( 2 ); } @Override @@ -408,7 +409,7 @@ public class HSQLDialect extends Dialect { @Override public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { - return version.isBefore( 2 ) ? EXTRACTOR_18 : EXTRACTOR_20; + return getVersion().isBefore( 2 ) ? EXTRACTOR_18 : EXTRACTOR_20; } private static final ViolatedConstraintNameExtractor EXTRACTOR_18 = @@ -518,7 +519,7 @@ public class HSQLDialect extends Dialect { // the definition and data is private to the session and table declaration // can happen in the middle of a transaction - if ( version.isBefore( 2 ) ) { + if ( getVersion().isBefore( 2 ) ) { return new GlobalTemporaryTableMutationStrategy( TemporaryTable.createIdTable( rootEntityDescriptor, @@ -558,7 +559,7 @@ public class HSQLDialect extends Dialect { // the definition and data is private to the session and table declaration // can happen in the middle of a transaction - if ( version.isBefore( 2 ) ) { + if ( getVersion().isBefore( 2 ) ) { return new GlobalTemporaryTableInsertStrategy( TemporaryTable.createEntityTable( rootEntityDescriptor, @@ -586,24 +587,24 @@ public class HSQLDialect extends Dialect { @Override public TemporaryTableKind getSupportedTemporaryTableKind() { - return version.isBefore( 2 ) ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL; + return getVersion().isBefore( 2 ) ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL; } @Override public String getTemporaryTableCreateCommand() { - return version.isBefore( 2 ) ? super.getTemporaryTableCreateCommand() : "declare local temporary table"; + return getVersion().isBefore( 2 ) ? super.getTemporaryTableCreateCommand() : "declare local temporary table"; } @Override public AfterUseAction getTemporaryTableAfterUseAction() { // Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end // of the session (by default, data is cleared at commit). - return version.isBefore( 2 ) ? AfterUseAction.CLEAN : AfterUseAction.DROP; + return getVersion().isBefore( 2 ) ? AfterUseAction.CLEAN : AfterUseAction.DROP; } @Override public BeforeUseAction getTemporaryTableBeforeUseAction() { - return version.isBefore( 2 ) ? BeforeUseAction.NONE : BeforeUseAction.CREATE; + return getVersion().isBefore( 2 ) ? BeforeUseAction.NONE : BeforeUseAction.CREATE; } // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -651,7 +652,7 @@ public class HSQLDialect extends Dialect { case OPTIMISTIC_FORCE_INCREMENT: return new OptimisticForceIncrementLockingStrategy(lockable, lockMode); } - if ( version.isBefore( 2 ) ) { + if ( getVersion().isBefore( 2 ) ) { return new ReadUncommittedLockingStrategy( lockable, lockMode ); } else { @@ -676,19 +677,19 @@ public class HSQLDialect extends Dialect { @Override public boolean supportsCommentOn() { - return version.isSameOrAfter( 2 ); + return getVersion().isSameOrAfter( 2 ); } // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @Override public boolean doesReadCommittedCauseWritersToBlockReaders() { - return version.isSameOrAfter( 2 ); + return getVersion().isSameOrAfter( 2 ); } @Override public boolean doesRepeatableReadCauseReadersToBlockWriters() { - return version.isSameOrAfter( 2 ); + return getVersion().isSameOrAfter( 2 ); } @Override @@ -709,7 +710,7 @@ public class HSQLDialect extends Dialect { @Override public boolean supportsTupleDistinctCounts() { // from v. 2.2.9 is added support for COUNT(DISTINCT ...) with multiple arguments - return version.isSameOrAfter( 2, 2, 9 ); + return getVersion().isSameOrAfter( 2, 2, 9 ); } @Override @@ -724,7 +725,7 @@ public class HSQLDialect extends Dialect { @Override public IdentityColumnSupport getIdentityColumnSupport() { - return new HSQLIdentityColumnSupport( this.version ); + return new HSQLIdentityColumnSupport( this.getVersion() ); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java index ca4478570a..978907b6e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java @@ -34,35 +34,34 @@ import org.hibernate.type.StandardBasicTypes; */ public class MariaDBDialect extends MySQLDialect { - private final DatabaseVersion version; + private final DatabaseVersion mariaVersion; public MariaDBDialect() { this( DatabaseVersion.make( 5 ) ); } public MariaDBDialect(DialectResolutionInfo info) { - this( info.makeCopy(), getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) ) ); + this( info.makeCopy(), info ); registerKeywords( info ); } public MariaDBDialect(DatabaseVersion version) { - // Let's be conservative and assume people use a 4 byte character set - this( version, 4 ); + this(version, null); } - public MariaDBDialect(DatabaseVersion version, int characterSetBytesPerCharacter) { + protected MariaDBDialect(DatabaseVersion mariaVersion, DialectResolutionInfo info) { super( - version.isBefore( 5, 3 ) + mariaVersion.isBefore( 5, 3 ) ? DatabaseVersion.make( 5 ) : DatabaseVersion.make( 5, 7 ), - characterSetBytesPerCharacter + info ); - this.version = version; + this.mariaVersion = mariaVersion; } @Override public DatabaseVersion getVersion() { - return version; + return mariaVersion; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java index 1cdad7562f..a15c3460c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/MySQLDialect.java @@ -11,6 +11,8 @@ import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.util.ArrayList; +import java.util.List; import org.hibernate.LockOptions; import org.hibernate.PessimisticLockException; @@ -76,6 +78,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.type.SqlTypes.*; /** * An SQL dialect for MySQL (prior to 5.x). @@ -86,12 +89,12 @@ public class MySQLDialect extends Dialect { private final UniqueDelegate uniqueDelegate; private final MySQLStorageEngine storageEngine; - private final DatabaseVersion version; - private final int characterSetBytesPerCharacter; private final SizeStrategy sizeStrategy; + private int maxVarcharLength; + private int maxVarbinaryLength; public MySQLDialect(DialectResolutionInfo info) { - this( info.makeCopy(), getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) ) ); + this( info.makeCopy(), info ); registerKeywords( info ); } @@ -100,16 +103,11 @@ public class MySQLDialect extends Dialect { } public MySQLDialect(DatabaseVersion version) { - // Let's be conservative and assume people use a 4 byte character set - this( version, 4 ); + this(version, null); } - public MySQLDialect(DatabaseVersion version, int characterSetBytesPerCharacter) { - super(false); - this.version = version; - this.characterSetBytesPerCharacter = characterSetBytesPerCharacter; - - registerDefaultColumnTypes(); + protected MySQLDialect(DatabaseVersion mySQLVersion, DialectResolutionInfo info) { + super(mySQLVersion, info); String storageEngine = Environment.getProperties().getProperty( Environment.STORAGE_ENGINE ); if (storageEngine == null) { @@ -128,72 +126,13 @@ public class MySQLDialect extends Dialect { throw new UnsupportedOperationException( "The " + storageEngine + " storage engine is not supported!" ); } - registerColumnType( Types.BOOLEAN, "bit" ); // HHH-6935: Don't use "boolean" i.e. tinyint(1) due to JDBC ResultSetMetaData - - registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); //it's just a synonym - - if ( getMySQLVersion().isBefore( 5, 7 ) ) { - registerColumnType( Types.TIMESTAMP, "datetime" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp" ); - } - else { - // Since 5.7 we can explicitly specify a fractional second - // precision for the timestamp-like types - registerColumnType(Types.TIMESTAMP, "datetime($p)"); - registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p)"); - } - - final int maxTinyLobLen = 255; - final int maxLobLen = 65_535; - final int maxMediumLobLen = 16_777_215; - //the maximum long LOB length is 4_294_967_295, bigger than any Java string - - registerColumnType( Types.VARCHAR, "longtext" ); - registerColumnType( Types.VARCHAR, maxMediumLobLen, "mediumtext" ); - if ( getMaxVarcharLength() < maxLobLen ) { - registerColumnType( Types.VARCHAR, maxLobLen, "text" ); - } - - registerColumnType( Types.NVARCHAR, "longtext" ); - registerColumnType( Types.NVARCHAR, maxMediumLobLen, "mediumtext" ); - if ( getMaxNVarcharLength() < maxLobLen ) { - registerColumnType( Types.NVARCHAR, maxLobLen, "text" ); - } - - registerColumnType( Types.VARBINARY, "longblob" ); - registerColumnType( Types.VARBINARY, maxMediumLobLen, "mediumblob" ); - if ( getMaxVarbinaryLength() < maxLobLen ) { - registerColumnType( Types.VARBINARY, maxLobLen, "blob" ); - } - - registerColumnType( Types.BLOB, "longblob" ); - registerColumnType( Types.BLOB, maxMediumLobLen, "mediumblob" ); - registerColumnType( Types.BLOB, maxLobLen, "blob" ); - registerColumnType( Types.BLOB, maxTinyLobLen, "tinyblob" ); - - registerColumnType( Types.CLOB, "longtext" ); - registerColumnType( Types.CLOB, maxMediumLobLen, "mediumtext" ); - registerColumnType( Types.CLOB, maxLobLen, "text" ); - registerColumnType( Types.CLOB, maxTinyLobLen, "tinytext" ); - - registerColumnType( Types.NCLOB, "longtext" ); - registerColumnType( Types.NCLOB, maxMediumLobLen, "mediumtext" ); - registerColumnType( Types.NCLOB, maxLobLen, "text" ); - registerColumnType( Types.NCLOB, maxTinyLobLen, "tinytext" ); - - if ( getMySQLVersion().isBefore( 5, 7 ) ) { - // MySQL 5.7 brings JSON native support with a dedicated datatype - // https://dev.mysql.com/doc/refman/5.7/en/json.html - registerColumnType( SqlTypes.JSON, "json"); - } - registerColumnType( SqlTypes.GEOMETRY, "geometry" ); - registerKeyword( "key" ); getDefaultProperties().setProperty( Environment.MAX_FETCH_DEPTH, "2" ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); uniqueDelegate = new MySQLUniqueDelegate( this ); + sizeStrategy = new SizeStrategyImpl() { @Override public Size resolveSize( @@ -204,7 +143,7 @@ public class MySQLDialect extends Dialect { Long length) { switch ( jdbcType.getDefaultSqlTypeCode() ) { case Types.BIT: - // MySQL allows BIT with a length up to 64 + // MySQL allows BIT with a length up to 64 (less the the default length 255) if ( length != null ) { return Size.length( Math.min( Math.max( length, 1 ), 64 ) ); } @@ -214,6 +153,101 @@ public class MySQLDialect extends Dialect { }; } + @Override + protected List getSupportedJdbcTypeCodes() { + List typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + typeCodes.add(GEOMETRY); + if ( getMySQLVersion().isBefore( 5, 7 ) ) { + // MySQL 5.7 brings JSON native support with a dedicated datatype + // https://dev.mysql.com/doc/refman/5.7/en/json.html + typeCodes.add(JSON); + } + return typeCodes; + } + + @Override + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case BOOLEAN: + // HHH-6935: Don't use "boolean" i.e. tinyint(1) due to JDBC ResultSetMetaData + return "bit"; + + case TIMESTAMP: + return getMySQLVersion().isBefore( 5, 7 ) + ? "datetime" : "datetime($p)"; + case TIMESTAMP_WITH_TIMEZONE: + return getMySQLVersion().isBefore( 5, 7 ) + ? "timestamp" : "timestamp($p)"; + + case NUMERIC: + // it's just a synonym + return super.columnType(DECIMAL); + + case JSON: + return "json"; + case GEOMETRY: + return "geometry"; + + case BLOB: + return "longblob"; + case NCLOB: + case CLOB: + return "longtext"; + } + return super.columnType(jdbcTypeCode); + } + + @Override + protected void registerDefaultColumnTypes(DialectResolutionInfo info) { + // we need to remember the character set before calling getMaxVarcharLength() + // we could not do this earlier because we get called from the constructor + // of the superclass, before our own constructor has run + int bytesPerCharacter = info == null + ? 4 // Let's be conservative and assume people use a 4 byte character set + : getCharacterSetBytesPerCharacter( info.unwrap(DatabaseMetaData.class) ); + maxVarcharLength = maxVarcharLength(bytesPerCharacter); + maxVarbinaryLength = maxVarbinaryLength(); + + super.registerDefaultColumnTypes( maxVarcharLength, maxVarcharLength, maxVarbinaryLength ); + + // MySQL has approximately one million text and blob types. We have + // already registered longtext + longblob via the regular method, + // but we still need to do the rest of them here. + + final int maxTinyLobLen = 255; + final int maxLobLen = 65_535; + final int maxMediumLobLen = 16_777_215; + + //the maximum long LOB length is 4_294_967_295, bigger than any Java string + + registerColumnType( VARCHAR, maxMediumLobLen, "mediumtext" ); + if ( getMaxVarcharLength() < maxLobLen ) { + registerColumnType( VARCHAR, maxLobLen, "text" ); + } + + registerColumnType( NVARCHAR, maxMediumLobLen, "mediumtext" ); + if ( getMaxNVarcharLength() < maxLobLen ) { + registerColumnType( NVARCHAR, maxLobLen, "text" ); + } + + registerColumnType( VARBINARY, maxMediumLobLen, "mediumblob" ); + if ( getMaxVarbinaryLength() < maxLobLen ) { + registerColumnType( VARBINARY, maxLobLen, "blob" ); + } + + registerColumnType( BLOB, maxMediumLobLen, "mediumblob" ); + registerColumnType( BLOB, maxLobLen, "blob" ); + registerColumnType( BLOB, maxTinyLobLen, "tinyblob" ); + + registerColumnType( CLOB, maxMediumLobLen, "mediumtext" ); + registerColumnType( CLOB, maxLobLen, "text" ); + registerColumnType( CLOB, maxTinyLobLen, "tinytext" ); + + registerColumnType( NCLOB, maxMediumLobLen, "mediumtext" ); + registerColumnType( NCLOB, maxLobLen, "text" ); + registerColumnType( NCLOB, maxTinyLobLen, "tinytext" ); + } + protected static int getCharacterSetBytesPerCharacter(DatabaseMetaData databaseMetaData) { if ( databaseMetaData != null ) { try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { @@ -254,14 +288,17 @@ public class MySQLDialect extends Dialect { return 4; } - @Override - public int getMaxVarcharLength() { + private int maxVarbinaryLength() { + return getMySQLVersion().isBefore( 5 ) ? 255 : 65_535; + } + + private int maxVarcharLength(int bytesPerCharacter) { // max length for VARCHAR changed in 5.0.3 if ( getMySQLVersion().isBefore( 5 ) ) { return 255; } else { - switch ( characterSetBytesPerCharacter ) { + switch ( bytesPerCharacter ) { case 1: return 65_535; case 2: @@ -275,9 +312,14 @@ public class MySQLDialect extends Dialect { } } + @Override + public int getMaxVarcharLength() { + return maxVarcharLength; + } + @Override public int getMaxVarbinaryLength() { - return getMySQLVersion().isBefore( 5 ) ? 255 : 65_535; + return maxVarbinaryLength; } @Override @@ -290,13 +332,8 @@ public class MySQLDialect extends Dialect { return super.getNullColumnString( columnType ); } - @Override - public DatabaseVersion getVersion() { - return version; - } - public DatabaseVersion getMySQLVersion() { - return version; + return super.getVersion(); } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java index cbe5e6720d..c481f683a7 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/OracleDialect.java @@ -10,6 +10,8 @@ import java.sql.CallableStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; +import java.util.ArrayList; +import java.util.List; import java.util.Locale; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -68,7 +70,6 @@ import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorOr import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.type.JavaObjectType; import org.hibernate.type.NullType; -import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.BlobJdbcType; @@ -86,6 +87,7 @@ import static org.hibernate.query.TemporalUnit.MINUTE; import static org.hibernate.query.TemporalUnit.MONTH; import static org.hibernate.query.TemporalUnit.SECOND; import static org.hibernate.query.TemporalUnit.YEAR; +import static org.hibernate.type.SqlTypes.*; /** * A dialect for Oracle 8i and above. @@ -105,7 +107,6 @@ public class OracleDialect extends Dialect { public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw"; private final LimitHandler limitHandler; - private final DatabaseVersion version; public OracleDialect(DialectResolutionInfo info) { this( info.makeCopy() ); @@ -117,15 +118,8 @@ public class OracleDialect extends Dialect { } public OracleDialect(DatabaseVersion version) { - super(); - this.version = version; + super(version); - registerCharacterTypeMappings(); - registerNumericTypeMappings(); - registerDateTimeTypeMappings(); - registerBinaryTypeMappings(); - registerExtendedTypeMappings(); - registerReverseHibernateTypeMappings(); registerDefaultProperties(); limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY ) @@ -133,11 +127,6 @@ public class OracleDialect extends Dialect { : new LegacyOracleLimitHandler( getVersion() ); } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public int getPreferredSqlTypeCodeForBoolean() { return Types.BIT; @@ -536,52 +525,69 @@ public class OracleDialect extends Dialect { pattern.append( unit.conversionFactor( toUnit, this ) ); } - protected void registerCharacterTypeMappings() { - if ( getVersion().isBefore( 9 ) ) { - registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "varchar2($l)" ); - registerColumnType( Types.VARCHAR, "clob" ); - } - else { - registerColumnType( Types.CHAR, "char($l char)" ); - registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "varchar2($l char)" ); - registerColumnType( Types.VARCHAR, "clob" ); - registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "nvarchar2($l)" ); - registerColumnType( Types.NVARCHAR, "nclob" ); - } + @Override + protected String columnType(int jdbcTypeCode) { //note: the 'long' type is deprecated + switch (jdbcTypeCode) { + case BOOLEAN: + // still, after all these years... + return "number(1,0)"; + + case VARCHAR: + return getVersion().isBefore( 9 ) + ? "varchar2($l)": "varchar2($l char)"; + case NVARCHAR: + return "nvarchar2($l)"; + + case BIGINT: + return "number(19,0)"; + case SMALLINT: + return "number(5,0)"; + case TINYINT: + return "number(3,0)"; + case INTEGER: + return "number(10,0)"; + + case REAL: + // Oracle's 'real' type is actually double precision + return "float(24)"; + + case NUMERIC: + case DECIMAL: + // Note that 38 is the maximum precision Oracle supports + return "number($p,$s)"; + + case DATE: + case TIME: + return "date"; + case TIMESTAMP: + // the only difference between date and timestamp + // on Oracle is that date has no fractional seconds + return getVersion().isBefore( 9 ) + ? "date" : "timestamp($p)"; + case TIME_WITH_TIMEZONE: + return getVersion().isBefore( 9 ) + ? "date" : "timestamp($p) with time zone"; + + case BINARY: + case VARBINARY: + return "raw($l)"; + + case GEOMETRY: + return "MDSYS.SDO_GEOMETRY"; + + default: + return super.columnType(jdbcTypeCode); + } } - protected void registerNumericTypeMappings() { - registerColumnType( Types.BOOLEAN, "number(1,0)" ); - - registerColumnType( Types.BIGINT, "number(19,0)" ); - registerColumnType( Types.SMALLINT, "number(5,0)" ); - registerColumnType( Types.TINYINT, "number(3,0)" ); - registerColumnType( Types.INTEGER, "number(10,0)" ); - - // Oracle has DOUBLE semantics for the REAL type, so we map it to float(24) - registerColumnType( Types.REAL, "float(24)" ); - - // Note that 38 is the maximum precision Oracle supports - registerColumnType( Types.NUMERIC, "number($p,$s)" ); - registerColumnType( Types.DECIMAL, "number($p,$s)" ); - } - - protected void registerDateTimeTypeMappings() { - if ( getVersion().isBefore( 9 ) ) { - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "date" ); - registerColumnType( Types.TIMESTAMP, "date" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "date" ); - } - else { - //the only difference between date and timestamp - //on Oracle is that date has no fractional seconds - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "date" ); - registerColumnType( Types.TIMESTAMP, "timestamp($p)" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p) with time zone" ); + @Override + protected List getSupportedJdbcTypeCodes() { + List list = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + if ( getVersion().isSameOrAfter( 10 ) ) { + list.add(GEOMETRY); } + return list; } @Override @@ -589,23 +595,6 @@ public class OracleDialect extends Dialect { return getVersion().isSameOrAfter( 9 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; } - protected void registerBinaryTypeMappings() { - registerColumnType( Types.BINARY, getMaxVarbinaryLength(), "raw($l)" ); - registerColumnType( Types.BINARY, "blob" ); - - registerColumnType( Types.VARBINARY, getMaxVarbinaryLength(), "raw($l)" ); - registerColumnType( Types.VARBINARY, "blob" ); - } - - protected void registerExtendedTypeMappings() { - if ( getVersion().isSameOrAfter( 10 ) ) { - registerColumnType( SqlTypes.GEOMETRY, "MDSYS.SDO_GEOMETRY" ); - } - } - - protected void registerReverseHibernateTypeMappings() { - } - protected void registerDefaultProperties() { getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "true" ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java index b23f45e479..537d8fe8c4 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/PostgreSQLDialect.java @@ -12,6 +12,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.Iterator; @@ -85,6 +86,7 @@ import static org.hibernate.query.TemporalUnit.EPOCH; import static org.hibernate.query.TemporalUnit.MONTH; import static org.hibernate.query.TemporalUnit.QUARTER; import static org.hibernate.query.TemporalUnit.YEAR; +import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -98,7 +100,6 @@ public class PostgreSQLDialect extends Dialect { private static final PostgreSQLIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new PostgreSQLIdentityColumnSupport(); - private final DatabaseVersion version; private final PostgreSQLDriverKind driverKind; public PostgreSQLDialect(DialectResolutionInfo info) { @@ -116,53 +117,77 @@ public class PostgreSQLDialect extends Dialect { } public PostgreSQLDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) { - super(); - this.version = version; + super(version); this.driverKind = driverKind; - registerColumnType( Types.TINYINT, "smallint" ); //no tinyint, not even in Postgres 11 - - registerColumnType( Types.VARBINARY, "bytea" ); - registerColumnType( Types.BINARY, "bytea" ); - - //use oid as the blob type on Postgres because - //the JDBC driver is rubbish - registerColumnType( Types.BLOB, "oid" ); - registerColumnType( Types.CLOB, "oid" ); - - //there are no nchar/nvarchar types in Postgres - registerColumnType( Types.NCHAR, "char($l)" ); - registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "varchar($l)" ); - registerColumnType( Types.NVARCHAR, "text" ); - - // since there's no real difference between TEXT and VARCHAR, - // except for the length limit, we can just use 'text' for the - // "long" string types - registerColumnType( Types.VARCHAR, "text" ); - - registerColumnType( SqlTypes.INET, "inet" ); - registerColumnType( SqlTypes.INTERVAL_SECOND, "interval second($s)" ); - - if ( getVersion().isSameOrAfter( 8, 2 ) ) { - registerColumnType( SqlTypes.UUID, "uuid" ); - - if ( getVersion().isSameOrAfter( 9, 2 ) ) { - // Prefer jsonb if possible - if ( getVersion().isSameOrAfter( 9, 4 ) ) { - registerColumnType( SqlTypes.JSON, "jsonb" ); - } - else { - registerColumnType( SqlTypes.JSON, "json" ); - } - } - } - - registerColumnType( SqlTypes.GEOMETRY, "geometry" ); - getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" ); } + @Override + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case TINYINT: + // no tinyint, not even in Postgres 11 + return "smallint"; + + case BINARY: + return "bytea"; + + case BLOB: + case CLOB: + // use oid as the blob type on Postgres because + // the JDBC driver is rubbish + return "oid"; + + // there are no nchar/nvarchar types in Postgres + case NCHAR: + return super.columnType(CHAR); + case NVARCHAR: + return super.columnType(VARCHAR); + + // since there's no real difference between TEXT and VARCHAR, + // except for the length limit, we can just use 'text' for the + // "long" string types + case LONGVARCHAR: + case LONGNVARCHAR: + return "text"; + // use bytea as the "long" binary type (that there is no + // real VARBINARY type in Postgres, so we always use this) + case LONGVARBINARY: + return "bytea"; + + case INET: + return "inet"; + case UUID: + return "uuid"; + case GEOMETRY: + return "geometry"; + case JSON: + // Prefer jsonb if possible + return getVersion().isSameOrAfter( 9, 4 ) + ? "jsonb" : "json"; + case INTERVAL_SECOND: + return "interval second($s)"; + + default: + return super.columnType(jdbcTypeCode); + } + } + + @Override + protected List getSupportedJdbcTypeCodes() { + List typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + typeCodes.addAll( List.of(INET, INTERVAL_SECOND, GEOMETRY) ); + if ( getVersion().isSameOrAfter( 8, 2 ) ) { + typeCodes.add(UUID); + } + if ( getVersion().isSameOrAfter( 9, 2 ) ) { + typeCodes.add(JSON); + } + return typeCodes; + } + @Override public int getMaxVarcharLength() { return 10_485_760; @@ -171,17 +196,17 @@ public class PostgreSQLDialect extends Dialect { @Override public int getMaxVarbinaryLength() { //postgres has no varbinary-like type - return 0; + return -1; } @Override public String getTypeName(int code, Size size) throws HibernateException { // The maximum scale for `interval second` is 6 unfortunately so we have to use numeric by default switch ( code ) { - case SqlTypes.INTERVAL_SECOND: + case INTERVAL_SECOND: final Integer scale = size.getScale(); if ( scale == null || scale > 6 ) { - return getTypeName( SqlTypes.NUMERIC, size ); + return getTypeName( NUMERIC, size ); } } return super.getTypeName( code, size ); @@ -194,32 +219,27 @@ public class PostgreSQLDialect extends Dialect { int precision, int scale, JdbcTypeRegistry jdbcTypeRegistry) { - if ( jdbcTypeCode == SqlTypes.OTHER ) { + if ( jdbcTypeCode == OTHER ) { switch ( columnTypeName ) { case "uuid": - jdbcTypeCode = SqlTypes.UUID; + jdbcTypeCode = UUID; break; case "json": case "jsonb": - jdbcTypeCode = SqlTypes.JSON; + jdbcTypeCode = JSON; break; case "inet": - jdbcTypeCode = SqlTypes.INET; + jdbcTypeCode = INET; break; case "geometry": case "geography": - jdbcTypeCode = SqlTypes.GEOMETRY; + jdbcTypeCode = GEOMETRY; break; } } return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public String currentTime() { return "localtime"; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java index a5b74d6c68..ae5a009946 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SQLServerDialect.java @@ -50,7 +50,6 @@ import org.hibernate.tool.schema.internal.StandardSequenceExporter; import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.type.BasicType; import org.hibernate.type.BasicTypeRegistry; -import org.hibernate.type.SqlTypes; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor; import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType; @@ -60,13 +59,16 @@ import java.sql.SQLException; import java.sql.Types; import java.time.temporal.ChronoField; import java.time.temporal.TemporalAccessor; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.List; import java.util.TimeZone; import jakarta.persistence.TemporalType; import static org.hibernate.query.TemporalUnit.NANOSECOND; +import static org.hibernate.type.SqlTypes.*; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; @@ -79,12 +81,10 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM public class SQLServerDialect extends AbstractTransactSQLDialect { private static final int PARAM_LIST_SIZE_LIMIT = 2100; - private final DatabaseVersion version; - private StandardSequenceExporter exporter; public SQLServerDialect(DialectResolutionInfo info) { - this( info.makeCopy() ); + this( info.makeCopy(), info ); registerKeywords( info ); } @@ -93,53 +93,71 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { } public SQLServerDialect(DatabaseVersion version) { - super(); - this.version = version; + this(version, null); + } - //there is no 'double' type in SQL server - //but 'float' is double precision by default - registerColumnType( Types.DOUBLE, "float" ); - - if ( getVersion().isSameOrAfter( 10 ) ) { - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "time" ); - registerColumnType( Types.TIMESTAMP, "datetime2($p)" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "datetimeoffset($p)" ); - registerColumnType( SqlTypes.GEOMETRY, "geometry" ); - } + protected SQLServerDialect(DatabaseVersion version, DialectResolutionInfo info) { + super(version, info); if ( getVersion().isSameOrAfter( 11 ) ) { exporter = new SqlServerSequenceExporter( this ); } - if ( getVersion().isBefore( 9 ) ) { - registerColumnType( Types.VARBINARY, "image" ); - registerColumnType( Types.VARCHAR, "text" ); - registerColumnType( Types.NVARCHAR, "text" ); - } - else { + registerKeyword( "top" ); + registerKeyword( "key" ); + } - // Use 'varchar(max)' and 'varbinary(max)' instead + @Override + protected List getSupportedJdbcTypeCodes() { + List list = new ArrayList<>( super.getSupportedJdbcTypeCodes() ); + if ( getVersion().isSameOrAfter( 10 ) ) { + list.add(GEOMETRY); + } + return list; + } + + @Override + protected String columnType(int jdbcTypeCode) { + if ( getVersion().isSameOrAfter( 10 ) ) { + switch (jdbcTypeCode) { + case DATE: + return "date"; + case TIME: + return "time"; + case TIMESTAMP: + return"datetime2($p)"; + case TIMESTAMP_WITH_TIMEZONE: + return "datetimeoffset($p)"; + } + } + + if ( getVersion().isSameOrAfter(9) ) { + // Prefer 'varchar(max)' and 'varbinary(max)' to // the deprecated TEXT and IMAGE types. Note that // the length of a VARCHAR or VARBINARY column must // be either between 1 and 8000 or exactly MAX, and // the length of an NVARCHAR column must be either - // between 1 and 4000 or exactly MAX. - - // See http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx - // See HHH-3965 - - registerColumnType( Types.BLOB, "varbinary(max)" ); - registerColumnType( Types.VARBINARY, "varbinary(max)" ); - - registerColumnType( Types.CLOB, "varchar(max)" ); - registerColumnType( Types.NCLOB, "nvarchar(max)" ); // HHH-8435 fix - registerColumnType( Types.VARCHAR, "varchar(max)" ); - registerColumnType( Types.NVARCHAR, "nvarchar(max)" ); + // between 1 and 4000 or exactly MAX. (HHH-3965) + switch (jdbcTypeCode) { + case BLOB: + return "varbinary(max)"; + case CLOB: + return "varchar(max)"; + case NCLOB: + return "nvarchar(max)"; + } } - registerKeyword( "top" ); - registerKeyword( "key" ); + switch (jdbcTypeCode) { + case DOUBLE: + // there is no 'double' type in SQL server + // but 'float' is double precision by default + return "float"; + case GEOMETRY: + return "geometry"; + default: + return super.columnType(jdbcTypeCode); + } } @Override @@ -152,11 +170,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect { return 4000; } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public TimeZoneSupport getTimeZoneSupport() { return getVersion().isSameOrAfter( 10 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java index 0e75c5f5a1..7cc974137d 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SpannerDialect.java @@ -53,6 +53,7 @@ import org.hibernate.type.StandardBasicTypes; import jakarta.persistence.TemporalType; import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION; +import static org.hibernate.type.SqlTypes.*; /** * Hibernate Dialect implementation for Cloud Spanner. @@ -73,50 +74,61 @@ public class SpannerDialect extends Dialect { private static final UniqueDelegate NOOP_UNIQUE_DELEGATE = new DoNothingUniqueDelegate(); public SpannerDialect() { - registerColumnType( Types.BOOLEAN, "bool" ); - - registerColumnType( Types.TINYINT, "int64" ); - registerColumnType( Types.SMALLINT, "int64" ); - registerColumnType( Types.INTEGER, "int64" ); - registerColumnType( Types.BIGINT, "int64" ); - - registerColumnType( Types.REAL, "float64" ); - registerColumnType( Types.FLOAT, "float64" ); - registerColumnType( Types.DOUBLE, "float64" ); - registerColumnType( Types.DECIMAL, "float64" ); - registerColumnType( Types.NUMERIC, "float64" ); - - //timestamp does not accept precision - registerColumnType( Types.TIMESTAMP, "timestamp" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp" ); - //there is no time type of any kind - registerColumnType( Types.TIME, "timestamp" ); - - registerColumnType( Types.CHAR, getMaxVarcharLength(), "string($l)" ); - registerColumnType( Types.CHAR, "string(max)" ); - registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "string($l)" ); - registerColumnType( Types.VARCHAR, "string(max)" ); - - registerColumnType( Types.NCHAR, getMaxNVarcharLength(), "string($l)" ); - registerColumnType( Types.NCHAR, "string(max)" ); - registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "string($l)" ); - registerColumnType( Types.NVARCHAR, "string(max)" ); - - registerColumnType( Types.BINARY, getMaxBytesLength(), "bytes($l)" ); - registerColumnType( Types.BINARY, "bytes(max)" ); - registerColumnType( Types.VARBINARY, getMaxBytesLength(), "bytes($l)" ); - registerColumnType( Types.VARBINARY, "bytes(max)" ); - - registerColumnType( Types.CLOB, "string(max)" ); - registerColumnType( Types.NCLOB, "string(max)" ); - registerColumnType( Types.BLOB, "bytes(max)" ); + super(); } public SpannerDialect(DialectResolutionInfo info) { - this(); + super(); registerKeywords( info ); } + @Override + protected String columnType(int jdbcTypeCode) { + switch (jdbcTypeCode) { + case BOOLEAN: + return "bool"; + + case TINYINT: + case SMALLINT: + case INTEGER: + case BIGINT: + return "int64"; + + case REAL: + case FLOAT: + case DOUBLE: + case DECIMAL: + case NUMERIC: + return "float64"; + + //there is no time type of any kind + case TIME: + //timestamp does not accept precision + case TIMESTAMP: + case TIMESTAMP_WITH_TIMEZONE: + return "timestamp"; + + case CHAR: + case NCHAR: + case VARCHAR: + case NVARCHAR: + return "string($l)"; + + case BINARY: + case VARBINARY: + return "bytes($l)"; + + case CLOB: + case NCLOB: + return "string(max)"; + case BLOB: + return "bytes(max)"; + + default: + return super.columnType(jdbcTypeCode); + } + } + @Override public int getMaxVarcharLength() { //max is equivalent to 2_621_440 diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java index 1f6cb9e2bc..8f913786cb 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Sybase11Dialect.java @@ -16,6 +16,6 @@ package org.hibernate.dialect; @Deprecated public class Sybase11Dialect extends SybaseASEDialect { public Sybase11Dialect() { - super( DatabaseVersion.make( 11 ), false, false ); + super( DatabaseVersion.make( 11 ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java index 574ba3a7f5..6c8e152b4f 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE157Dialect.java @@ -17,7 +17,7 @@ package org.hibernate.dialect; public class SybaseASE157Dialect extends SybaseASEDialect { public SybaseASE157Dialect() { - super( DatabaseVersion.make( 15, 7 ), false, false ); + super( DatabaseVersion.make( 15, 7 ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java index c80035e631..23091d7683 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASE15Dialect.java @@ -17,7 +17,7 @@ package org.hibernate.dialect; public class SybaseASE15Dialect extends SybaseASEDialect { public SybaseASE15Dialect() { - super( DatabaseVersion.make( 15 ), false, false ); + super( DatabaseVersion.make( 15 ) ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java index e557f9f453..ea8595b646 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseASEDialect.java @@ -43,6 +43,7 @@ import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import jakarta.persistence.TemporalType; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; +import static org.hibernate.type.SqlTypes.*; /** * Dialect for Sybase Adaptive Server Enterprise for @@ -54,55 +55,25 @@ public class SybaseASEDialect extends SybaseDialect { private final boolean ansiNull; public SybaseASEDialect() { - this( DatabaseVersion.make( 11 ), false, false ); + this( DatabaseVersion.make( 11 ) ); } public SybaseASEDialect(DialectResolutionInfo info) { - this( - info.makeCopy(), - info.getDriverName() != null && info.getDriverName().contains( "jTDS" ), - isAnsiNull( info.unwrap( DatabaseMetaData.class ) ) - ); + this( info.makeCopy(), info ); registerKeywords( info ); } - public SybaseASEDialect(DatabaseVersion version, boolean jtdsDriver, boolean ansiNull) { - super( version, jtdsDriver ); - this.ansiNull = ansiNull; - //On Sybase ASE, the 'bit' type cannot be null, - //and cannot have indexes (while we don't use - //tinyint to store signed bytes, we can use it - //to store boolean values) - registerColumnType( Types.BOOLEAN, "tinyint" ); + public SybaseASEDialect(DatabaseVersion version) { + this(version, null); + } + protected SybaseASEDialect(DatabaseVersion version, DialectResolutionInfo info) { + super(version, info); - if ( getVersion().isSameOrAfter( 12 ) ) { - //date / date were introduced in version 12 - registerColumnType( Types.DATE, "date" ); - registerColumnType( Types.TIME, "time" ); - if ( getVersion().isSameOrAfter( 15 ) ) { - //bigint was added in version 15 - registerColumnType( Types.BIGINT, "bigint" ); - - if ( getVersion().isSameOrAfter( 15, 5 ) && !jtdsDriver ) { - //According to Wikipedia bigdatetime and bigtime were added in 15.5 - //But with jTDS we can't use them as the driver can't handle the types - registerColumnType( Types.DATE, "bigdatetime" ); - registerColumnType( Types.DATE, 3, "datetime" ); - registerColumnType( Types.TIME, "bigtime" ); - registerColumnType( Types.TIME, 3, "datetime" ); - registerColumnType( Types.TIMESTAMP, "bigdatetime" ); - registerColumnType( Types.TIMESTAMP, 3, "datetime" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "bigdatetime" ); - registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, 3, "datetime" ); - } - } - } - - registerColumnType( Types.VARBINARY, "image" ); - registerColumnType( Types.VARCHAR, "text" ); + ansiNull = info != null && isAnsiNull( info.unwrap( DatabaseMetaData.class ) ); registerSybaseKeywords(); + sizeStrategy = new SizeStrategyImpl() { @Override public Size resolveSize( @@ -123,6 +94,56 @@ public class SybaseASEDialect extends SybaseDialect { }; } + @Override + protected String columnType(int jdbcTypeCode) { + + if ( jdbcTypeCode == BIGINT && getVersion().isBefore( 15 ) ) { + // Sybase ASE didn't introduce 'bigint' until version 15.0 + return "numeric(19,0)"; + } + + if ( getVersion().isSameOrAfter( 12 ) ) { + // 'date' and 'time' were introduced in version 12 + // note: 'timestamp' is something weird on ASE, + // not what we're looking for here! + switch (jdbcTypeCode) { + case DATE: + return "date"; + case TIME: + return "time"; + } + } + + switch (jdbcTypeCode) { + case BOOLEAN: + // On Sybase ASE, the 'bit' type cannot be null, + // and cannot have indexes (while we don't use + // tinyint to store signed bytes, we can use it + // to store boolean values) + return "tinyint"; + default: + return super.columnType(jdbcTypeCode); + } + } + + @Override + protected void registerDefaultColumnTypes(DialectResolutionInfo info) { + super.registerDefaultColumnTypes(info); + + // According to Wikipedia bigdatetime and bigtime were added in 15.5 + // But with jTDS we can't use them as the driver can't handle the types + if ( getVersion().isSameOrAfter( 15, 5 ) && !jtdsDriver ) { + registerColumnType( DATE, "bigdatetime" ); + registerColumnType( DATE, 3, "datetime" ); + registerColumnType( TIME, "bigtime" ); + registerColumnType( TIME, 3, "datetime" ); + registerColumnType( TIMESTAMP, "bigdatetime" ); + registerColumnType( TIMESTAMP, 3, "datetime" ); + registerColumnType( TIMESTAMP_WITH_TIMEZONE, "bigdatetime" ); + registerColumnType( TIMESTAMP_WITH_TIMEZONE, 3, "datetime" ); + } + } + @Override public int getMaxVarcharLength() { // the maximum length of a VARCHAR or VARBINARY diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java index 59f39887d4..3455834bcc 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/SybaseDialect.java @@ -59,30 +59,39 @@ import jakarta.persistence.TemporalType; */ public class SybaseDialect extends AbstractTransactSQLDialect { - private final DatabaseVersion version; - protected final boolean jtdsDriver; + protected boolean jtdsDriver; //All Sybase dialects share an IN list size limit. private static final int PARAM_LIST_SIZE_LIMIT = 250000; - public SybaseDialect(){ - this( DatabaseVersion.make( 11, 0 ), false ); + public SybaseDialect() { + this( DatabaseVersion.make( 11, 0 ) ); } - public SybaseDialect(DialectResolutionInfo info){ - this( - info.makeCopy(), - info.getDriverName() != null && info.getDriverName().contains( "jTDS" ) - ); + public SybaseDialect(DatabaseVersion version) { + this(version, null); + } + + public SybaseDialect(DialectResolutionInfo info) { + this( info.makeCopy(), info ); registerKeywords( info ); } - public SybaseDialect(DatabaseVersion version, boolean jtdsDriver) { - super(); - this.version = version; - this.jtdsDriver = jtdsDriver; - //Sybase ASE didn't introduce bigint until version 15.0 - registerColumnType( Types.BIGINT, "numeric(19,0)" ); + protected SybaseDialect(DatabaseVersion version, DialectResolutionInfo info) { + super(version, info); + } + + @Override + protected void registerDefaultColumnTypes(DialectResolutionInfo info) { + // we need to check init the jtdsDriver field here, + // because we need it in the registerDefaultColumnTypes() + // of the subclass, which is called from the superclass + // constructor, before our constructor has been called + jtdsDriver = info != null + && info.getDriverName() != null + && info.getDriverName().contains( "jTDS" ); + + super.registerDefaultColumnTypes(info); } @Override @@ -146,11 +155,6 @@ public class SybaseDialect extends AbstractTransactSQLDialect { }; } - @Override - public DatabaseVersion getVersion() { - return version; - } - @Override public boolean supportsNullPrecedence() { return false; @@ -314,7 +318,7 @@ public class SybaseDialect extends AbstractTransactSQLDialect { @Override public NameQualifierSupport getNameQualifierSupport() { - if ( version.isSameOrAfter( 15 ) ) { + if ( getVersion().isSameOrAfter( 15 ) ) { return NameQualifierSupport.BOTH; } return NameQualifierSupport.CATALOG; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java index 38b78ace82..55882d80cd 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/TiDBDialect.java @@ -29,29 +29,30 @@ import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; */ public class TiDBDialect extends MySQLDialect { - private final int tidbVersion; + private final DatabaseVersion tidbVersion; public TiDBDialect() { - // Let's be conservative and assume people use a 4 byte character set - this( 500, 4 ); + this( DatabaseVersion.make(5, 4) ); } public TiDBDialect(DialectResolutionInfo info) { - this( - info.getDatabaseMajorVersion() * 100 + info.getDatabaseMinorVersion() * 10, - getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) ) - ); + this( info.makeCopy(), info ); registerKeywords( info ); } - public TiDBDialect(int version, int characterSetBytesPerCharacter) { + public TiDBDialect(DatabaseVersion version) { + this(version, null); + } + + protected TiDBDialect(DatabaseVersion tidbVersion, DialectResolutionInfo info) { // For simplicity’s sake, configure MySQL 5.7 compatibility - super( DatabaseVersion.make( 5, 7 ), characterSetBytesPerCharacter ); - this.tidbVersion = version; + super( DatabaseVersion.make( 5, 7 ), info ); + this.tidbVersion = tidbVersion; registerKeywords(); } - public int getTidbVersion() { + @Override + public DatabaseVersion getVersion() { return tidbVersion; }