reworked approach to Dialect column type customization

This commit is contained in:
Gavin King 2021-12-12 14:42:53 +01:00
parent 103e5c658b
commit eb3bcdb94a
20 changed files with 990 additions and 675 deletions

View File

@ -28,6 +28,8 @@
import org.hibernate.sql.ast.tree.Statement; import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation; import org.hibernate.sql.exec.spi.JdbcOperation;
import static org.hibernate.type.SqlTypes.*;
/** /**
* SQL Dialect for Sybase Anywhere * SQL Dialect for Sybase Anywhere
* (Tested on ASA 8.x) * (Tested on ASA 8.x)
@ -35,31 +37,48 @@
public class SybaseAnywhereDialect extends SybaseDialect { public class SybaseAnywhereDialect extends SybaseDialect {
public SybaseAnywhereDialect() { public SybaseAnywhereDialect() {
this( DatabaseVersion.make( 8 ), false ); this( DatabaseVersion.make( 8 ) );
}
public SybaseAnywhereDialect(DatabaseVersion version) {
this(version, null);
} }
public SybaseAnywhereDialect(DialectResolutionInfo info){ public SybaseAnywhereDialect(DialectResolutionInfo info){
this( this( info.makeCopy(), info );
info,
info.getDriverName() != null && info.getDriverName().contains( "jTDS" )
);
registerKeywords( info ); registerKeywords( info );
} }
public SybaseAnywhereDialect(DatabaseVersion version, boolean jtdsDriver) { public SybaseAnywhereDialect(DatabaseVersion version, DialectResolutionInfo info) {
super( version, jtdsDriver ); super( version, info );
}
registerColumnType( Types.BIGINT, "bigint" ); @Override
registerColumnType( Types.DATE, "date" ); protected String columnType(int jdbcTypeCode) {
registerColumnType( Types.TIME, "time" ); switch (jdbcTypeCode) {
registerColumnType( Types.TIMESTAMP, "timestamp" ); case DATE:
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp with time zone" ); return "date";
case TIME:
return "time";
case TIMESTAMP:
return "timestamp";
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp with time zone";
registerColumnType( Types.VARCHAR, "long varchar)" ); //these types hold up to 2 GB
registerColumnType( Types.NVARCHAR, "long nvarchar)" ); case LONGVARCHAR:
return "long varchar";
case LONGNVARCHAR:
return "long nvarchar";
case LONGVARBINARY:
return "long binary";
//note: 'binary' is actually a synonym for 'varbinary' case NCLOB:
registerColumnType( Types.VARBINARY, "long binary)" ); return "ntext";
default:
return super.columnType(jdbcTypeCode);
}
} }
@Override @Override
@ -150,7 +169,6 @@ public String appendLockHint(LockOptions mode, String tableName) {
} }
@Override @Override
@SuppressWarnings("deprecation")
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) { public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
return getVersion().isBefore( 10 ) return getVersion().isBefore( 10 )
? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames ) ? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames )

View File

@ -10,6 +10,7 @@
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.dialect.function.CastingConcatFunction; import org.hibernate.dialect.function.CastingConcatFunction;
import org.hibernate.dialect.function.TransactSQLStrFunction; import org.hibernate.dialect.function.TransactSQLStrFunction;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.query.NullOrdering; import org.hibernate.query.NullOrdering;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CommonFunctionFactory; import org.hibernate.dialect.function.CommonFunctionFactory;
@ -41,37 +42,51 @@
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import static org.hibernate.type.SqlTypes.*;
/** /**
* An abstract base class for Sybase and MS SQL Server dialects. * An abstract base class for Sybase and MS SQL Server dialects.
* *
* @author Gavin King * @author Gavin King
*/ */
public abstract class AbstractTransactSQLDialect extends Dialect { public abstract class AbstractTransactSQLDialect extends Dialect {
public AbstractTransactSQLDialect() {
super();
registerColumnType( Types.BOOLEAN, "bit" ); 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 //'tinyint' is an unsigned type in Sybase and
//SQL Server, holding values in the range 0-255 //SQL Server, holding values in the range 0-255
//see HHH-6779 //see HHH-6779
registerColumnType( Types.TINYINT, "smallint" ); return "smallint";
case INTEGER:
//it's called 'int' not 'integer' //it's called 'int' not 'integer'
registerColumnType( Types.INTEGER, "int" ); return "int";
//note that 'real' is double precision on SQL Server, single precision on Sybase case DATE:
//but 'float' is single precision on Sybase, double precision on SQL Server case TIME:
case TIMESTAMP:
case TIME_WITH_TIMEZONE:
return "datetime";
registerColumnType( Types.DATE, "datetime" ); case BLOB:
registerColumnType( Types.TIME, "datetime" ); return "image";
registerColumnType( Types.TIMESTAMP, "datetime" ); case CLOB:
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "datetime" ); return "text";
registerColumnType( Types.BLOB, "image" ); default:
registerColumnType( Types.CLOB, "text" ); return super.columnType(jdbcTypeCode);
}
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH );
} }
@Override @Override

View File

@ -8,11 +8,12 @@
import java.sql.DatabaseMetaData; import java.sql.DatabaseMetaData;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TimeZone; import java.util.TimeZone;
@ -55,6 +56,7 @@
import static org.hibernate.query.TemporalUnit.DAY; import static org.hibernate.query.TemporalUnit.DAY;
import static org.hibernate.query.TemporalUnit.NATIVE; 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.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
@ -70,7 +72,6 @@ public class CockroachDialect extends Dialect {
// * no support for java.sql.Clob // * no support for java.sql.Clob
private final DatabaseVersion version;
private final PostgreSQLDriverKind driverKind; private final PostgreSQLDriverKind driverKind;
public CockroachDialect() { public CockroachDialect() {
@ -88,45 +89,56 @@ public CockroachDialect(DatabaseVersion version) {
} }
public CockroachDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) { public CockroachDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) {
super(); super(version);
this.version = version;
this.driverKind = driverKind; this.driverKind = driverKind;
}
registerColumnType( Types.TINYINT, "smallint" ); //no tinyint @Override
protected List<Integer> getSupportedJdbcTypeCodes() {
List<Integer> typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() );
typeCodes.addAll( List.of(UUID, INTERVAL_SECOND, GEOMETRY, JSON) );
if ( getVersion().isSameOrAfter( 20 ) ) {
typeCodes.add(INET);
}
return typeCodes;
}
//use 'string' instead of 'varchar' @Override
registerColumnType( Types.VARCHAR, getMaxVarcharLength(), "string($l)"); protected String columnType(int jdbcTypeCode) {
registerColumnType( Types.VARCHAR, "string"); switch (jdbcTypeCode) {
case TINYINT:
return "smallint"; //no tinyint
//no binary/varbinary case CHAR:
registerColumnType( Types.VARBINARY, "bytes" ); case NCHAR:
registerColumnType( Types.BINARY, "bytes" ); case VARCHAR:
case NVARCHAR:
return "string($l)";
//no clob case NCLOB:
registerColumnType( Types.CLOB, "string" ); case CLOB:
return "string";
//no nchar/nvarchar case BINARY:
registerColumnType( Types.NCHAR, "string($l)" ); case VARBINARY:
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "string($l)" ); case BLOB:
registerColumnType( Types.NVARCHAR, "string"); return "bytes";
//no nclob case INET:
registerColumnType( Types.NCLOB, "string" ); return "inet";
case UUID:
registerColumnType( SqlTypes.UUID, "uuid" ); return "uuid";
registerColumnType( SqlTypes.INTERVAL_SECOND, "interval second($s)" ); case GEOMETRY:
return "geometry";
case INTERVAL_SECOND:
return "interval second($s)";
case JSON:
// Prefer jsonb if possible // Prefer jsonb if possible
if ( getVersion().isSameOrAfter( 20, 0 ) ) { return getVersion().isSameOrAfter( 20 ) ? "jsonb" : "json";
registerColumnType( SqlTypes.INET, "inet" ); default:
registerColumnType( SqlTypes.JSON, "jsonb" ); return super.columnType(jdbcTypeCode);
} }
else {
registerColumnType( SqlTypes.JSON, "json" );
}
registerColumnType( SqlTypes.GEOMETRY, "geometry" );
} }
@Override @Override
@ -148,21 +160,21 @@ public JdbcType resolveSqlTypeDescriptor(
int precision, int precision,
int scale, int scale,
JdbcTypeRegistry jdbcTypeRegistry) { JdbcTypeRegistry jdbcTypeRegistry) {
if ( jdbcTypeCode == SqlTypes.OTHER ) { if ( jdbcTypeCode == OTHER ) {
switch ( columnTypeName ) { switch ( columnTypeName ) {
case "uuid": case "uuid":
jdbcTypeCode = SqlTypes.UUID; jdbcTypeCode = UUID;
break; break;
case "json": case "json":
case "jsonb": case "jsonb":
jdbcTypeCode = SqlTypes.JSON; jdbcTypeCode = JSON;
break; break;
case "inet": case "inet":
jdbcTypeCode = SqlTypes.INET; jdbcTypeCode = INET;
break; break;
case "geometry": case "geometry":
case "geography": case "geography":
jdbcTypeCode = SqlTypes.GEOMETRY; jdbcTypeCode = GEOMETRY;
break; break;
} }
} }
@ -189,11 +201,6 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
} }
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public void initializeFunctionRegistry(QueryEngine queryEngine) { public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry(queryEngine); super.initializeFunctionRegistry(queryEngine);

View File

@ -59,6 +59,8 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.type.SqlTypes.*;
/** /**
* An SQL dialect for DB2. * 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_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 static final String FOR_UPDATE_SKIP_LOCKED_SQL = FOR_UPDATE_SQL + SKIP_LOCKED_SQL;
private final DatabaseVersion version;
private final LimitHandler limitHandler; private final LimitHandler limitHandler;
private final UniqueDelegate uniqueDelegate; private final UniqueDelegate uniqueDelegate;
@ -90,37 +90,7 @@ public DB2Dialect() {
} }
public DB2Dialect(DatabaseVersion version) { public DB2Dialect(DatabaseVersion version) {
super(); super(version);
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)" );
//not keywords, at least not in DB2 11, //not keywords, at least not in DB2 11,
//but perhaps they were in older versions? //but perhaps they were in older versions?
@ -142,6 +112,51 @@ public DB2Dialect(DatabaseVersion version) {
: DB2LimitHandler.INSTANCE; : 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() { protected UniqueDelegate createUniqueDelegate() {
return new DB2UniqueDelegate( this ); return new DB2UniqueDelegate( this );
} }
@ -151,11 +166,6 @@ public int getMaxVarcharLength() {
return 32_672; return 32_672;
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public int getDefaultDecimalPrecision() { public int getDefaultDecimalPrecision() {
//this is the maximum allowed in DB2 //this is the maximum allowed in DB2
@ -541,11 +551,11 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeDescriptorRegistry(); final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeDescriptorRegistry();
if ( version.isBefore( 11 ) ) { if ( getVersion().isBefore( 11 ) ) {
jdbcTypeRegistry.addDescriptor( Types.BOOLEAN, SmallIntJdbcType.INSTANCE ); 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 // 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 ); jdbcTypeRegistry.addDescriptor( Types.VARBINARY, VarbinaryJdbcType.INSTANCE_WITHOUT_LITERALS );
if ( version.isBefore( 9, 7 ) ) { if ( getVersion().isBefore( 9, 7 ) ) {
jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE ); jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE );
} }
} }

View File

@ -70,6 +70,7 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.useArgType; import static org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers.useArgType;
import static org.hibernate.type.SqlTypes.*;
/** /**
* Hibernate Dialect for Apache Derby / Cloudscape 10 * Hibernate Dialect for Apache Derby / Cloudscape 10
@ -91,8 +92,6 @@ public class DerbyDialect extends Dialect {
// * can't select a parameter unless wrapped // * can't select a parameter unless wrapped
// in a cast or function call // in a cast or function call
private final DatabaseVersion version;
private final LimitHandler limitHandler; private final LimitHandler limitHandler;
public DerbyDialect(DialectResolutionInfo info) { public DerbyDialect(DialectResolutionInfo info) {
@ -105,35 +104,7 @@ public DerbyDialect() {
} }
public DerbyDialect(DatabaseVersion version) { public DerbyDialect(DatabaseVersion version) {
super(); super(version);
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)" );
registerDerbyKeywords(); registerDerbyKeywords();
@ -144,6 +115,52 @@ public DerbyDialect(DatabaseVersion version) {
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, NO_BATCH ); 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 @Override
public int getMaxVarcharLength() { public int getMaxVarcharLength() {
return 32_672; return 32_672;
@ -173,11 +190,6 @@ public int getPreferredSqlTypeCodeForBoolean() {
: Types.BOOLEAN; : Types.BOOLEAN;
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public NationalizationSupport getNationalizationSupport() { public NationalizationSupport getNationalizationSupport() {
return NationalizationSupport.IMPLICIT; return NationalizationSupport.IMPLICIT;

View File

@ -163,6 +163,7 @@
import static java.lang.Math.ceil; import static java.lang.Math.ceil;
import static java.lang.Math.log; 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_END;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE; import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME; 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 SizeStrategy sizeStrategy;
private final DatabaseVersion version;
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @deprecated provide a {@link DatabaseVersion}
*/
@Deprecated
protected Dialect() { 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 ); uniqueDelegate = new DefaultUniqueDelegate( this );
sizeStrategy = new SizeStrategyImpl(); sizeStrategy = new SizeStrategyImpl();
if (autoRegisterColumnTypes) { registerDefaultColumnTypes(info); // pass the info back down to the subclass in case it needs it (MySQL)
registerDefaultColumnTypes();
}
registerHibernateTypes(); registerHibernateTypes();
registerDefaultKeywords(); 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() ); registerDefaultColumnTypes( getMaxVarcharLength(), getMaxNVarcharLength(), getMaxVarbinaryLength() );
} }
@ -249,55 +264,181 @@ protected void registerDefaultColumnTypes() {
* {@code Dialect} by calling {@link #registerColumnType(int,String)} * {@code Dialect} by calling {@link #registerColumnType(int,String)}
* from the constructor. * from the constructor.
* <p> * <p>
* 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.
* <p>
* The "long" types {@link Types#LONGVARCHAR}, {@link Types#LONGNVARCHAR}
* and {@link Types#LONGVARBINARY} are considered synonyms for their * and {@link Types#LONGVARBINARY} are considered synonyms for their
* non-{@code LONG} counterparts, with the only difference being that * non-{@code LONG} counterparts, with the only difference being that
* a different default length is used: {@link org.hibernate.Length#LONG} * a different default length is used: {@link org.hibernate.Length#LONG}
* instead of {@link org.hibernate.Length#DEFAULT}. Concrete dialects * instead of {@link org.hibernate.Length#DEFAULT}.
* should usually avoid registering mappings for these JDBC types. * <p>
* 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 maxVarcharLength the maximum length of the {@link Types#VARCHAR} type
* @param maxNVarcharLength the maximum length of the {@link Types#NVARCHAR} type * @param maxNVarcharLength the maximum length of the {@link Types#NVARCHAR} type
* @param maxVarBinaryLength the maximum length of the {@link Types#VARBINARY} type * @param maxVarBinaryLength the maximum length of the {@link Types#VARBINARY} type
*/ */
protected void registerDefaultColumnTypes(int maxVarcharLength, int maxNVarcharLength, int maxVarBinaryLength) { 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" ); * A list of JDBC types that we expect to be supported on all databases.
registerColumnType( Types.INTEGER, "integer" ); */
registerColumnType( Types.BIGINT, "bigint" ); private static final List<Integer> 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)" ); * The JDBC type codes of types supported by this SQL dialect, from the lists
registerColumnType( Types.DOUBLE, "double precision" ); * defined by {@link Types} and {@link SqlTypes}.
* <p>
* 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.
* <p>
* 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<Integer> getSupportedJdbcTypeCodes() {
return ANSI_SQL_TYPES;
}
//these are pretty much synonyms, but are considered /**
//separate types by the ANSI spec, and in some dialects * The column type name for a given JDBC type code defined in {@link Types} or
registerColumnType( Types.NUMERIC, "numeric($p,$s)" ); * {@link SqlTypes}. This default implementation returns the ANSI-standard type
registerColumnType( Types.DECIMAL, "decimal($p,$s)" ); * name.
* <p>
* 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" ); case TINYINT:
registerColumnType( Types.TIME, "time" ); return "tinyint";
registerColumnType( Types.TIMESTAMP, "timestamp($p)" ); case SMALLINT:
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp($p) with time zone" ); return "smallint";
case INTEGER:
return "integer";
case BIGINT:
return "bigint";
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";
// 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)";
case DATE:
return "date";
case TIME:
return "time";
case TIME_WITH_TIMEZONE:
// type included here for completeness but note that // type included here for completeness but note that
// very few databases support it, and the general // very few databases support it, and the general
// advice is to caution against its use (for reasons, // advice is to caution against its use (for reasons,
// check the comments in the Postgres documentation). // check the comments in the Postgres documentation).
registerColumnType( Types.TIME_WITH_TIMEZONE, "time with time zone" ); return "time with time zone";
case TIMESTAMP:
return "timestamp($p)";
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp($p) with time zone";
registerColumnType( Types.BINARY, "binary($l)" ); case CHAR:
registerColumnType( Types.VARBINARY, maxVarBinaryLength, "varbinary($l)" ); return "char($l)";
registerColumnType( Types.BLOB, "blob" ); case VARCHAR:
return "varchar($l)";
case CLOB:
return "clob";
registerColumnType( Types.CHAR, "char($l)" ); case NCHAR:
registerColumnType( Types.VARCHAR, maxVarcharLength, "varchar($l)" ); return "nchar($l)";
registerColumnType( Types.CLOB, "clob" ); case NVARCHAR:
return "nvarchar($l)";
case NCLOB:
return "nclob";
registerColumnType( Types.NCHAR, "nchar($l)" ); case BINARY:
registerColumnType( Types.NVARCHAR, maxNVarcharLength, "nvarchar($l)" ); return "binary($l)";
registerColumnType( Types.NCLOB, "nclob" ); 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() { protected void registerHibernateTypes() {
@ -353,7 +494,9 @@ protected void registerKeywords(DialectResolutionInfo info) {
} }
} }
public abstract DatabaseVersion getVersion(); public DatabaseVersion getVersion() {
return version;
}
public JdbcType resolveSqlTypeDescriptor( public JdbcType resolveSqlTypeDescriptor(
String columnTypeName, String columnTypeName,

View File

@ -7,6 +7,8 @@
package org.hibernate.dialect; package org.hibernate.dialect;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.PessimisticLockException; import org.hibernate.PessimisticLockException;
import org.hibernate.boot.model.TypeContributions; import org.hibernate.boot.model.TypeContributions;
@ -63,6 +65,7 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.query.TemporalUnit.SECOND; import static org.hibernate.query.TemporalUnit.SECOND;
import static org.hibernate.type.SqlTypes.*;
/** /**
* A dialect compatible with the H2 database. * A dialect compatible with the H2 database.
@ -77,8 +80,6 @@ public class H2Dialect extends Dialect {
private final boolean cascadeConstraints; private final boolean cascadeConstraints;
private final boolean useLocalTime; private final boolean useLocalTime;
private final DatabaseVersion version;
private final boolean supportsTuplesInSubqueries; private final boolean supportsTuplesInSubqueries;
private final SequenceInformationExtractor sequenceInformationExtractor; private final SequenceInformationExtractor sequenceInformationExtractor;
private final String querySequenceString; private final String querySequenceString;
@ -88,27 +89,13 @@ public H2Dialect(DialectResolutionInfo info) {
registerKeywords( info ); 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() { public H2Dialect() {
this( SimpleDatabaseVersion.ZERO_VERSION ); this( SimpleDatabaseVersion.ZERO_VERSION );
} }
public H2Dialect(DatabaseVersion version) { public H2Dialect(DatabaseVersion version) {
super(); super(version);
this.version = version;
// https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440 // https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440
limitHandler = version.isSameOrAfter( 1, 4, 195 ) limitHandler = version.isSameOrAfter( 1, 4, 195 )
? OffsetFetchLimitHandler.INSTANCE ? OffsetFetchLimitHandler.INSTANCE
@ -128,12 +115,6 @@ public H2Dialect(DatabaseVersion version) {
// http://code.google.com/p/h2database/issues/detail?id=235 // http://code.google.com/p/h2database/issues/detail?id=235
getDefaultProperties().setProperty( AvailableSettings.NON_CONTEXTUAL_LOB_CREATION, "true" ); 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 ) ) { if ( version.isSameOrAfter( 1, 4, 32 ) ) {
this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 ) this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 )
? SequenceInformationExtractorLegacyImpl.INSTANCE ? SequenceInformationExtractorLegacyImpl.INSTANCE
@ -151,12 +132,49 @@ public H2Dialect(DatabaseVersion version) {
else { else {
this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE; this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE;
this.querySequenceString = null; this.querySequenceString = null;
if ( version.isBefore( 2 ) ) { }
}
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, // prior to version 2.0, H2 reported NUMERIC columns as DECIMAL,
// which caused problems for schema update tool // which caused problems for schema update tool
registerColumnType( Types.NUMERIC, "decimal($p,$s)" ); 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<Integer> getSupportedJdbcTypeCodes() {
List<Integer> typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() );
typeCodes.add(ARRAY);
return typeCodes;
} }
@Override @Override
@ -166,11 +184,11 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration() final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
.getJdbcTypeDescriptorRegistry(); .getJdbcTypeDescriptorRegistry();
if ( version.isSameOrAfter( 1, 4, 197 ) ) { if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE ); 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 boolean hasOddDstBehavior() {
return getVersion().isSame( 1, 4, 200 ); return getVersion().isSame( 1, 4, 200 );
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public void initializeFunctionRegistry(QueryEngine queryEngine) { public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry( queryEngine ); super.initializeFunctionRegistry( queryEngine );
@ -235,7 +248,7 @@ public void initializeFunctionRegistry(QueryEngine queryEngine) {
CommonFunctionFactory.median( queryEngine ); CommonFunctionFactory.median( queryEngine );
CommonFunctionFactory.stddevPopSamp( queryEngine ); CommonFunctionFactory.stddevPopSamp( queryEngine );
CommonFunctionFactory.varPopSamp( queryEngine ); CommonFunctionFactory.varPopSamp( queryEngine );
if ( version.isSame( 1, 4, 200 ) ) { if ( getVersion().isSame( 1, 4, 200 ) ) {
// See https://github.com/h2database/h2database/issues/2518 // See https://github.com/h2database/h2database/issues/2518
CommonFunctionFactory.format_toChar( queryEngine ); CommonFunctionFactory.format_toChar( queryEngine );
} }
@ -524,7 +537,7 @@ public String getQueryHintString(String query, String hints) {
@Override @Override
public void appendDatetimeFormat(SqlAppender appender, String format) { 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 // See https://github.com/h2database/h2database/issues/2518
appender.appendSql( OracleDialect.datetimeFormat( format, true, true ).result() ); appender.appendSql( OracleDialect.datetimeFormat( format, true, true ).result() );
} }

View File

@ -71,6 +71,10 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; 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. * An SQL dialect compatible with HyperSQL (HSQLDB) version 1.8 and above.
@ -85,11 +89,6 @@ public class HSQLDialect extends Dialect {
HSQLDialect.class.getName() HSQLDialect.class.getName()
); );
/**
* version is 180 for 1.8.0 or 200 for 2.0.0
*/
private final DatabaseVersion version;
public HSQLDialect(DialectResolutionInfo info) { public HSQLDialect(DialectResolutionInfo info) {
this( info.makeCopy() ); this( info.makeCopy() );
registerKeywords( info ); registerKeywords( info );
@ -100,41 +99,48 @@ public HSQLDialect() {
} }
public HSQLDialect(DatabaseVersion version) { public HSQLDialect(DatabaseVersion version) {
super(); super( version.isSame( 1, 8 ) ? reflectedVersion( version ) : version );
if ( version.isSame( 1, 8 ) ) { if ( getVersion().isSameOrAfter( 2, 5 ) ) {
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 ) ) {
registerKeyword( "period" ); registerKeyword( "period" );
} }
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); 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) { private static DatabaseVersion reflectedVersion(DatabaseVersion version) {
try { try {
final Class<?> props = ReflectHelper.classForName("org.hsqldb.persist.HsqlDatabaseProperties"); final Class<?> props = ReflectHelper.classForName("org.hsqldb.persist.HsqlDatabaseProperties");
@ -152,11 +158,6 @@ private static DatabaseVersion reflectedVersion(DatabaseVersion version) {
} }
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public void initializeFunctionRegistry(QueryEngine queryEngine) { public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry( queryEngine ); super.initializeFunctionRegistry( queryEngine );
@ -208,13 +209,13 @@ public void initializeFunctionRegistry(QueryEngine queryEngine) {
CommonFunctionFactory.addMonths( queryEngine ); CommonFunctionFactory.addMonths( queryEngine );
CommonFunctionFactory.monthsBetween( 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 //SYSDATE is similar to LOCALTIMESTAMP but it returns the timestamp when it is called
CommonFunctionFactory.sysdate( queryEngine ); CommonFunctionFactory.sysdate( queryEngine );
} }
// from v. 2.2.0 ROWNUM() is supported in all modes as the equivalent of Oracle ROWNUM // 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 ); CommonFunctionFactory.rownum( queryEngine );
} }
} }
@ -357,7 +358,7 @@ public boolean supportsLockTimeouts() {
@Override @Override
public String getForUpdateString() { public String getForUpdateString() {
if ( version.isSameOrAfter( 2 ) ) { if ( getVersion().isSameOrAfter( 2 ) ) {
return " for update"; return " for update";
} }
else { else {
@ -367,8 +368,8 @@ public String getForUpdateString() {
@Override @Override
public LimitHandler getLimitHandler() { public LimitHandler getLimitHandler() {
return version.isBefore( 2 ) ? LegacyHSQLLimitHandler.INSTANCE return getVersion().isBefore( 2 ) ? LegacyHSQLLimitHandler.INSTANCE
: version.isBefore( 2, 5 ) ? LimitOffsetLimitHandler.INSTANCE : getVersion().isBefore( 2, 5 ) ? LimitOffsetLimitHandler.INSTANCE
: OffsetFetchLimitHandler.INSTANCE; : OffsetFetchLimitHandler.INSTANCE;
} }
@ -387,7 +388,7 @@ public boolean supportsIfExistsBeforeTableName() {
@Override @Override
public boolean supportsColumnCheck() { public boolean supportsColumnCheck() {
return version.isSameOrAfter( 2 ); return getVersion().isSameOrAfter( 2 );
} }
@Override @Override
@ -408,7 +409,7 @@ public SequenceInformationExtractor getSequenceInformationExtractor() {
@Override @Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() { 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 = private static final ViolatedConstraintNameExtractor EXTRACTOR_18 =
@ -518,7 +519,7 @@ public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
// the definition and data is private to the session and table declaration // the definition and data is private to the session and table declaration
// can happen in the middle of a transaction // can happen in the middle of a transaction
if ( version.isBefore( 2 ) ) { if ( getVersion().isBefore( 2 ) ) {
return new GlobalTemporaryTableMutationStrategy( return new GlobalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable( TemporaryTable.createIdTable(
rootEntityDescriptor, rootEntityDescriptor,
@ -558,7 +559,7 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
// the definition and data is private to the session and table declaration // the definition and data is private to the session and table declaration
// can happen in the middle of a transaction // can happen in the middle of a transaction
if ( version.isBefore( 2 ) ) { if ( getVersion().isBefore( 2 ) ) {
return new GlobalTemporaryTableInsertStrategy( return new GlobalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable( TemporaryTable.createEntityTable(
rootEntityDescriptor, rootEntityDescriptor,
@ -586,24 +587,24 @@ public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
@Override @Override
public TemporaryTableKind getSupportedTemporaryTableKind() { public TemporaryTableKind getSupportedTemporaryTableKind() {
return version.isBefore( 2 ) ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL; return getVersion().isBefore( 2 ) ? TemporaryTableKind.GLOBAL : TemporaryTableKind.LOCAL;
} }
@Override @Override
public String getTemporaryTableCreateCommand() { public String getTemporaryTableCreateCommand() {
return version.isBefore( 2 ) ? super.getTemporaryTableCreateCommand() : "declare local temporary table"; return getVersion().isBefore( 2 ) ? super.getTemporaryTableCreateCommand() : "declare local temporary table";
} }
@Override @Override
public AfterUseAction getTemporaryTableAfterUseAction() { public AfterUseAction getTemporaryTableAfterUseAction() {
// Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end // Version 1.8 GLOBAL TEMPORARY table definitions persist beyond the end
// of the session (by default, data is cleared at commit). // 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 @Override
public BeforeUseAction getTemporaryTableBeforeUseAction() { public BeforeUseAction getTemporaryTableBeforeUseAction() {
return version.isBefore( 2 ) ? BeforeUseAction.NONE : BeforeUseAction.CREATE; return getVersion().isBefore( 2 ) ? BeforeUseAction.NONE : BeforeUseAction.CREATE;
} }
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -651,7 +652,7 @@ public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode)
case OPTIMISTIC_FORCE_INCREMENT: case OPTIMISTIC_FORCE_INCREMENT:
return new OptimisticForceIncrementLockingStrategy(lockable, lockMode); return new OptimisticForceIncrementLockingStrategy(lockable, lockMode);
} }
if ( version.isBefore( 2 ) ) { if ( getVersion().isBefore( 2 ) ) {
return new ReadUncommittedLockingStrategy( lockable, lockMode ); return new ReadUncommittedLockingStrategy( lockable, lockMode );
} }
else { else {
@ -676,19 +677,19 @@ public void lock(Object id, Object version, Object object, int timeout, SharedSe
@Override @Override
public boolean supportsCommentOn() { public boolean supportsCommentOn() {
return version.isSameOrAfter( 2 ); return getVersion().isSameOrAfter( 2 );
} }
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override @Override
public boolean doesReadCommittedCauseWritersToBlockReaders() { public boolean doesReadCommittedCauseWritersToBlockReaders() {
return version.isSameOrAfter( 2 ); return getVersion().isSameOrAfter( 2 );
} }
@Override @Override
public boolean doesRepeatableReadCauseReadersToBlockWriters() { public boolean doesRepeatableReadCauseReadersToBlockWriters() {
return version.isSameOrAfter( 2 ); return getVersion().isSameOrAfter( 2 );
} }
@Override @Override
@ -709,7 +710,7 @@ public boolean supportsTupleCounts() {
@Override @Override
public boolean supportsTupleDistinctCounts() { public boolean supportsTupleDistinctCounts() {
// from v. 2.2.9 is added support for COUNT(DISTINCT ...) with multiple arguments // 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 @Override
@ -724,7 +725,7 @@ public boolean requiresFloatCastingOfIntegerDivision() {
@Override @Override
public IdentityColumnSupport getIdentityColumnSupport() { public IdentityColumnSupport getIdentityColumnSupport() {
return new HSQLIdentityColumnSupport( this.version ); return new HSQLIdentityColumnSupport( this.getVersion() );
} }
@Override @Override

View File

@ -34,35 +34,34 @@
*/ */
public class MariaDBDialect extends MySQLDialect { public class MariaDBDialect extends MySQLDialect {
private final DatabaseVersion version; private final DatabaseVersion mariaVersion;
public MariaDBDialect() { public MariaDBDialect() {
this( DatabaseVersion.make( 5 ) ); this( DatabaseVersion.make( 5 ) );
} }
public MariaDBDialect(DialectResolutionInfo info) { public MariaDBDialect(DialectResolutionInfo info) {
this( info.makeCopy(), getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) ) ); this( info.makeCopy(), info );
registerKeywords( info ); registerKeywords( info );
} }
public MariaDBDialect(DatabaseVersion version) { public MariaDBDialect(DatabaseVersion version) {
// Let's be conservative and assume people use a 4 byte character set this(version, null);
this( version, 4 );
} }
public MariaDBDialect(DatabaseVersion version, int characterSetBytesPerCharacter) { protected MariaDBDialect(DatabaseVersion mariaVersion, DialectResolutionInfo info) {
super( super(
version.isBefore( 5, 3 ) mariaVersion.isBefore( 5, 3 )
? DatabaseVersion.make( 5 ) ? DatabaseVersion.make( 5 )
: DatabaseVersion.make( 5, 7 ), : DatabaseVersion.make( 5, 7 ),
characterSetBytesPerCharacter info
); );
this.version = version; this.mariaVersion = mariaVersion;
} }
@Override @Override
public DatabaseVersion getVersion() { public DatabaseVersion getVersion() {
return version; return mariaVersion;
} }
@Override @Override

View File

@ -11,6 +11,8 @@
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.LockOptions; import org.hibernate.LockOptions;
import org.hibernate.PessimisticLockException; import org.hibernate.PessimisticLockException;
@ -76,6 +78,7 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.*;
/** /**
* An SQL dialect for MySQL (prior to 5.x). * An SQL dialect for MySQL (prior to 5.x).
@ -86,12 +89,12 @@ public class MySQLDialect extends Dialect {
private final UniqueDelegate uniqueDelegate; private final UniqueDelegate uniqueDelegate;
private final MySQLStorageEngine storageEngine; private final MySQLStorageEngine storageEngine;
private final DatabaseVersion version;
private final int characterSetBytesPerCharacter;
private final SizeStrategy sizeStrategy; private final SizeStrategy sizeStrategy;
private int maxVarcharLength;
private int maxVarbinaryLength;
public MySQLDialect(DialectResolutionInfo info) { public MySQLDialect(DialectResolutionInfo info) {
this( info.makeCopy(), getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) ) ); this( info.makeCopy(), info );
registerKeywords( info ); registerKeywords( info );
} }
@ -100,16 +103,11 @@ public MySQLDialect() {
} }
public MySQLDialect(DatabaseVersion version) { public MySQLDialect(DatabaseVersion version) {
// Let's be conservative and assume people use a 4 byte character set this(version, null);
this( version, 4 );
} }
public MySQLDialect(DatabaseVersion version, int characterSetBytesPerCharacter) { protected MySQLDialect(DatabaseVersion mySQLVersion, DialectResolutionInfo info) {
super(false); super(mySQLVersion, info);
this.version = version;
this.characterSetBytesPerCharacter = characterSetBytesPerCharacter;
registerDefaultColumnTypes();
String storageEngine = Environment.getProperties().getProperty( Environment.STORAGE_ENGINE ); String storageEngine = Environment.getProperties().getProperty( Environment.STORAGE_ENGINE );
if (storageEngine == null) { if (storageEngine == null) {
@ -128,72 +126,13 @@ else if( "myisam".equalsIgnoreCase( storageEngine ) ) {
throw new UnsupportedOperationException( "The " + storageEngine + " storage engine is not supported!" ); 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" ); registerKeyword( "key" );
getDefaultProperties().setProperty( Environment.MAX_FETCH_DEPTH, "2" ); getDefaultProperties().setProperty( Environment.MAX_FETCH_DEPTH, "2" );
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
uniqueDelegate = new MySQLUniqueDelegate( this ); uniqueDelegate = new MySQLUniqueDelegate( this );
sizeStrategy = new SizeStrategyImpl() { sizeStrategy = new SizeStrategyImpl() {
@Override @Override
public Size resolveSize( public Size resolveSize(
@ -204,7 +143,7 @@ public Size resolveSize(
Long length) { Long length) {
switch ( jdbcType.getDefaultSqlTypeCode() ) { switch ( jdbcType.getDefaultSqlTypeCode() ) {
case Types.BIT: 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 ) { if ( length != null ) {
return Size.length( Math.min( Math.max( length, 1 ), 64 ) ); return Size.length( Math.min( Math.max( length, 1 ), 64 ) );
} }
@ -214,6 +153,101 @@ public Size resolveSize(
}; };
} }
@Override
protected List<Integer> getSupportedJdbcTypeCodes() {
List<Integer> 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) { protected static int getCharacterSetBytesPerCharacter(DatabaseMetaData databaseMetaData) {
if ( databaseMetaData != null ) { if ( databaseMetaData != null ) {
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) { try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {
@ -254,14 +288,17 @@ protected static int getCharacterSetBytesPerCharacter(DatabaseMetaData databaseM
return 4; return 4;
} }
@Override private int maxVarbinaryLength() {
public int getMaxVarcharLength() { return getMySQLVersion().isBefore( 5 ) ? 255 : 65_535;
}
private int maxVarcharLength(int bytesPerCharacter) {
// max length for VARCHAR changed in 5.0.3 // max length for VARCHAR changed in 5.0.3
if ( getMySQLVersion().isBefore( 5 ) ) { if ( getMySQLVersion().isBefore( 5 ) ) {
return 255; return 255;
} }
else { else {
switch ( characterSetBytesPerCharacter ) { switch ( bytesPerCharacter ) {
case 1: case 1:
return 65_535; return 65_535;
case 2: case 2:
@ -275,9 +312,14 @@ public int getMaxVarcharLength() {
} }
} }
@Override
public int getMaxVarcharLength() {
return maxVarcharLength;
}
@Override @Override
public int getMaxVarbinaryLength() { public int getMaxVarbinaryLength() {
return getMySQLVersion().isBefore( 5 ) ? 255 : 65_535; return maxVarbinaryLength;
} }
@Override @Override
@ -290,13 +332,8 @@ public String getNullColumnString(String columnType) {
return super.getNullColumnString( columnType ); return super.getNullColumnString( columnType );
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
public DatabaseVersion getMySQLVersion() { public DatabaseVersion getMySQLVersion() {
return version; return super.getVersion();
} }
@Override @Override

View File

@ -10,6 +10,8 @@
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -68,7 +70,6 @@
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor; import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.JavaObjectType; import org.hibernate.type.JavaObjectType;
import org.hibernate.type.NullType; import org.hibernate.type.NullType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType; import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
@ -86,6 +87,7 @@
import static org.hibernate.query.TemporalUnit.MONTH; import static org.hibernate.query.TemporalUnit.MONTH;
import static org.hibernate.query.TemporalUnit.SECOND; import static org.hibernate.query.TemporalUnit.SECOND;
import static org.hibernate.query.TemporalUnit.YEAR; import static org.hibernate.query.TemporalUnit.YEAR;
import static org.hibernate.type.SqlTypes.*;
/** /**
* A dialect for Oracle 8i and above. * 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"; public static final String PREFER_LONG_RAW = "hibernate.dialect.oracle.prefer_long_raw";
private final LimitHandler limitHandler; private final LimitHandler limitHandler;
private final DatabaseVersion version;
public OracleDialect(DialectResolutionInfo info) { public OracleDialect(DialectResolutionInfo info) {
this( info.makeCopy() ); this( info.makeCopy() );
@ -117,15 +118,8 @@ public OracleDialect() {
} }
public OracleDialect(DatabaseVersion version) { public OracleDialect(DatabaseVersion version) {
super(); super(version);
this.version = version;
registerCharacterTypeMappings();
registerNumericTypeMappings();
registerDateTimeTypeMappings();
registerBinaryTypeMappings();
registerExtendedTypeMappings();
registerReverseHibernateTypeMappings();
registerDefaultProperties(); registerDefaultProperties();
limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY ) limitHandler = supportsFetchClause( FetchClauseType.ROWS_ONLY )
@ -133,11 +127,6 @@ public OracleDialect(DatabaseVersion version) {
: new LegacyOracleLimitHandler( getVersion() ); : new LegacyOracleLimitHandler( getVersion() );
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public int getPreferredSqlTypeCodeForBoolean() { public int getPreferredSqlTypeCodeForBoolean() {
return Types.BIT; return Types.BIT;
@ -536,52 +525,69 @@ private void extractField(
pattern.append( unit.conversionFactor( toUnit, this ) ); pattern.append( unit.conversionFactor( toUnit, this ) );
} }
protected void registerCharacterTypeMappings() { @Override
if ( getVersion().isBefore( 9 ) ) { protected String columnType(int jdbcTypeCode) {
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" );
}
//note: the 'long' type is deprecated //note: the 'long' type is deprecated
} switch (jdbcTypeCode) {
case BOOLEAN:
// still, after all these years...
return "number(1,0)";
protected void registerNumericTypeMappings() { case VARCHAR:
registerColumnType( Types.BOOLEAN, "number(1,0)" ); return getVersion().isBefore( 9 )
? "varchar2($l)": "varchar2($l char)";
case NVARCHAR:
return "nvarchar2($l)";
registerColumnType( Types.BIGINT, "number(19,0)" ); case BIGINT:
registerColumnType( Types.SMALLINT, "number(5,0)" ); return "number(19,0)";
registerColumnType( Types.TINYINT, "number(3,0)" ); case SMALLINT:
registerColumnType( Types.INTEGER, "number(10,0)" ); return "number(5,0)";
case TINYINT:
return "number(3,0)";
case INTEGER:
return "number(10,0)";
// Oracle has DOUBLE semantics for the REAL type, so we map it to float(24) case REAL:
registerColumnType( Types.REAL, "float(24)" ); // Oracle's 'real' type is actually double precision
return "float(24)";
case NUMERIC:
case DECIMAL:
// Note that 38 is the maximum precision Oracle supports // Note that 38 is the maximum precision Oracle supports
registerColumnType( Types.NUMERIC, "number($p,$s)" ); return "number($p,$s)";
registerColumnType( Types.DECIMAL, "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 registerDateTimeTypeMappings() { @Override
if ( getVersion().isBefore( 9 ) ) { protected List<Integer> getSupportedJdbcTypeCodes() {
registerColumnType( Types.DATE, "date" ); List<Integer> list = new ArrayList<>( super.getSupportedJdbcTypeCodes() );
registerColumnType( Types.TIME, "date" ); if ( getVersion().isSameOrAfter( 10 ) ) {
registerColumnType( Types.TIMESTAMP, "date" ); list.add(GEOMETRY);
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" );
} }
return list;
} }
@Override @Override
@ -589,23 +595,6 @@ public TimeZoneSupport getTimeZoneSupport() {
return getVersion().isSameOrAfter( 9 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; 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() { protected void registerDefaultProperties() {
getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "true" ); getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "true" );
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );

View File

@ -12,6 +12,7 @@
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Types; import java.sql.Types;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
@ -85,6 +86,7 @@
import static org.hibernate.query.TemporalUnit.MONTH; import static org.hibernate.query.TemporalUnit.MONTH;
import static org.hibernate.query.TemporalUnit.QUARTER; import static org.hibernate.query.TemporalUnit.QUARTER;
import static org.hibernate.query.TemporalUnit.YEAR; 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.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; 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 static final PostgreSQLIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new PostgreSQLIdentityColumnSupport();
private final DatabaseVersion version;
private final PostgreSQLDriverKind driverKind; private final PostgreSQLDriverKind driverKind;
public PostgreSQLDialect(DialectResolutionInfo info) { public PostgreSQLDialect(DialectResolutionInfo info) {
@ -116,51 +117,75 @@ public PostgreSQLDialect(DatabaseVersion version) {
} }
public PostgreSQLDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) { public PostgreSQLDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) {
super(); super(version);
this.version = version;
this.driverKind = driverKind; this.driverKind = driverKind;
registerColumnType( Types.TINYINT, "smallint" ); //no tinyint, not even in Postgres 11 getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" );
}
registerColumnType( Types.VARBINARY, "bytea" ); @Override
registerColumnType( Types.BINARY, "bytea" ); protected String columnType(int jdbcTypeCode) {
switch (jdbcTypeCode) {
case TINYINT:
// no tinyint, not even in Postgres 11
return "smallint";
//use oid as the blob type on Postgres because case BINARY:
//the JDBC driver is rubbish return "bytea";
registerColumnType( Types.BLOB, "oid" );
registerColumnType( Types.CLOB, "oid" );
//there are no nchar/nvarchar types in Postgres case BLOB:
registerColumnType( Types.NCHAR, "char($l)" ); case CLOB:
registerColumnType( Types.NVARCHAR, getMaxNVarcharLength(), "varchar($l)" ); // use oid as the blob type on Postgres because
registerColumnType( Types.NVARCHAR, "text" ); // 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, // since there's no real difference between TEXT and VARCHAR,
// except for the length limit, we can just use 'text' for the // except for the length limit, we can just use 'text' for the
// "long" string types // "long" string types
registerColumnType( Types.VARCHAR, "text" ); 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";
registerColumnType( SqlTypes.INET, "inet" ); case INET:
registerColumnType( SqlTypes.INTERVAL_SECOND, "interval second($s)" ); return "inet";
case UUID:
if ( getVersion().isSameOrAfter( 8, 2 ) ) { return "uuid";
registerColumnType( SqlTypes.UUID, "uuid" ); case GEOMETRY:
return "geometry";
if ( getVersion().isSameOrAfter( 9, 2 ) ) { case JSON:
// Prefer jsonb if possible // Prefer jsonb if possible
if ( getVersion().isSameOrAfter( 9, 4 ) ) { return getVersion().isSameOrAfter( 9, 4 )
registerColumnType( SqlTypes.JSON, "jsonb" ); ? "jsonb" : "json";
} case INTERVAL_SECOND:
else { return "interval second($s)";
registerColumnType( SqlTypes.JSON, "json" );
} default:
return super.columnType(jdbcTypeCode);
} }
} }
registerColumnType( SqlTypes.GEOMETRY, "geometry" ); @Override
protected List<Integer> getSupportedJdbcTypeCodes() {
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE ); List<Integer> typeCodes = new ArrayList<>( super.getSupportedJdbcTypeCodes() );
getDefaultProperties().setProperty( Environment.NON_CONTEXTUAL_LOB_CREATION, "true" ); 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 @Override
@ -171,17 +196,17 @@ public int getMaxVarcharLength() {
@Override @Override
public int getMaxVarbinaryLength() { public int getMaxVarbinaryLength() {
//postgres has no varbinary-like type //postgres has no varbinary-like type
return 0; return -1;
} }
@Override @Override
public String getTypeName(int code, Size size) throws HibernateException { 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 // The maximum scale for `interval second` is 6 unfortunately so we have to use numeric by default
switch ( code ) { switch ( code ) {
case SqlTypes.INTERVAL_SECOND: case INTERVAL_SECOND:
final Integer scale = size.getScale(); final Integer scale = size.getScale();
if ( scale == null || scale > 6 ) { if ( scale == null || scale > 6 ) {
return getTypeName( SqlTypes.NUMERIC, size ); return getTypeName( NUMERIC, size );
} }
} }
return super.getTypeName( code, size ); return super.getTypeName( code, size );
@ -194,32 +219,27 @@ public JdbcType resolveSqlTypeDescriptor(
int precision, int precision,
int scale, int scale,
JdbcTypeRegistry jdbcTypeRegistry) { JdbcTypeRegistry jdbcTypeRegistry) {
if ( jdbcTypeCode == SqlTypes.OTHER ) { if ( jdbcTypeCode == OTHER ) {
switch ( columnTypeName ) { switch ( columnTypeName ) {
case "uuid": case "uuid":
jdbcTypeCode = SqlTypes.UUID; jdbcTypeCode = UUID;
break; break;
case "json": case "json":
case "jsonb": case "jsonb":
jdbcTypeCode = SqlTypes.JSON; jdbcTypeCode = JSON;
break; break;
case "inet": case "inet":
jdbcTypeCode = SqlTypes.INET; jdbcTypeCode = INET;
break; break;
case "geometry": case "geometry":
case "geography": case "geography":
jdbcTypeCode = SqlTypes.GEOMETRY; jdbcTypeCode = GEOMETRY;
break; break;
} }
} }
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode ); return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public String currentTime() { public String currentTime() {
return "localtime"; return "localtime";

View File

@ -50,7 +50,6 @@
import org.hibernate.tool.schema.spi.Exporter; import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.type.BasicType; import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry; import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor; import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaTypeDescriptor;
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType; import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
@ -60,13 +59,16 @@
import java.sql.Types; import java.sql.Types;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.ArrayList;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.TimeZone; import java.util.TimeZone;
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.query.TemporalUnit.NANOSECOND; 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.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros; import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
@ -79,12 +81,10 @@
public class SQLServerDialect extends AbstractTransactSQLDialect { public class SQLServerDialect extends AbstractTransactSQLDialect {
private static final int PARAM_LIST_SIZE_LIMIT = 2100; private static final int PARAM_LIST_SIZE_LIMIT = 2100;
private final DatabaseVersion version;
private StandardSequenceExporter exporter; private StandardSequenceExporter exporter;
public SQLServerDialect(DialectResolutionInfo info) { public SQLServerDialect(DialectResolutionInfo info) {
this( info.makeCopy() ); this( info.makeCopy(), info );
registerKeywords( info ); registerKeywords( info );
} }
@ -93,53 +93,71 @@ public SQLServerDialect() {
} }
public SQLServerDialect(DatabaseVersion version) { public SQLServerDialect(DatabaseVersion version) {
super(); this(version, null);
this.version = version;
//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 ) ) { if ( getVersion().isSameOrAfter( 11 ) ) {
exporter = new SqlServerSequenceExporter( this ); exporter = new SqlServerSequenceExporter( this );
} }
if ( getVersion().isBefore( 9 ) ) { registerKeyword( "top" );
registerColumnType( Types.VARBINARY, "image" ); registerKeyword( "key" );
registerColumnType( Types.VARCHAR, "text" );
registerColumnType( Types.NVARCHAR, "text" );
} }
else {
// Use 'varchar(max)' and 'varbinary(max)' instead @Override
protected List<Integer> getSupportedJdbcTypeCodes() {
List<Integer> 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 deprecated TEXT and IMAGE types. Note that
// the length of a VARCHAR or VARBINARY column must // the length of a VARCHAR or VARBINARY column must
// be either between 1 and 8000 or exactly MAX, and // be either between 1 and 8000 or exactly MAX, and
// the length of an NVARCHAR column must be either // the length of an NVARCHAR column must be either
// between 1 and 4000 or exactly MAX. // between 1 and 4000 or exactly MAX. (HHH-3965)
switch (jdbcTypeCode) {
// See http://www.sql-server-helper.com/faq/sql-server-2005-varchar-max-p01.aspx case BLOB:
// See HHH-3965 return "varbinary(max)";
case CLOB:
registerColumnType( Types.BLOB, "varbinary(max)" ); return "varchar(max)";
registerColumnType( Types.VARBINARY, "varbinary(max)" ); case NCLOB:
return "nvarchar(max)";
registerColumnType( Types.CLOB, "varchar(max)" ); }
registerColumnType( Types.NCLOB, "nvarchar(max)" ); // HHH-8435 fix
registerColumnType( Types.VARCHAR, "varchar(max)" );
registerColumnType( Types.NVARCHAR, "nvarchar(max)" );
} }
registerKeyword( "top" ); switch (jdbcTypeCode) {
registerKeyword( "key" ); 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 @Override
@ -152,11 +170,6 @@ public int getMaxNVarcharLength() {
return 4000; return 4000;
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public TimeZoneSupport getTimeZoneSupport() { public TimeZoneSupport getTimeZoneSupport() {
return getVersion().isSameOrAfter( 10 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE; return getVersion().isSameOrAfter( 10 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE;

View File

@ -53,6 +53,7 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION; import static org.hibernate.dialect.SimpleDatabaseVersion.ZERO_VERSION;
import static org.hibernate.type.SqlTypes.*;
/** /**
* Hibernate Dialect implementation for Cloud Spanner. * Hibernate Dialect implementation for Cloud Spanner.
@ -73,50 +74,61 @@ public class SpannerDialect extends Dialect {
private static final UniqueDelegate NOOP_UNIQUE_DELEGATE = new DoNothingUniqueDelegate(); private static final UniqueDelegate NOOP_UNIQUE_DELEGATE = new DoNothingUniqueDelegate();
public SpannerDialect() { public SpannerDialect() {
registerColumnType( Types.BOOLEAN, "bool" ); super();
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)" );
} }
public SpannerDialect(DialectResolutionInfo info) { public SpannerDialect(DialectResolutionInfo info) {
this(); super();
registerKeywords( info ); 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 @Override
public int getMaxVarcharLength() { public int getMaxVarcharLength() {
//max is equivalent to 2_621_440 //max is equivalent to 2_621_440

View File

@ -16,6 +16,6 @@
@Deprecated @Deprecated
public class Sybase11Dialect extends SybaseASEDialect { public class Sybase11Dialect extends SybaseASEDialect {
public Sybase11Dialect() { public Sybase11Dialect() {
super( DatabaseVersion.make( 11 ), false, false ); super( DatabaseVersion.make( 11 ) );
} }
} }

View File

@ -17,7 +17,7 @@
public class SybaseASE157Dialect extends SybaseASEDialect { public class SybaseASE157Dialect extends SybaseASEDialect {
public SybaseASE157Dialect() { public SybaseASE157Dialect() {
super( DatabaseVersion.make( 15, 7 ), false, false ); super( DatabaseVersion.make( 15, 7 ) );
} }
} }

View File

@ -17,7 +17,7 @@
public class SybaseASE15Dialect extends SybaseASEDialect { public class SybaseASE15Dialect extends SybaseASEDialect {
public SybaseASE15Dialect() { public SybaseASE15Dialect() {
super( DatabaseVersion.make( 15 ), false, false ); super( DatabaseVersion.make( 15 ) );
} }
} }

View File

@ -43,6 +43,7 @@
import jakarta.persistence.TemporalType; import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate; import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.*;
/** /**
* Dialect for Sybase Adaptive Server Enterprise for * Dialect for Sybase Adaptive Server Enterprise for
@ -54,55 +55,25 @@ public class SybaseASEDialect extends SybaseDialect {
private final boolean ansiNull; private final boolean ansiNull;
public SybaseASEDialect() { public SybaseASEDialect() {
this( DatabaseVersion.make( 11 ), false, false ); this( DatabaseVersion.make( 11 ) );
} }
public SybaseASEDialect(DialectResolutionInfo info) { public SybaseASEDialect(DialectResolutionInfo info) {
this( this( info.makeCopy(), info );
info.makeCopy(),
info.getDriverName() != null && info.getDriverName().contains( "jTDS" ),
isAnsiNull( info.unwrap( DatabaseMetaData.class ) )
);
registerKeywords( info ); registerKeywords( info );
} }
public SybaseASEDialect(DatabaseVersion version, boolean jtdsDriver, boolean ansiNull) { public SybaseASEDialect(DatabaseVersion version) {
super( version, jtdsDriver ); this(version, null);
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" );
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" ); protected SybaseASEDialect(DatabaseVersion version, DialectResolutionInfo info) {
registerColumnType( Types.VARCHAR, "text" ); super(version, info);
ansiNull = info != null && isAnsiNull( info.unwrap( DatabaseMetaData.class ) );
registerSybaseKeywords(); registerSybaseKeywords();
sizeStrategy = new SizeStrategyImpl() { sizeStrategy = new SizeStrategyImpl() {
@Override @Override
public Size resolveSize( public Size resolveSize(
@ -123,6 +94,56 @@ public Size resolveSize(
}; };
} }
@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 @Override
public int getMaxVarcharLength() { public int getMaxVarcharLength() {
// the maximum length of a VARCHAR or VARBINARY // the maximum length of a VARCHAR or VARBINARY

View File

@ -59,30 +59,39 @@
*/ */
public class SybaseDialect extends AbstractTransactSQLDialect { public class SybaseDialect extends AbstractTransactSQLDialect {
private final DatabaseVersion version; protected boolean jtdsDriver;
protected final boolean jtdsDriver;
//All Sybase dialects share an IN list size limit. //All Sybase dialects share an IN list size limit.
private static final int PARAM_LIST_SIZE_LIMIT = 250000; private static final int PARAM_LIST_SIZE_LIMIT = 250000;
public SybaseDialect(){ public SybaseDialect() {
this( DatabaseVersion.make( 11, 0 ), false ); this( DatabaseVersion.make( 11, 0 ) );
} }
public SybaseDialect(DialectResolutionInfo info){ public SybaseDialect(DatabaseVersion version) {
this( this(version, null);
info.makeCopy(), }
info.getDriverName() != null && info.getDriverName().contains( "jTDS" )
); public SybaseDialect(DialectResolutionInfo info) {
this( info.makeCopy(), info );
registerKeywords( info ); registerKeywords( info );
} }
public SybaseDialect(DatabaseVersion version, boolean jtdsDriver) { protected SybaseDialect(DatabaseVersion version, DialectResolutionInfo info) {
super(); super(version, info);
this.version = version; }
this.jtdsDriver = jtdsDriver;
//Sybase ASE didn't introduce bigint until version 15.0 @Override
registerColumnType( Types.BIGINT, "numeric(19,0)" ); 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 @Override
@ -146,11 +155,6 @@ protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
}; };
} }
@Override
public DatabaseVersion getVersion() {
return version;
}
@Override @Override
public boolean supportsNullPrecedence() { public boolean supportsNullPrecedence() {
return false; return false;
@ -314,7 +318,7 @@ public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, D
@Override @Override
public NameQualifierSupport getNameQualifierSupport() { public NameQualifierSupport getNameQualifierSupport() {
if ( version.isSameOrAfter( 15 ) ) { if ( getVersion().isSameOrAfter( 15 ) ) {
return NameQualifierSupport.BOTH; return NameQualifierSupport.BOTH;
} }
return NameQualifierSupport.CATALOG; return NameQualifierSupport.CATALOG;

View File

@ -29,29 +29,30 @@
*/ */
public class TiDBDialect extends MySQLDialect { public class TiDBDialect extends MySQLDialect {
private final int tidbVersion; private final DatabaseVersion tidbVersion;
public TiDBDialect() { public TiDBDialect() {
// Let's be conservative and assume people use a 4 byte character set this( DatabaseVersion.make(5, 4) );
this( 500, 4 );
} }
public TiDBDialect(DialectResolutionInfo info) { public TiDBDialect(DialectResolutionInfo info) {
this( this( info.makeCopy(), info );
info.getDatabaseMajorVersion() * 100 + info.getDatabaseMinorVersion() * 10,
getCharacterSetBytesPerCharacter( info.unwrap( DatabaseMetaData.class ) )
);
registerKeywords( info ); registerKeywords( info );
} }
public TiDBDialect(int version, int characterSetBytesPerCharacter) { public TiDBDialect(DatabaseVersion version) {
this(version, null);
}
protected TiDBDialect(DatabaseVersion tidbVersion, DialectResolutionInfo info) {
// For simplicitys sake, configure MySQL 5.7 compatibility // For simplicitys sake, configure MySQL 5.7 compatibility
super( DatabaseVersion.make( 5, 7 ), characterSetBytesPerCharacter ); super( DatabaseVersion.make( 5, 7 ), info );
this.tidbVersion = version; this.tidbVersion = tidbVersion;
registerKeywords(); registerKeywords();
} }
public int getTidbVersion() { @Override
public DatabaseVersion getVersion() {
return tidbVersion; return tidbVersion;
} }