org.hibernate.dialect.Dialect Maven / Gradle / Ivy
Show all versions of beangle-hibernate-core Show documentation
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or .
*/
package org.hibernate.dialect;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.hibernate.Incubating;
import org.hibernate.Length;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.ScrollMode;
import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.FunctionContributor;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.TypeContributor;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.dialect.aggregate.AggregateSupport;
import org.hibernate.dialect.aggregate.AggregateSupportImpl;
import org.hibernate.dialect.function.CastFunction;
import org.hibernate.dialect.function.CastStrEmulation;
import org.hibernate.dialect.function.CoalesceIfnullEmulation;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CurrentFunction;
import org.hibernate.dialect.function.ExtractFunction;
import org.hibernate.dialect.function.InsertSubstringOverlayEmulation;
import org.hibernate.dialect.function.LocatePositionEmulation;
import org.hibernate.dialect.function.LpadRpadPadEmulation;
import org.hibernate.dialect.function.SqlFunction;
import org.hibernate.dialect.function.TimestampaddFunction;
import org.hibernate.dialect.function.TimestampdiffFunction;
import org.hibernate.dialect.function.TrimFunction;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupportImpl;
import org.hibernate.dialect.lock.LockingStrategy;
import org.hibernate.dialect.lock.OptimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.OptimisticLockingStrategy;
import org.hibernate.dialect.lock.PessimisticForceIncrementLockingStrategy;
import org.hibernate.dialect.lock.PessimisticReadSelectLockingStrategy;
import org.hibernate.dialect.lock.PessimisticWriteSelectLockingStrategy;
import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.sequence.NoSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.dialect.temptable.StandardTemporaryTableExporter;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableExporter;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.LobCreator;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.internal.DefaultSchemaNameResolver;
import org.hibernate.engine.jdbc.env.spi.AnsiSqlKeywords;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.exception.spi.ConversionContext;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.MathHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.io.StreamCopier;
import org.hibernate.loader.ast.spi.MultiKeyLoadSizingStrategy;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UserDefinedType;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.persister.entity.Lockable;
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.query.Query;
import org.hibernate.query.hql.HqlTranslator;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.sqm.CastType;
import org.hibernate.query.sqm.FetchClauseType;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.TrimSpec;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.PersistentTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategyProvider;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.internal.ParameterMarkerStrategyStandard;
import org.hibernate.sql.ast.spi.ParameterMarkerStrategy;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StringBuilderSqlAppender;
import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.internal.OptionalTableUpdate;
import org.hibernate.sql.model.jdbc.OptionalTableUpdateOperation;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.internal.StandardAuxiliaryDatabaseObjectExporter;
import org.hibernate.tool.schema.internal.StandardForeignKeyExporter;
import org.hibernate.tool.schema.internal.StandardIndexExporter;
import org.hibernate.tool.schema.internal.StandardSequenceExporter;
import org.hibernate.tool.schema.internal.StandardTableCleaner;
import org.hibernate.tool.schema.internal.StandardTableExporter;
import org.hibernate.tool.schema.internal.StandardTableMigrator;
import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter;
import org.hibernate.tool.schema.internal.StandardUserDefinedTypeExporter;
import org.hibernate.tool.schema.internal.TableMigrator;
import org.hibernate.tool.schema.spi.Cleaner;
import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.tool.schema.spi.SchemaManagementTool;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.WrapperOptions;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
import org.hibernate.type.descriptor.jdbc.ArrayJdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcLiteralFormatter;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcTypeConstructor;
import org.hibernate.type.descriptor.jdbc.LongNVarcharJdbcType;
import org.hibernate.type.descriptor.jdbc.NCharJdbcType;
import org.hibernate.type.descriptor.jdbc.NClobJdbcType;
import org.hibernate.type.descriptor.jdbc.NVarcharJdbcType;
import org.hibernate.type.descriptor.jdbc.TimeUtcAsJdbcTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.TimeUtcAsOffsetTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsJdbcTimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampUtcAsOffsetDateTimeJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.ArrayDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;
import jakarta.persistence.TemporalType;
import static java.lang.Math.ceil;
import static java.lang.Math.log;
import static org.hibernate.cfg.AvailableSettings.NON_CONTEXTUAL_LOB_CREATION;
import static org.hibernate.cfg.AvailableSettings.STATEMENT_BATCH_SIZE;
import static org.hibernate.cfg.AvailableSettings.USE_GET_GENERATED_KEYS;
import static org.hibernate.internal.util.StringHelper.parseCommaSeparatedString;
import static org.hibernate.internal.util.collections.ArrayHelper.EMPTY_STRING_ARRAY;
import static org.hibernate.type.SqlTypes.ARRAY;
import static org.hibernate.type.SqlTypes.BIGINT;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.CLOB;
import static org.hibernate.type.SqlTypes.DATE;
import static org.hibernate.type.SqlTypes.DECIMAL;
import static org.hibernate.type.SqlTypes.DOUBLE;
import static org.hibernate.type.SqlTypes.FLOAT;
import static org.hibernate.type.SqlTypes.INTEGER;
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
import static org.hibernate.type.SqlTypes.NCHAR;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NUMERIC;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.REAL;
import static org.hibernate.type.SqlTypes.ROWID;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TIME_UTC;
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import static org.hibernate.type.SqlTypes.isFloatOrRealOrDouble;
import static org.hibernate.type.SqlTypes.isNumericOrDecimal;
import static org.hibernate.type.SqlTypes.isVarbinaryType;
import static org.hibernate.type.SqlTypes.isVarcharType;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_END;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_DATE;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIME;
import static org.hibernate.type.descriptor.DateTimeUtils.JDBC_ESCAPE_START_TIMESTAMP;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
import static org.hibernate.type.descriptor.converter.internal.EnumHelper.getEnumeratedValues;
/**
* Represents a dialect of SQL implemented by a particular RDBMS. Every
* subclass of this class implements support for a certain database
* platform. For example, {@link PostgreSQLDialect} implements support
* for PostgreSQL, and {@link MySQLDialect} implements support for MySQL.
*
* A subclass must provide a public constructor with a single parameter
* of type {@link DialectResolutionInfo}. Alternatively, for purposes of
* backward compatibility with older versions of Hibernate, a constructor
* with no parameters is also allowed.
*
* Almost every subclass must, as a bare minimum, override at least:
*
* - {@link #columnType(int)} to define a mapping from SQL
* {@linkplain SqlTypes type codes} to database column types, and
*
- {@link #initializeFunctionRegistry(FunctionContributions)} to
* register mappings for standard HQL functions with the
* {@link org.hibernate.query.sqm.function.SqmFunctionRegistry}.
*
*
* A subclass representing a dialect of SQL which deviates significantly
* from ANSI SQL will certainly override many additional operations.
*
* Subclasses should be thread-safe and immutable.
*
* Since Hibernate 6, a single subclass of {@code Dialect} represents all
* releases of a given product-specific SQL dialect. The version of the
* database is exposed at runtime via the {@link DialectResolutionInfo}
* passed to the constructor, and by the {@link #getVersion()} property.
*
* Programs using Hibernate should migrate away from the use of versioned
* dialect classes like, for example, {@link MySQL8Dialect}. These
* classes are now deprecated and will be removed in a future release.
*
* A custom {@code Dialect} may be specified using the configuration
* property {@value org.hibernate.cfg.AvailableSettings#DIALECT}, but
* for supported databases this property is unnecessary, and Hibernate
* will select the correct {@code Dialect} based on the JDBC URL and
* {@link DialectResolutionInfo}.
*
* @author Gavin King, David Channon
*/
public abstract class Dialect implements ConversionContext, TypeContributor, FunctionContributor {
/**
* Characters used as opening for quoting SQL identifiers
*/
public static final String QUOTE = "`\"[";
/**
* Characters used as closing for quoting SQL identifiers
*/
public static final String CLOSED_QUOTE = "`\"]";
private static final Pattern ESCAPE_CLOSING_COMMENT_PATTERN = Pattern.compile( "\\*/" );
private static final Pattern ESCAPE_OPENING_COMMENT_PATTERN = Pattern.compile( "/\\*" );
private static final CoreMessageLogger LOG = Logger.getMessageLogger( CoreMessageLogger.class, Dialect.class.getName() );
//needed for converting precision from decimal to binary digits
protected static final double LOG_BASE2OF10 = log(10)/log(2);
private final Properties properties = new Properties();
private final Set sqlKeywords = new HashSet<>();
private final SizeStrategy sizeStrategy = new SizeStrategyImpl();
private final DatabaseVersion version;
// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @deprecated provide a {@link DatabaseVersion}
*/
@Deprecated(since = "6.0")
protected Dialect() {
this( (DatabaseVersion) null );
}
protected Dialect(DatabaseVersion version) {
this.version = version;
checkVersion();
registerDefaultKeywords();
initDefaultProperties();
}
protected Dialect(DialectResolutionInfo info) {
this.version = info.makeCopy();
checkVersion();
registerDefaultKeywords();
registerKeywords(info);
initDefaultProperties();
}
protected void checkVersion() {
final DatabaseVersion version = getVersion();
final DatabaseVersion minimumVersion = getMinimumSupportedVersion();
if ( version != null && version.isBefore( minimumVersion.getMajor(), minimumVersion.getMinor(), minimumVersion.getMicro() ) ) {
LOG.unsupportedDatabaseVersion(
getClass().getName(),
version.getMajor() + "." + version.getMinor() + "." + version.getMicro(),
minimumVersion.getMajor() + "." + minimumVersion.getMinor() + "." + minimumVersion.getMicro()
);
}
}
/**
* Set appropriate default values for configuration properties.
*
* This default implementation sets
* {@value org.hibernate.cfg.AvailableSettings#STATEMENT_BATCH_SIZE},
* {@value org.hibernate.cfg.AvailableSettings#NON_CONTEXTUAL_LOB_CREATION},
* and {@value org.hibernate.cfg.AvailableSettings#USE_GET_GENERATED_KEYS}
* to defaults determined by calling
* {@link #getDefaultStatementBatchSize()},
* {@link #getDefaultNonContextualLobCreation()},
* and {@link #getDefaultUseGetGeneratedKeys()}.
*
* An implementation may set additional configuration properties, but
* this is discouraged.
*/
protected void initDefaultProperties() {
getDefaultProperties().setProperty( STATEMENT_BATCH_SIZE,
Integer.toString( getDefaultStatementBatchSize() ) );
getDefaultProperties().setProperty( NON_CONTEXTUAL_LOB_CREATION,
Boolean.toString( getDefaultNonContextualLobCreation() ) );
getDefaultProperties().setProperty( USE_GET_GENERATED_KEYS,
Boolean.toString( getDefaultUseGetGeneratedKeys() ) );
}
/**
* Register ANSI-standard column types using the length limits defined
* by {@link #getMaxVarcharLength()}, {@link #getMaxNVarcharLength()},
* and {@link #getMaxVarbinaryLength()}.
*
* This method is always called when a {@code Dialect} is instantiated.
*/
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
ddlTypeRegistry.addDescriptor( simpleSqlType( BOOLEAN ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TINYINT ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( SMALLINT ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( INTEGER ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( BIGINT ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( FLOAT ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( REAL ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( DOUBLE ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( NUMERIC ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( DECIMAL ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( DATE ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME_WITH_TIMEZONE ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIME_UTC ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_WITH_TIMEZONE ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( TIMESTAMP_UTC ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( CHAR ) );
ddlTypeRegistry.addDescriptor(
sqlTypeBuilder( VARCHAR, LONG32VARCHAR, VARCHAR )
.withTypeCapacity( getMaxVarcharLength(), columnType( VARCHAR ) )
.build()
);
ddlTypeRegistry.addDescriptor( simpleSqlType( CLOB ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( NCHAR ) );
ddlTypeRegistry.addDescriptor(
sqlTypeBuilder( NVARCHAR, LONG32NVARCHAR, NVARCHAR )
.withTypeCapacity( getMaxNVarcharLength(), columnType( NVARCHAR ) )
.build()
);
ddlTypeRegistry.addDescriptor( simpleSqlType( NCLOB ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( BINARY ) );
ddlTypeRegistry.addDescriptor(
sqlTypeBuilder( VARBINARY, LONG32VARBINARY, VARBINARY )
.withTypeCapacity( getMaxVarbinaryLength(), columnType( VARBINARY ) )
.build()
);
ddlTypeRegistry.addDescriptor( simpleSqlType( BLOB ) );
// by default use the LOB mappings for the "long" types
ddlTypeRegistry.addDescriptor( simpleSqlType( LONG32VARCHAR ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( LONG32NVARCHAR ) );
ddlTypeRegistry.addDescriptor( simpleSqlType( LONG32VARBINARY ) );
if ( supportsStandardArrays() ) {
ddlTypeRegistry.addDescriptor( new ArrayDdlTypeImpl( this, false ) );
}
if ( rowId( null ) != null ) {
ddlTypeRegistry.addDescriptor( simpleSqlType( ROWID ) );
}
}
protected boolean isLob(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case LONG32VARBINARY:
case LONG32VARCHAR:
case LONG32NVARCHAR:
case BLOB:
case CLOB:
case NCLOB:
return true;
default:
return false;
}
}
private DdlTypeImpl simpleSqlType(int sqlTypeCode) {
return new DdlTypeImpl(
sqlTypeCode,
isLob( sqlTypeCode ),
columnType( sqlTypeCode ),
castType( sqlTypeCode ),
this
);
}
/**
* Obtain a builder object for a family of capacity-dependent SQL types.
*
* @param sqlTypeCode the JDBC type code abstracting over the capacity-limited types
* @param biggestSqlTypeCode the real JDBC type code of the largest type
* @param castTypeCode the real JDBC type code to use to look at the type to use in typecasts
* @return the builder object
*/
private CapacityDependentDdlType.Builder sqlTypeBuilder(int sqlTypeCode, int biggestSqlTypeCode, int castTypeCode) {
return CapacityDependentDdlType.builder(
sqlTypeCode,
isLob( sqlTypeCode )
? CapacityDependentDdlType.LobKind.ALL_LOB
: isLob( biggestSqlTypeCode )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( biggestSqlTypeCode ),
castType( castTypeCode ),
this
);
}
/**
* The database column type name for a given JDBC type code defined
* in {@link Types} or {@link SqlTypes}. This default implementation
* returns the ANSI-standard type name.
*
* This method may be overridden by concrete {@code Dialect}s as an
* alternative to
* {@link #registerColumnTypes(TypeContributions, ServiceRegistry)}
* for simple registrations.
*
* Note that:
*
* - Implementations of this method are expected to define a
* sensible mapping for{@link Types#NCLOB} {@link Types#NCHAR},
* and {@link Types#NVARCHAR}. On some database, these types
* are simply remapped to {@code CLOB}, {@code CHAR}, and
* {@code VARCHAR}.
*
- Mappings for {@link Types#TIMESTAMP} and
* {@link Types#TIMESTAMP_WITH_TIMEZONE} should support explicit
* specification of precision if possible.
*
- As specified by {@link DdlTypeRegistry#getDescriptor(int)},
* this method never receives {@link Types#LONGVARCHAR},
* {@link Types#LONGNVARCHAR}, nor {@link Types#LONGVARBINARY},
* which are considered synonyms for their non-{@code LONG}
* counterparts.
*
- On the other hand, the types {@link SqlTypes#LONG32VARCHAR},
* {@link SqlTypes#LONG32NVARCHAR}, and
* {@link SqlTypes#LONG32VARBINARY} are not synonyms,
* and implementations of this method must define sensible
* mappings, for example to database-native {@code TEXT} or
* {@code CLOB} types.
*
*
* @param sqlTypeCode a SQL {@link SqlTypes type code}
* @return a column type name, with {@code $l}, {@code $p}, {@code $s}
* placeholders for length, precision, scale
*
* @see SqlTypes
*/
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case ROWID:
return "rowid";
case BOOLEAN:
return "boolean";
case TINYINT:
return "tinyint";
case SMALLINT:
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($p)";
case TIME_WITH_TIMEZONE:
// type included here for completeness but note that
// very few databases support it, and the general
// advice is to caution against its use (for reasons,
// check the comments in the Postgres documentation).
return "time($p) with time zone";
case TIMESTAMP:
return "timestamp($p)";
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp($p) with time zone";
case TIME_UTC:
return getTimeZoneSupport() == TimeZoneSupport.NATIVE
? columnType( TIME_WITH_TIMEZONE )
: columnType( TIME );
case TIMESTAMP_UTC:
return getTimeZoneSupport() == TimeZoneSupport.NATIVE
? columnType( TIMESTAMP_WITH_TIMEZONE )
: columnType( TIMESTAMP );
case CHAR:
return "char($l)";
case VARCHAR:
return "varchar($l)";
case CLOB:
return "clob";
case NCHAR:
return "nchar($l)";
case NVARCHAR:
return "nvarchar($l)";
case NCLOB:
return "nclob";
case BINARY:
return "binary($l)";
case VARBINARY:
return "varbinary($l)";
case BLOB:
return "blob";
// by default use the LOB mappings for the "long" types
case LONG32VARCHAR:
return columnType( CLOB );
case LONG32NVARCHAR:
return columnType( NCLOB );
case LONG32VARBINARY:
return columnType( BLOB );
default:
throw new IllegalArgumentException( "unknown type: " + sqlTypeCode );
}
}
/**
* The SQL type to use in {@code cast( ... as ... )} expressions when
* casting to the target type represented by the given JDBC type code.
*
* @param sqlTypeCode The JDBC type code representing the target type
* @return The SQL type to use in {@code cast()}
*/
protected String castType(int sqlTypeCode) {
return columnType( sqlTypeCode );
}
/**
* Register the reserved words of ANSI-standard SQL as keywords.
*
* @see AnsiSqlKeywords
*/
protected void registerDefaultKeywords() {
AnsiSqlKeywords keywords = new AnsiSqlKeywords();
//Not using #registerKeyword as:
// # these are already lowercase
// # better efficiency of addAll as it can pre-size the collections
sqlKeywords.addAll( keywords.sql2003() );
}
/**
* Register the reserved words
* {@linkplain java.sql.DatabaseMetaData#getSQLKeywords() reported}
* by the JDBC driver as keywords.
*
* @see java.sql.DatabaseMetaData#getSQLKeywords()
*/
protected void registerKeywords(DialectResolutionInfo info) {
for ( String keyword : parseCommaSeparatedString( info.getSQLKeywords() ) ) {
registerKeyword( keyword );
}
}
/**
* Get the version of the SQL dialect that is the target of this instance.
*/
public DatabaseVersion getVersion() {
return version;
}
/**
* Get the version of the SQL dialect that is the minimum supported by this implementation.
*/
protected DatabaseVersion getMinimumSupportedVersion() {
return SimpleDatabaseVersion.ZERO_VERSION;
}
/**
* Resolves the {@link SqlTypes} type code for the given column
* type name as reported by the database, or null
* if it can't be resolved.
*/
protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) {
final int parenthesisIndex = columnTypeName.lastIndexOf( '(' );
final String baseTypeName;
if ( parenthesisIndex == -1 ) {
baseTypeName = columnTypeName;
}
else {
baseTypeName = columnTypeName.substring( 0, parenthesisIndex ).trim();
}
return resolveSqlTypeCode( columnTypeName, baseTypeName, typeConfiguration );
}
/**
* Resolves the {@link SqlTypes} type code for the given column
* type name as reported by the database and the base type name
* (i.e. without precision, length and scale), or null
* if it can't be resolved.
*/
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
return typeConfiguration.getDdlTypeRegistry().getSqlTypeCode( baseTypeName );
}
/**
* Assigns an appropriate {@link JdbcType} to a column of a JDBC
* result set based on the column type name, JDBC type code,
* precision, and scale.
*
* @param columnTypeName the column type name
* @param jdbcTypeCode the {@link SqlTypes type code}
* @param precision the precision or 0
* @param scale the scale or 0
* @return an appropriate instance of {@link JdbcType}
*/
public JdbcType resolveSqlTypeDescriptor(
String columnTypeName,
int jdbcTypeCode,
int precision,
int scale,
JdbcTypeRegistry jdbcTypeRegistry) {
if ( jdbcTypeCode == ARRAY ) {
final JdbcTypeConstructor jdbcTypeConstructor = jdbcTypeRegistry.getConstructor( jdbcTypeCode );
if ( jdbcTypeConstructor != null ) {
// Special handling for array types, because we need the proper element/component type
// To determine the element JdbcType, we pass the database reported type to #resolveSqlTypeCode
final int arraySuffixIndex = columnTypeName.toLowerCase( Locale.ROOT ).indexOf( " array" );
if ( arraySuffixIndex != -1 ) {
final String componentTypeName = columnTypeName.substring( 0, arraySuffixIndex );
final Integer sqlTypeCode = resolveSqlTypeCode( componentTypeName, jdbcTypeRegistry.getTypeConfiguration() );
if ( sqlTypeCode != null ) {
return jdbcTypeConstructor.resolveType(
jdbcTypeRegistry.getTypeConfiguration(),
this,
jdbcTypeRegistry.getDescriptor( sqlTypeCode ),
ColumnTypeInformation.EMPTY
);
}
}
}
}
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
}
/**
* Determine the length/precision of a column based on information in the
* JDBC {@link java.sql.ResultSetMetaData}. Note that what JDBC reports
* as a "precision" {@linkplain java.sql.ResultSetMetaData#getPrecision
* might actually be the column length}.
*
* @param columnTypeName the name of the column type
* @param jdbcTypeCode the JDBC type code of the column type
* @param precision the (numeric) precision or (character) length of the column
* @param scale the scale of a numeric column
* @param displaySize the {@linkplain java.sql.ResultSetMetaData#getColumnDisplaySize
* display size} of the column
* @return the precision or length of the column
*/
public int resolveSqlTypeLength(
String columnTypeName,
int jdbcTypeCode,
int precision,
int scale,
int displaySize) {
// It seems MariaDB/MySQL return the precision in bytes depending on the charset,
// so to detect whether we have a single character here, we check the display size
if ( jdbcTypeCode == Types.CHAR && precision <= 4 ) {
return displaySize;
}
else {
return precision;
}
}
/**
* If this database has a special MySQL-style {@code enum} column type,
* return the type declaration for the given enumeration of values.
*
* If the database has no such type, return {@code null}.
*
* @param values the enumerated values of the type
* @return the DDL column type declaration
*/
public String getEnumTypeDeclaration(String name, String[] values) {
return null;
}
public String getEnumTypeDeclaration(Class extends Enum>> enumType) {
return getEnumTypeDeclaration( enumType.getSimpleName(), getEnumeratedValues( enumType ) );
}
public String[] getCreateEnumTypeCommand(String name, String[] values) {
return EMPTY_STRING_ARRAY;
}
public String[] getCreateEnumTypeCommand(Class extends Enum>> enumType) {
return getCreateEnumTypeCommand( enumType.getSimpleName(), getEnumeratedValues( enumType ) );
}
public String[] getDropEnumTypeCommand(String name) {
return EMPTY_STRING_ARRAY;
}
public String[] getDropEnumTypeCommand(Class extends Enum>> enumType) {
return getDropEnumTypeCommand( enumType.getSimpleName() );
}
/**
* Render a SQL check condition for a column that represents an enumerated value
* by its {@linkplain jakarta.persistence.EnumType#STRING string representation}.
*
* @return a SQL expression that will occur in a {@code check} constraint
*/
public String getCheckCondition(String columnName, String[] values) {
StringBuilder check = new StringBuilder();
check.append( columnName ).append( " in (" );
String separator = "";
for ( String value : values ) {
check.append( separator ).append('\'').append( value ).append('\'');
separator = ",";
}
return check.append( ')' ).toString();
}
public String getCheckCondition(String columnName, Class extends Enum>> enumType) {
return getCheckCondition( columnName, getEnumeratedValues( enumType ) );
}
/**
* Render a SQL check condition for a column that represents an enumerated value.
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}.
*
* @return a SQL expression that will occur in a {@code check} constraint
*/
public String getCheckCondition(String columnName, long min, long max) {
return columnName + " between " + min + " and " + max;
}
/**
* Render a SQL check condition for a column that represents an enumerated value
* by its {@linkplain jakarta.persistence.EnumType#ORDINAL ordinal representation}.
*
* @return a SQL expression that will occur in a {@code check} constraint
*/
public String getCheckCondition(String columnName, long[] values) {
StringBuilder check = new StringBuilder();
check.append( columnName ).append( " in (" );
String separator = "";
for ( long value : values ) {
check.append( separator ).append( value );
separator = ",";
}
return check.append( ')' ).toString();
}
@Override
public void contributeFunctions(FunctionContributions functionContributions) {
initializeFunctionRegistry( functionContributions );
}
@Override
public int ordinal() {
// dialect-contributed functions come first
return 0;
}
/**
* Initialize the given registry with any dialect-specific functions.
*
* Support for certain SQL functions is required, and if the database
* does not support a required function, then the dialect must define
* a way to emulate it.
*
* These required functions include the functions defined by the JPA
* query language specification:
*
*
* -
avg(arg)
- aggregate function
* -
count([distinct ]arg)
- aggregate function
* -
max(arg)
- aggregate function
* -
min(arg)
- aggregate function
* -
sum(arg)
- aggregate function
*
*
*
* -
coalesce(arg0, arg1, ...)
* -
nullif(arg0, arg1)
*
*
*
* -
lower(arg)
* -
upper(arg)
* -
length(arg)
* -
concat(arg0, arg1, ...)
* -
locate(pattern, string[, start])
* -
substring(string, start[, length])
* -
trim([[spec ][character ]from] string)
*
*
*
* -
abs(arg)
* -
mod(arg0, arg1)
* -
sqrt(arg)
*
*
*
* -
current date
* -
current time
* -
current timestamp
*
*
* Along with an additional set of functions defined by ANSI SQL:
*
*
* -
any(arg)
- aggregate function
* -
every(arg)
- aggregate function
*
*
* -
var_samp(arg)
- aggregate function
* -
var_pop(arg)
- aggregate function
* -
stddev_samp(arg)
- aggregate function
* -
stddev_pop(arg)
- aggregate function
*
*
*
* -
cast(arg as Type)
* -
extract(field from arg)
*
*
*
* -
ln(arg)
* -
exp(arg)
* -
power(arg0, arg1)
* -
floor(arg)
* -
ceiling(arg)
*
*
*
* -
position(pattern in string)
* -
substring(string from start[ for length])
* -
overlay(string placing replacement from start[ for length])
*
*
* And the following functions for working with java.time
* types:
*
*
* -
local date
* -
local time
* -
local datetime
* -
offset datetime
* -
instant
*
*
* And a number of additional "standard" functions:
*
*
* -
left(string, length)
* -
right(string, length)
* -
replace(string, pattern, replacement)
* -
pad(string with length spec[ character])
* -
repeat(string, times)
*
*
*
* -
pi
* -
log10(arg)
* -
log(base, arg)
* -
sign(arg)
* -
sin(arg)
* -
cos(arg)
* -
tan(arg)
* -
asin(arg)
* -
acos(arg)
* -
atan(arg)
* -
atan2(arg0, arg1)
* -
round(arg0[, arg1])
* -
truncate(arg0[, arg1])
* -
sinh(arg)
* -
tanh(arg)
* -
cosh(arg)
* -
least(arg0, arg1, ...)
* -
greatest(arg0, arg1, ...)
* -
degrees(arg)
* -
radians(arg)
*
*
*
* -
format(datetime as pattern)
* -
collate(string as collation)
* -
str(arg)
- synonym of cast(a as String)
* -
ifnull(arg0, arg1)
- synonym of coalesce(a, b)
*
*
* Finally, the following functions are defined as abbreviations for
* extract()
, and desugared by the parser:
*
*
* -
second(arg)
- synonym of extract(second from a)
* -
minute(arg)
- synonym of extract(minute from a)
* -
hour(arg)
- synonym of extract(hour from a)
* -
day(arg)
- synonym of extract(day from a)
* -
month(arg)
- synonym of extract(month from a)
* -
year(arg)
- synonym of extract(year from a)
*
*
* Note that according to this definition, the second()
* function returns a floating point value, contrary to the integer
* type returned by the native function with this name on many databases.
* Thus, we don't just naively map these HQL functions to the native SQL
* functions with the same names.
*/
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
final TypeConfiguration typeConfiguration = functionContributions.getTypeConfiguration();
final BasicTypeRegistry basicTypeRegistry = typeConfiguration.getBasicTypeRegistry();
final BasicType timestampType = basicTypeRegistry.resolve( StandardBasicTypes.TIMESTAMP );
final BasicType dateType = basicTypeRegistry.resolve( StandardBasicTypes.DATE );
final BasicType timeType = basicTypeRegistry.resolve( StandardBasicTypes.TIME );
final BasicType instantType = basicTypeRegistry.resolve( StandardBasicTypes.INSTANT );
final BasicType offsetDateTimeType = basicTypeRegistry.resolve( StandardBasicTypes.OFFSET_DATE_TIME );
final BasicType localDateTimeType = basicTypeRegistry.resolve( StandardBasicTypes.LOCAL_DATE_TIME );
final BasicType localTimeType = basicTypeRegistry.resolve( StandardBasicTypes.LOCAL_TIME );
final BasicType localDateType = basicTypeRegistry.resolve( StandardBasicTypes.LOCAL_DATE );
CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
//standard aggregate functions count(), sum(), max(), min(), avg(),
//supported on every database
//Note that we don't include median() in this list, since it's difficult
//to implement on MySQL and Sybase ASE
functionFactory.aggregates( this, SqlAstNodeRenderingMode.DEFAULT );
//the ANSI SQL-defined aggregate functions any() and every() are only
//supported on one database, but can be emulated using sum() and case,
//though there is a more natural mapping on some databases
functionFactory.everyAny_sumCase( supportsPredicateAsExpression() );
//math functions supported on almost every database
//Note that while certain mathematical functions return the same type
//as their arguments, this is not the case in general - any function
//involving exponentiation by a non-integer power, logarithms,
//trigonometric functions, etc., should be considered to be of type
//Double. In particular, there is no meaningful concept of an "exact
//decimal" version of these functions, and if any database attempted
//to implement such a silly thing, it would be dog slow.
functionFactory.math();
functionFactory.round();
//trig functions supported on almost every database
functionFactory.trigonometry();
//hyperbolic sinh and tanh are very useful but not supported on most
//databases, so emulate them here (cosh along for the ride)
functionFactory.sinh_exp();
functionFactory.cosh_exp();
functionFactory.tanh_exp();
//pi supported on most databases, but emulate it here
functionFactory.pi_acos();
//log(base, arg) supported on most databases, but emulate it here
functionFactory.log_ln();
//coalesce() function, supported by most databases, must be emulated
//in terms of nvl() for platforms which don't support it natively
functionFactory.coalesce();
//nullif() function, supported on almost every database
functionFactory.nullif();
//string functions, must be emulated where not supported
functionFactory.leftRight();
functionFactory.replace();
functionFactory.concat();
functionFactory.lowerUpper();
//there are two forms of substring(), the JPA standard syntax, which
//separates arguments using commas, and the ANSI SQL standard syntax
//with named arguments (we support both)
functionFactory.substring();
//the JPA locate() function is especially tricky to emulate, calling
//for lots of Dialect-specific customization
functionFactory.locate();
//JPA string length() function, a synonym for ANSI SQL character_length()
functionFactory.length_characterLength();
//only some databases support the ANSI SQL-style position() function, so
//define it here as an alias for locate()
functionContributions.getFunctionRegistry().register( "position",
new LocatePositionEmulation( typeConfiguration ) );
//very few databases support ANSI-style overlay() function, so emulate
//it here in terms of either insert() or concat()/substring()
functionContributions.getFunctionRegistry().register( "overlay",
new InsertSubstringOverlayEmulation( typeConfiguration, false ) );
//ANSI SQL trim() function is supported on almost all of the databases
//we care about, but on some it must be emulated using ltrim(), rtrim(),
//and replace()
functionContributions.getFunctionRegistry().register( "trim",
new TrimFunction( this, typeConfiguration ) );
//ANSI SQL cast() function is supported on the databases we care most
//about but in certain cases it doesn't allow some useful typecasts,
//which must be emulated in a dialect-specific way
//Note that two case are especially tricky to make portable:
// - casts to and from Boolean, and
// - casting Double or Float to String.
functionContributions.getFunctionRegistry().register(
"cast",
new CastFunction(
this,
functionContributions.getTypeConfiguration()
.getCurrentBaseSqlTypeIndicators()
.getPreferredSqlTypeCodeForBoolean()
)
);
//There is a 'collate' operator in a number of major databases
functionFactory.collate();
//ANSI SQL extract() function is supported on the databases we care most
//about (though it is called datepart() in some of them) but HQL defines
//additional non-standard temporal field types, which must be emulated in
//a very dialect-specific way
functionContributions.getFunctionRegistry().register( "extract",
new ExtractFunction( this, typeConfiguration ) );
//comparison functions supported on most databases, emulated on others
//using a case expression
functionFactory.leastGreatest();
//two-argument synonym for coalesce() supported on most but not every
//database, so define it here as an alias for coalesce(arg1,arg2)
functionContributions.getFunctionRegistry().register( "ifnull",
new CoalesceIfnullEmulation() );
//rpad() and pad() are supported on almost every database, and emulated
//where not supported, but they're not considered "standard" ... instead
//they're used to implement pad()
functionFactory.pad();
//pad() is a function we've designed to look like ANSI trim()
functionContributions.getFunctionRegistry().register( "pad",
new LpadRpadPadEmulation( typeConfiguration ) );
//legacy Hibernate convenience function for casting to string, defined
//here as an alias for cast(arg as String)
functionContributions.getFunctionRegistry().register( "str",
new CastStrEmulation( typeConfiguration ) );
//format() function for datetimes, emulated on many databases using the
//Oracle-style to_char() function, and on others using their native
//formatting functions
functionFactory.format_toChar();
//timestampadd()/timestampdiff() delegated back to the Dialect itself
//since there is a great variety of different ways to emulate them
functionContributions.getFunctionRegistry().register( "timestampadd",
new TimestampaddFunction( this, typeConfiguration ) );
functionContributions.getFunctionRegistry().register( "timestampdiff",
new TimestampdiffFunction( this, typeConfiguration ) );
functionContributions.getFunctionRegistry().registerAlternateKey( "dateadd", "timestampadd" );
functionContributions.getFunctionRegistry().registerAlternateKey( "datediff", "timestampdiff" );
//ANSI SQL (and JPA) current date/time/timestamp functions, supported
//natively on almost every database, delegated back to the Dialect
functionContributions.getFunctionRegistry().register(
"current_date",
new CurrentFunction(
"current_date",
currentDate(),
dateType
)
);
functionContributions.getFunctionRegistry().register(
"current_time",
new CurrentFunction(
"current_time",
currentTime(),
timeType
)
);
functionContributions.getFunctionRegistry().register(
"current_timestamp",
new CurrentFunction(
"current_timestamp",
currentTimestamp(),
timestampType
)
);
functionContributions.getFunctionRegistry().registerAlternateKey( "current date", "current_date" );
functionContributions.getFunctionRegistry().registerAlternateKey( "current time", "current_time" );
functionContributions.getFunctionRegistry().registerAlternateKey( "current timestamp", "current_timestamp" );
//HQL current instant/date/time/datetime functions, delegated back to the Dialect
functionContributions.getFunctionRegistry().register(
"local_date",
new CurrentFunction(
"local_date",
currentDate(),
localDateType
)
);
functionContributions.getFunctionRegistry().register(
"local_time",
new CurrentFunction(
"local_time",
currentLocalTime(),
localTimeType
)
);
functionContributions.getFunctionRegistry().register(
"local_datetime",
new CurrentFunction(
"local_datetime",
currentLocalTimestamp(),
localDateTimeType
)
);
functionContributions.getFunctionRegistry().register(
"offset_datetime",
new CurrentFunction(
"offset_datetime",
currentTimestampWithTimeZone(),
offsetDateTimeType
)
);
functionContributions.getFunctionRegistry().registerAlternateKey( "local date", "local_date" );
functionContributions.getFunctionRegistry().registerAlternateKey( "local time", "local_time" );
functionContributions.getFunctionRegistry().registerAlternateKey( "local datetime", "local_datetime" );
functionContributions.getFunctionRegistry().registerAlternateKey( "offset datetime", "offset_datetime" );
functionContributions.getFunctionRegistry().register(
"instant",
new CurrentFunction(
"instant",
currentTimestampWithTimeZone(),
instantType
)
);
functionContributions.getFunctionRegistry().registerAlternateKey( "current_instant", "instant" ); //deprecated legacy!
functionContributions.getFunctionRegistry().register( "sql", new SqlFunction() );
}
/**
* Translation of the HQL/JPQL {@code current_date} function, which
* maps to the Java type {@link java.sql.Date}, and of the HQL
* {@code local_date} function which maps to the Java type
* {@link java.time.LocalDate}.
*/
public String currentDate() {
return "current_date";
}
/**
* Translation of the HQL/JPQL {@code current_time} function, which
* maps to the Java type {@link java.sql.Time} which is a time with
* no time zone. This contradicts ANSI SQL where {@code current_time}
* has the type {@code TIME WITH TIME ZONE}.
*
* It is recommended to override this in dialects for databases which
* support {@code localtime} or {@code time at local}.
*/
public String currentTime() {
return "current_time";
}
/**
* Translation of the HQL/JPQL {@code current_timestamp} function,
* which maps to the Java type {@link java.sql.Timestamp} which is
* a datetime with no time zone. This contradicts ANSI SQL where
* {@code current_timestamp} has the type
* {@code TIMESTAMP WITH TIME ZONE}.
*
* It is recommended to override this in dialects for databases which
* support {@code localtimestamp} or {@code timestamp at local}.
*/
public String currentTimestamp() {
return "current_timestamp";
}
/**
* Translation of the HQL {@code local_time} function, which maps to
* the Java type {@link java.time.LocalTime} which is a time with no
* time zone. It should usually be the same SQL function as for
* {@link #currentTime()}.
*
* It is recommended to override this in dialects for databases which
* support {@code localtime} or {@code current_time at local}.
*/
public String currentLocalTime() {
return currentTime();
}
/**
* Translation of the HQL {@code local_datetime} function, which maps
* to the Java type {@link java.time.LocalDateTime} which is a datetime
* with no time zone. It should usually be the same SQL function as for
* {@link #currentTimestamp()}.
*
* It is recommended to override this in dialects for databases which
* support {@code localtimestamp} or {@code current_timestamp at local}.
*/
public String currentLocalTimestamp() {
return currentTimestamp();
}
/**
* Translation of the HQL {@code offset_datetime} function, which maps
* to the Java type {@link java.time.OffsetDateTime} which is a datetime
* with a time zone. This in principle correctly maps to the ANSI SQL
* {@code current_timestamp} which has the type
* {@code TIMESTAMP WITH TIME ZONE}.
*/
public String currentTimestampWithTimeZone() {
return currentTimestamp();
}
/**
* Obtain a pattern for the SQL equivalent to an
* {@code extract()} function call. The resulting
* pattern must contain ?1 and ?2 placeholders
* for the arguments.
*
* This method does not need to handle
* {@link TemporalUnit#NANOSECOND},
* {@link TemporalUnit#NATIVE},
* {@link TemporalUnit#OFFSET},
* {@link TemporalUnit#DATE},
* {@link TemporalUnit#TIME},
* {@link TemporalUnit#WEEK_OF_YEAR}, or
* {@link TemporalUnit#WEEK_OF_MONTH},
* which are already desugared by
* {@link ExtractFunction}.
*
* @param unit the first argument
*/
public String extractPattern(TemporalUnit unit) {
return "extract(?1 from ?2)";
}
/**
* Obtain a pattern for the SQL equivalent to a
* {@code cast()} function call. The resulting
* pattern must contain ?1 and ?2 placeholders
* for the arguments.
*
* @param from a {@link CastType} indicating the
* type of the value argument
* @param to a {@link CastType} indicating the
* type the value argument is cast to
*/
public String castPattern(CastType from, CastType to) {
switch ( to ) {
case STRING:
switch ( from ) {
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'true' when 0 then 'false' else null end";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 'true' when 'N' then 'false' else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 'true' when 'F' then 'false' else null end";
}
break;
case INTEGER:
case LONG:
switch ( from ) {
case YN_BOOLEAN:
return "case ?1 when 'Y' then 1 when 'N' then 0 else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 1 when 'F' then 0 else null end";
case BOOLEAN:
return "case ?1 when true then 1 when false then 0 else null end";
}
break;
case INTEGER_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 1 when 'Y' then 1 when 'F' then 0 when 'N' then 0 else null end";
case INTEGER:
case LONG:
return "abs(sign(?1))";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 1 when 'N' then 0 else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 1 when 'F' then 0 else null end";
case BOOLEAN:
return "case ?1 when true then 1 when false then 0 else null end";
}
break;
case YN_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 'Y' when 'Y' then 'Y' when 'F' then 'N' when 'N' then 'N' else null end";
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'Y' when 0 then 'N' else null end";
case INTEGER:
case LONG:
return "case abs(sign(?1)) when 1 then 'Y' when 0 then 'N' else null end";
case TF_BOOLEAN:
return "case ?1 when 'T' then 'Y' when 'F' then 'N' else null end";
case BOOLEAN:
return "case ?1 when true then 'Y' when false then 'N' else null end";
}
break;
case TF_BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then 'T' when 'Y' then 'T' when 'F' then 'F' when 'N' then 'F' else null end";
case INTEGER_BOOLEAN:
return "case ?1 when 1 then 'T' when 0 then 'F' else null end";
case INTEGER:
case LONG:
return "case abs(sign(?1)) when 1 then 'T' when 0 then 'F' else null end";
case YN_BOOLEAN:
return "case ?1 when 'Y' then 'T' when 'N' then 'F' else null end";
case BOOLEAN:
return "case ?1 when true then 'T' when false then 'F' else null end";
}
break;
case BOOLEAN:
switch ( from ) {
case STRING:
return "case ?1 when 'T' then true when 'Y' then true when 'F' then false when 'N' then false else null end";
case INTEGER_BOOLEAN:
case INTEGER:
case LONG:
return "(?1<>0)";
case YN_BOOLEAN:
return "(?1<>'N')";
case TF_BOOLEAN:
return "(?1<>'F')";
}
break;
}
return "cast(?1 as ?2)";
}
/**
* Obtain a pattern for the SQL equivalent to a
* {@code trim()} function call. The resulting
* pattern must contain a ?1 placeholder for the
* argument of type {@link String}.
*
* @param specification {@code leading} or {@code trailing}
* @param character the character to trim
*/
public String trimPattern(TrimSpec specification, char character) {
return character == ' '
? "trim(" + specification + " from ?1)"
: "trim(" + specification + " '" + character + "' from ?1)";
}
/**
* Whether the database supports adding a fractional interval to a timestamp,
* for example {@code timestamp + 0.5 second}.
*/
public boolean supportsFractionalTimestampArithmetic() {
return true;
}
/**
* Obtain a pattern for the SQL equivalent to a
* {@code timestampdiff()} function call. The resulting
* pattern must contain ?1, ?2, and ?3 placeholders
* for the arguments.
*
* @param unit the first argument
* @param fromTemporalType true if the first argument is
* a timestamp, false if a date
* @param toTemporalType true if the second argument is
*/
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
throw new UnsupportedOperationException( "`" + getClass().getName() + "` does not yet support #timestampdiffPattern" );
}
/**
* Obtain a pattern for the SQL equivalent to a
* {@code timestampadd()} function call. The resulting
* pattern must contain ?1, ?2, and ?3 placeholders
* for the arguments.
*
* @param unit The unit to add to the temporal
* @param temporalType The type of the temporal
* @param intervalType The type of interval to add or null if it's not a native interval
*/
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
throw new UnsupportedOperationException( "`" + getClass().getName() + "` does not yet support #timestampaddPattern" );
}
/**
* Do the given JDBC type codes, as defined in {@link Types} represent
* essentially the same type in this dialect of SQL?
*
* The default implementation treats {@link Types#NUMERIC NUMERIC} and
* {@link Types#DECIMAL DECIMAL} as the same type, and
* {@link Types#FLOAT FLOAT}, {@link Types#REAL REAL}, and
* {@link Types#DOUBLE DOUBLE} as essentially the same type, since the
* ANSI SQL specification fails to meaningfully distinguish them.
*
* The default implementation also treats {@link Types#VARCHAR VARCHAR},
* {@link Types#NVARCHAR NVARCHAR}, {@link Types#LONGVARCHAR LONGVARCHAR},
* and {@link Types#LONGNVARCHAR LONGNVARCHAR} as the same type, and
* {@link Types#VARBINARY BINARY} and
* {@link Types#LONGVARBINARY LONGVARBINARY} as the same type, since
* Hibernate doesn't really differentiate these types.
*
* @param typeCode1 the first column type info
* @param typeCode2 the second column type info
*
* @return {@code true} if the two type codes are equivalent
*/
public boolean equivalentTypes(int typeCode1, int typeCode2) {
return typeCode1==typeCode2
|| isNumericOrDecimal(typeCode1) && isNumericOrDecimal(typeCode2)
// || isIntegral(typeCode1) && isIntegral(typeCode2)
|| isFloatOrRealOrDouble(typeCode1) && isFloatOrRealOrDouble(typeCode2)
|| isVarcharType(typeCode1) && isVarcharType(typeCode2)
|| isVarbinaryType(typeCode1) && isVarbinaryType(typeCode2)
|| sameColumnType(typeCode1, typeCode2);
}
private boolean sameColumnType(int typeCode1, int typeCode2) {
try {
return Objects.equals( columnType(typeCode1), columnType(typeCode2) );
}
catch (IllegalArgumentException iae) {
return false;
}
}
/**
* Retrieve a set of default Hibernate properties for this database.
*
* An implementation may set configuration properties from
* {@link #initDefaultProperties()}, though it is discouraged.
*
* @return a set of Hibernate configuration properties
*
* @see #initDefaultProperties()
*/
public Properties getDefaultProperties() {
return properties;
}
/**
* The default value to use for the configuration property
* {@value org.hibernate.cfg.Environment#STATEMENT_BATCH_SIZE}.
*/
public int getDefaultStatementBatchSize() {
return 1;
}
/**
* The default value to use for the configuration property
* {@value org.hibernate.cfg.Environment#NON_CONTEXTUAL_LOB_CREATION}.
*/
public boolean getDefaultNonContextualLobCreation() {
return false;
}
/**
* The default value to use for the configuration property
* {@value org.hibernate.cfg.Environment#USE_GET_GENERATED_KEYS}.
*/
public boolean getDefaultUseGetGeneratedKeys() {
return true;
}
@Override
public String toString() {
return getClass().getName() + ", version: " + getVersion();
}
// database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public void contribute(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
contributeTypes( typeContributions, serviceRegistry );
}
/**
* A callback which allows the {@code Dialect} to contribute types.
*
* @param typeContributions Callback to contribute the types
* @param serviceRegistry The service registry
*/
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
// by default, not much to do...
registerColumnTypes( typeContributions, serviceRegistry );
final NationalizationSupport nationalizationSupport = getNationalizationSupport();
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
if ( nationalizationSupport == NationalizationSupport.EXPLICIT ) {
jdbcTypeRegistry.addDescriptor( NCharJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( NVarcharJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( LongNVarcharJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( NClobJdbcType.DEFAULT );
}
if ( useInputStreamToInsertBlob() ) {
jdbcTypeRegistry.addDescriptor(
Types.CLOB,
ClobJdbcType.STREAM_BINDING
);
}
if ( getTimeZoneSupport() == TimeZoneSupport.NATIVE ) {
jdbcTypeRegistry.addDescriptor( TimestampUtcAsOffsetDateTimeJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( TimeUtcAsOffsetTimeJdbcType.INSTANCE );
}
else {
jdbcTypeRegistry.addDescriptor( TimestampUtcAsJdbcTimestampJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( TimeUtcAsJdbcTimeJdbcType.INSTANCE );
}
if ( supportsStandardArrays() ) {
jdbcTypeRegistry.addTypeConstructor( ArrayJdbcTypeConstructor.INSTANCE );
}
if ( supportsMaterializedLobAccess() ) {
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_BLOB, BlobJdbcType.MATERIALIZED );
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_CLOB, ClobJdbcType.MATERIALIZED );
jdbcTypeRegistry.addDescriptor( SqlTypes.MATERIALIZED_NCLOB, NClobJdbcType.MATERIALIZED );
}
}
/**
* A {@link LobMergeStrategy} representing the legacy behavior of Hibernate.
* LOBs are not processed by merge.
*/
@SuppressWarnings("unused")
protected static final LobMergeStrategy LEGACY_LOB_MERGE_STRATEGY = new LobMergeStrategy() {
@Override
public Blob mergeBlob(Blob original, Blob target, SharedSessionContractImplementor session) {
return target;
}
@Override
public Clob mergeClob(Clob original, Clob target, SharedSessionContractImplementor session) {
return target;
}
@Override
public NClob mergeNClob(NClob original, NClob target, SharedSessionContractImplementor session) {
return target;
}
};
/**
* A {@link LobMergeStrategy} based on transferring contents using streams.
*/
@SuppressWarnings("unused")
protected static final LobMergeStrategy STREAM_XFER_LOB_MERGE_STRATEGY = new LobMergeStrategy() {
@Override
public Blob mergeBlob(Blob original, Blob target, SharedSessionContractImplementor session) {
if ( original != target ) {
try {
// the BLOB just read during the load phase of merge
final OutputStream connectedStream = target.setBinaryStream( 1L );
// the BLOB from the detached state
final InputStream detachedStream = original.getBinaryStream();
StreamCopier.copy( detachedStream, connectedStream );
return target;
}
catch (SQLException e ) {
throw session.getFactory().getJdbcServices().getSqlExceptionHelper()
.convert( e, "unable to merge BLOB data" );
}
}
else {
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeBlob( original, target, session );
}
}
@Override
public Clob mergeClob(Clob original, Clob target, SharedSessionContractImplementor session) {
if ( original != target ) {
try {
// the CLOB just read during the load phase of merge
final OutputStream connectedStream = target.setAsciiStream( 1L );
// the CLOB from the detached state
final InputStream detachedStream = original.getAsciiStream();
StreamCopier.copy( detachedStream, connectedStream );
return target;
}
catch (SQLException e ) {
throw session.getFactory().getJdbcServices().getSqlExceptionHelper().convert( e, "unable to merge CLOB data" );
}
}
else {
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeClob( original, target, session );
}
}
@Override
public NClob mergeNClob(NClob original, NClob target, SharedSessionContractImplementor session) {
if ( original != target ) {
try {
// the NCLOB just read during the load phase of merge
final OutputStream connectedStream = target.setAsciiStream( 1L );
// the NCLOB from the detached state
final InputStream detachedStream = original.getAsciiStream();
StreamCopier.copy( detachedStream, connectedStream );
return target;
}
catch (SQLException e ) {
throw session.getFactory().getJdbcServices().getSqlExceptionHelper().convert( e, "unable to merge NCLOB data" );
}
}
else {
return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeNClob( original, target, session );
}
}
};
/**
* A {@link LobMergeStrategy} based on creating a new LOB locator.
*/
protected static final LobMergeStrategy NEW_LOCATOR_LOB_MERGE_STRATEGY = new LobMergeStrategy() {
@Override
public Blob mergeBlob(Blob original, Blob target, SharedSessionContractImplementor session) {
if ( original == null && target == null ) {
return null;
}
final JdbcServices jdbcServices = session.getFactory().getFastSessionServices().jdbcServices;
try {
final LobCreator lobCreator = jdbcServices.getLobCreator(
session
);
return original == null
? lobCreator.createBlob( ArrayHelper.EMPTY_BYTE_ARRAY )
: lobCreator.createBlob( original.getBinaryStream(), original.length() );
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert( e, "unable to merge BLOB data" );
}
}
@Override
public Clob mergeClob(Clob original, Clob target, SharedSessionContractImplementor session) {
if ( original == null && target == null ) {
return null;
}
final JdbcServices jdbcServices = session.getFactory().getFastSessionServices().jdbcServices;
try {
final LobCreator lobCreator = jdbcServices.getLobCreator( session );
return original == null
? lobCreator.createClob( "" )
: lobCreator.createClob( original.getCharacterStream(), original.length() );
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert( e, "unable to merge CLOB data" );
}
}
@Override
public NClob mergeNClob(NClob original, NClob target, SharedSessionContractImplementor session) {
if ( original == null && target == null ) {
return null;
}
final JdbcServices jdbcServices = session.getFactory().getFastSessionServices().jdbcServices;
try {
final LobCreator lobCreator = jdbcServices.getLobCreator( session );
return original == null
? lobCreator.createNClob( "" )
: lobCreator.createNClob( original.getCharacterStream(), original.length() );
}
catch (SQLException e) {
throw jdbcServices.getSqlExceptionHelper().convert( e, "unable to merge NCLOB data" );
}
}
};
/**
* Get the {@link LobMergeStrategy} to use, {@link #NEW_LOCATOR_LOB_MERGE_STRATEGY}
* by default.
*/
public LobMergeStrategy getLobMergeStrategy() {
return NEW_LOCATOR_LOB_MERGE_STRATEGY;
}
// native identifier generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The name identifying the "native" id generation strategy for this dialect.
*
* This is the name of the id generation strategy which should be used when
* {@code "native"} is specified in {@code hbm.xml}.
*
* @return The name identifying the native generator strategy.
*/
public String getNativeIdentifierGeneratorStrategy() {
return getIdentityColumnSupport().supportsIdentityColumns()
? "identity"
: "sequence";
}
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Get the appropriate {@link IdentityColumnSupport} for this dialect.
*
* @return the IdentityColumnSupport
* @since 5.1
*/
public IdentityColumnSupport getIdentityColumnSupport() {
return IdentityColumnSupportImpl.INSTANCE;
}
// SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Get the appropriate {@link SequenceSupport} for this dialect.
**/
public SequenceSupport getSequenceSupport() {
return NoSequenceSupport.INSTANCE;
}
/**
* Get the {@code select} command used retrieve the names of all sequences.
*
* @return The select command; or null if sequences are not supported.
*/
public String getQuerySequencesString() {
return null;
}
/**
* A {@link SequenceInformationExtractor} which is able to extract
* {@link org.hibernate.tool.schema.extract.spi.SequenceInformation}
* from the JDBC result set returned when {@link #getQuerySequencesString()}
* is executed.
*/
public SequenceInformationExtractor getSequenceInformationExtractor() {
return getQuerySequencesString() == null
? SequenceInformationExtractorNoOpImpl.INSTANCE
: SequenceInformationExtractorLegacyImpl.INSTANCE;
}
// GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Get the command used to select a GUID from the database.
*
* Optional operation.
*
* @return The appropriate command.
*/
public String getSelectGUIDString() {
throw new UnsupportedOperationException( getClass().getName() + " does not support GUIDs" );
}
/**
* Does this database have some sort of support for temporary tables?
*
* @return true by default, since most do
*/
public boolean supportsTemporaryTables() {
// Most databases do
return true;
}
/**
* Does this database support primary keys for temporary tables?
*
* @return true by default, since most do
*/
public boolean supportsTemporaryTablePrimaryKey() {
// Most databases do
return true;
}
// limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Obtain a {@link LimitHandler} that implements pagination support for
* {@link Query#setMaxResults(int)} and {@link Query#setFirstResult(int)}.
*/
public LimitHandler getLimitHandler() {
throw new UnsupportedOperationException("this dialect does not support query pagination");
}
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Does this dialect support specifying timeouts when requesting locks.
*
* @return True is this dialect supports specifying lock timeouts.
*/
public boolean supportsLockTimeouts() {
return true;
}
/**
* If this dialect supports specifying lock timeouts, are those timeouts
* rendered into the {@code SQL} string as parameters? The implication
* is that Hibernate will need to bind the timeout value as a parameter
* in the {@link PreparedStatement}. If true, the parameter position
* is always handled as the last parameter; if the dialect specifies the
* lock timeout elsewhere in the {@code SQL} statement then the timeout
* value should be directly rendered into the statement and this method
* should return false.
*
* @return True if the lock timeout is rendered into the {@code SQL}
* string as a parameter; false otherwise.
*
* @deprecated This is never called, and since at least Hibernate 5 has
* just returned {@code false} in every dialect. It will be
* removed.
*/
@Deprecated(since = "6", forRemoval = true)
public boolean isLockTimeoutParameterized() {
return false;
}
/**
* A {@link LockingStrategy} which is able to acquire a database-level
* lock with the specified {@linkplain LockMode level}.
*
* @param lockable The persister for the entity to be locked.
* @param lockMode The type of lock to be acquired.
* @return The appropriate locking strategy.
*
* @since 3.2
*/
public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
switch ( lockMode ) {
case PESSIMISTIC_FORCE_INCREMENT:
return new PessimisticForceIncrementLockingStrategy( lockable, lockMode );
case PESSIMISTIC_WRITE:
return new PessimisticWriteSelectLockingStrategy( lockable, lockMode );
case PESSIMISTIC_READ:
return new PessimisticReadSelectLockingStrategy( lockable, lockMode );
case OPTIMISTIC:
return new OptimisticLockingStrategy( lockable, lockMode );
case OPTIMISTIC_FORCE_INCREMENT:
return new OptimisticForceIncrementLockingStrategy( lockable, lockMode );
default:
return new SelectLockingStrategy( lockable, lockMode );
}
}
/**
* Given a set of {@link LockOptions} (lock level, timeout),
* determine the appropriate {@code for update} fragment to
* use to obtain the lock.
*
* @param lockOptions contains the lock mode to apply.
* @return The appropriate {@code for update} fragment.
*/
public String getForUpdateString(LockOptions lockOptions) {
return getForUpdateString( lockOptions.getLockMode(), lockOptions.getTimeOut() );
}
/**
* Given a {@linkplain LockMode lock level} and timeout,
* determine the appropriate {@code for update} fragment to
* use to obtain the lock.
*
* @param lockMode the lock mode to apply.
* @param timeout the timeout
* @return The appropriate {@code for update} fragment.
*/
private String getForUpdateString(LockMode lockMode, int timeout) {
switch ( lockMode ) {
case PESSIMISTIC_READ: {
return getReadLockString( timeout );
}
case PESSIMISTIC_WRITE: {
return getWriteLockString( timeout );
}
case UPGRADE_NOWAIT:
case PESSIMISTIC_FORCE_INCREMENT: {
return getForUpdateNowaitString();
}
case UPGRADE_SKIPLOCKED: {
return getForUpdateSkipLockedString();
}
default: {
return "";
}
}
}
/**
* Given a {@link LockMode}, determine the appropriate
* {@code for update} fragment to use to obtain the lock.
*
* @param lockMode The lock mode to apply.
* @return The appropriate for update fragment.
*/
public String getForUpdateString(LockMode lockMode) {
return getForUpdateString( lockMode, LockOptions.WAIT_FOREVER );
}
/**
* Get the string to append to {@code SELECT} statements to
* acquire pessimistic UPGRADE locks for this dialect.
*
* @return The appropriate {@code FOR UPDATE} clause string.
*/
public String getForUpdateString() {
return " for update";
}
/**
* Get the string to append to {@code SELECT} statements to
* acquire pessimistic WRITE locks for this dialect.
*
* Location of the returned string is treated the same as
* {@link #getForUpdateString()}.
*
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
* @return The appropriate {@code LOCK} clause string.
*/
public String getWriteLockString(int timeout) {
return getForUpdateString();
}
/**
* Get the string to append to {@code SELECT} statements to
* acquire WRITE locks for this dialect, given the aliases of
* the columns to be write locked.
*
* Location of the returned string is treated the same as
* {@link #getForUpdateString()}.
*
* @param aliases The columns to be read locked.
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
* @return The appropriate {@code LOCK} clause string.
*/
public String getWriteLockString(String aliases, int timeout) {
// by default we simply return the getWriteLockString(timeout) result since
// the default is to say no support for "FOR UPDATE OF ..."
return getWriteLockString( timeout );
}
/**
* Get the string to append to {@code SELECT} statements to
* acquire READ locks for this dialect.
*
* Location of the returned string is treated the same as
* {@link #getForUpdateString()}.
*
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
* @return The appropriate {@code LOCK} clause string.
*/
public String getReadLockString(int timeout) {
return getForUpdateString();
}
/**
* Get the string to append to {@code SELECT} statements to
* acquire READ locks for this dialect, given the aliases of
* the columns to be read locked.
*
* Location of the returned string is treated the same as
* {@link #getForUpdateString()}.
*
* @param aliases The columns to be read locked.
* @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait.
* @return The appropriate {@code LOCK} clause string.
*/
public String getReadLockString(String aliases, int timeout) {
// by default we simply return the getReadLockString(timeout) result since
// the default is to say no support for "FOR UPDATE OF ..."
return getReadLockString( timeout );
}
/**
* The {@linkplain RowLockStrategy row lock strategy} to use for write locks.
*/
public RowLockStrategy getWriteRowLockStrategy() {
// by default we report no support
return RowLockStrategy.NONE;
}
/**
* The {@linkplain RowLockStrategy row lock strategy} to use for read locks.
*/
public RowLockStrategy getReadRowLockStrategy() {
return getWriteRowLockStrategy();
}
/**
* Does this dialect support {@code FOR UPDATE} in conjunction with
* outer-joined rows?
*
* @return True if outer-joined rows can be locked via {@code FOR UPDATE}.
*/
public boolean supportsOuterJoinForUpdate() {
return true;
}
/**
* Get the {@code FOR UPDATE OF column_list} fragment appropriate
* for this dialect, given the aliases of the columns to be write
* locked.
*
* @param aliases The columns to be write locked.
* @return The appropriate {@code FOR UPDATE OF column_list} clause string.
*/
public String getForUpdateString(String aliases) {
// by default we simply return the getForUpdateString() result since
// the default is to say no support for "FOR UPDATE OF ..."
return getForUpdateString();
}
/**
* Get the {@code FOR UPDATE OF} or {@code FOR SHARE OF} fragment
* appropriate for this dialect, given the aliases of the columns
* to be locked.
*
* @param aliases The columns to be locked.
* @param lockOptions the lock options to apply
* @return The appropriate {@code FOR UPDATE OF column_list} clause string.
*/
public String getForUpdateString(String aliases, LockOptions lockOptions) {
LockMode lockMode = lockOptions.getLockMode();
for ( Map.Entry entry : lockOptions.getAliasSpecificLocks() ) {
// seek the highest lock mode
final LockMode lm = entry.getValue();
if ( lm.greaterThan(lockMode) ) {
lockMode = lm;
}
}
lockOptions.setLockMode( lockMode );
return getForUpdateString( lockOptions );
}
/**
* Retrieves the {@code FOR UPDATE NOWAIT} syntax specific to this dialect.
*
* @return The appropriate {@code FOR UPDATE NOWAIT} clause string.
*/
public String getForUpdateNowaitString() {
// by default, we report no support for NOWAIT lock semantics
return getForUpdateString();
}
/**
* Retrieves the {@code FOR UPDATE SKIP LOCKED} syntax specific to this dialect.
*
* @return The appropriate {@code FOR UPDATE SKIP LOCKED} clause string.
*/
public String getForUpdateSkipLockedString() {
// by default, we report no support for SKIP_LOCKED lock semantics
return getForUpdateString();
}
/**
* Get the {@code FOR UPDATE OF column_list NOWAIT} fragment appropriate
* for this dialect, given the aliases of the columns to be write locked.
*
* @param aliases The columns to be write locked.
* @return The appropriate {@code FOR UPDATE OF colunm_list NOWAIT} clause string.
*/
public String getForUpdateNowaitString(String aliases) {
return getForUpdateString( aliases );
}
/**
* Get the {@code FOR UPDATE OF column_list SKIP LOCKED} fragment appropriate
* for this dialect, given the aliases of the columns to be write locked.
*
* @param aliases The columns to be write locked.
* @return The appropriate {@code FOR UPDATE colunm_list SKIP LOCKED} clause string.
*/
public String getForUpdateSkipLockedString(String aliases) {
return getForUpdateString( aliases );
}
/**
* Some dialects support an alternative means to {@code SELECT FOR UPDATE},
* whereby a "lock hint" is appended to the table name in the {@code from}
* clause.
*
* @param lockOptions The lock options to apply
* @param tableName The name of the table to which to apply the lock hint.
* @return The table with any required lock hints.
*/
public String appendLockHint(LockOptions lockOptions, String tableName){
return tableName;
}
/**
* Modifies the given SQL, applying the appropriate updates for the specified
* lock modes and key columns.
*
* This allows emulation of {@code SELECT FOR UPDATE} for dialects which do not
* support the standard syntax.
*
* @param sql the SQL string to modify
* @param aliasedLockOptions lock options indexed by aliased table names.
* @param keyColumnNames a map of key columns indexed by aliased table names.
* @return the modified SQL string.
*/
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map keyColumnNames) {
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
}
protected int getTimeoutInSeconds(int millis) {
return millis == 0 ? 0 : Math.max( 1, Math.round( millis / 1e3f ) );
}
// table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The command used to create a table, usually {@code create table}.
*
* @return The command used to create a table.
*/
public String getCreateTableString() {
return "create table";
}
/**
* An arbitrary fragment appended to the end of the {@code create table}
* statement.
*
* @apiNote An example is the MySQL {@code engine} option specifying a
* storage engine.
*/
public String getTableTypeString() {
return "";
}
/**
* For dropping a table, can the phrase {@code if exists} be
* applied before the table name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsAfterTableName} should
* return true.
*
* @return {@code true} if {@code if exists} can be applied
* before the table name
*/
public boolean supportsIfExistsBeforeTableName() {
return false;
}
/**
* For dropping a table, can the phrase {@code if exists} be
* applied after the table name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsBeforeTableName} should
* return true.
*
* @return {@code true} if {@code if exists} can be applied
* after the table name
*/
public boolean supportsIfExistsAfterTableName() {
return false;
}
/**
* The command used to drop a table with the given name, usually
* {@code drop table tab_name}.
*
* @param tableName The name of the table to drop
*
* @return The {@code drop table} statement as a string
*/
public String getDropTableString(String tableName) {
final StringBuilder buf = new StringBuilder( "drop table " );
if ( supportsIfExistsBeforeTableName() ) {
buf.append( "if exists " );
}
buf.append( tableName ).append( getCascadeConstraintsString() );
if ( supportsIfExistsAfterTableName() ) {
buf.append( " if exists" );
}
return buf.toString();
}
/**
* The command used to create an index, usually {@code create index}
* or {@code create unique index}.
*
* @param unique {@code true} if the index is a unique index
* @return The command used to create an index.
*/
public String getCreateIndexString(boolean unique) {
return unique ? "create unique index" : "create index";
}
/**
* A string to be appended to the end of the {@code create index}
* command, usually to specify that {@code null} values are to be
* considered distinct.
*/
public String getCreateIndexTail(boolean unique, List columns) {
return "";
}
/**
* Do we need to qualify index names with the schema name?
*
* @return {@code true} if we do
*/
public boolean qualifyIndexName() {
return true;
}
/**
* Slight variation on {@link #getCreateTableString}. Here, we have
* the command used to create a table when there is no primary key
* and duplicate rows are expected.
*
* @apiNote Most databases do not have this distinction; this method
* was originally added for Teradata which does.
*
* @return The command used to create a multiset table.
*/
public String getCreateMultisetTableString() {
return getCreateTableString();
}
/**
* Does this dialect support the {@code ALTER TABLE} syntax?
*
* @return True if we support altering existing tables; false otherwise.
*/
public boolean hasAlterTable() {
return true;
}
/**
* The command used to alter a table with the given name, usually
* {@code alter table tab_name} or
* {@code alter table tab_name if exists}.
*
* We prefer the {@code if exists} form if supported.
*
* @param tableName The name of the table to alter
* @return The command used to alter a table.
*
* @since 5.2.11
*/
public String getAlterTableString(String tableName) {
final StringBuilder sb = new StringBuilder( "alter table " );
if ( supportsIfExistsAfterAlterTable() ) {
sb.append( "if exists " );
}
sb.append( tableName );
return sb.toString();
}
/**
* For an {@code alter table}, can the phrase {@code if exists} be
* applied?
*
* @return {@code true} if {@code if exists} can be applied after
* {@code alter table}
*
* @since 5.2.11
*/
public boolean supportsIfExistsAfterAlterTable() {
return false;
}
/**
* The subcommand of the {@code alter table} command used to add
* a column to a table, usually {@code add column} or {@code add}.
*
* @return The {@code add column} fragment.
*/
public String getAddColumnString() {
return "add column";
}
/**
* The syntax for the suffix used to add a column to a table.
*
* @return The suffix of the {@code add column} fragment.
*/
public String getAddColumnSuffixString() {
return "";
}
/**
* Do we need to drop constraints before dropping tables in this dialect?
*
* @return True if constraints must be dropped prior to dropping the table;
* false otherwise.
*/
public boolean dropConstraints() {
return true;
}
/**
* The subcommand of the {@code alter table} command used to drop
* a foreign key constraint, usually {@code drop constraint}.
*/
public String getDropForeignKeyString() {
return "drop constraint";
}
/**
* The subcommand of the {@code alter table} command used to drop
* a unique key constraint.
*/
public String getDropUniqueKeyString() {
return "drop constraint";
}
/**
* For dropping a constraint with an {@code alter table} statement,
* can the phrase {@code if exists} be applied before the constraint
* name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsAfterConstraintName} should
* return true
*
* @return {@code true} if {@code if exists} can be applied before
* the constraint name
*/
public boolean supportsIfExistsBeforeConstraintName() {
return false;
}
/**
* For dropping a constraint with an {@code alter table}, can the
* phrase {@code if exists} be applied after the constraint name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsBeforeConstraintName} should
* return true.
*
* @return {@code true} if {@code if exists} can be applied after
* the constraint name
*/
public boolean supportsIfExistsAfterConstraintName() {
return false;
}
/**
* Does this dialect support modifying the type of an existing column?
*/
public boolean supportsAlterColumnType() {
return false;
}
/**
* The fragment of an {@code alter table} command which modifies a
* column type, or null if column types cannot be modified.
* Often {@code alter column col_name set data type col_type}.
*
* @param columnName the name of the column
* @param columnType the new type of the column
* @param columnDefinition the full column definition
* @return a fragment to be appended to {@code alter table}
*/
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
return null;
}
/**
* The syntax used to add a foreign key constraint to a table,
* with the referenced key columns explicitly specified.
*
* @param constraintName The foreign key constraint name
* @param foreignKey The names of the columns comprising the
* foreign key
* @param referencedTable The table referenced by the foreign key
* @param primaryKey The explicit columns in the referencedTable
* referenced by this foreign key.
* @param referencesPrimaryKey if false, constraint should be
* explicit about which column names
* the constraint refers to
*
* @return the "add FK" fragment
*/
public String getAddForeignKeyConstraintString(
String constraintName,
String[] foreignKey,
String referencedTable,
String[] primaryKey,
boolean referencesPrimaryKey) {
final StringBuilder res = new StringBuilder( 30 );
res.append( " add constraint " )
.append( quote( constraintName ) )
.append( " foreign key (" )
.append( String.join( ", ", foreignKey ) )
.append( ") references " )
.append( referencedTable );
if ( !referencesPrimaryKey ) {
res.append( " (" )
.append( String.join( ", ", primaryKey ) )
.append( ')' );
}
return res.toString();
}
/**
* The syntax used to add a foreign key constraint to a table,
* given the definition of the foreign key as a string.
*
* @param constraintName The foreign key constraint name
* @param foreignKeyDefinition The whole definition of the
* foreign key as a fragment
*/
public String getAddForeignKeyConstraintString(
String constraintName,
String foreignKeyDefinition) {
return " add constraint " + quote( constraintName )
+ " " + foreignKeyDefinition;
}
/**
* The syntax used to add a primary key constraint to a table.
*
* @param constraintName The name of the PK constraint.
* @return The "add PK" fragment
*/
public String getAddPrimaryKeyConstraintString(String constraintName) {
return " add constraint " + constraintName + " primary key ";
}
/**
* The {@link SqmMultiTableMutationStrategy} to use when not specified by
* {@link org.hibernate.query.spi.QueryEngineOptions#getCustomSqmMultiTableMutationStrategy}.
*
* @see SqmMultiTableMutationStrategyProvider#createMutationStrategy
*/
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new PersistentTableMutationStrategy(
TemporaryTable.createIdTable(
entityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
/**
* The {@link SqmMultiTableInsertStrategy} to use when not specified by
* {@link org.hibernate.query.spi.QueryEngineOptions#getCustomSqmMultiTableInsertStrategy}.
*
* @see SqmMultiTableMutationStrategyProvider#createInsertStrategy
*/
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType entityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new PersistentTableInsertStrategy(
TemporaryTable.createEntityTable(
entityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
// UDT support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The kind of user-defined type to create, or the empty
* string if this does not need to be specified. Included
* after {@code create type type_name as}, but before the
* list of members.
*/
public String getCreateUserDefinedTypeKindString() {
return "";
}
/**
* An arbitrary extension to append to the end of the UDT
* {@code create type} command.
*/
public String getCreateUserDefinedTypeExtensionsString() {
return "";
}
/**
* For dropping a type, can the phrase {@code if exists} be
* applied before the type name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsAfterTypeName} should
* return true.
*
* @return {@code true} if {@code if exists} can be applied
* before the type name
*/
public boolean supportsIfExistsBeforeTypeName() {
return false;
}
/**
* For dropping a type, can the phrase {@code if exists} be
* applied after the type name?
*
* @apiNote Only one or the other (or neither) of this and
* {@link #supportsIfExistsBeforeTypeName} should
* return true.
*
* @return {@code true} if {@code if exists} can be applied
* after the type name
*/
public boolean supportsIfExistsAfterTypeName() {
return false;
}
// callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Registers a parameter capable of returning a {@link ResultSet}
* by position, either an {@code OUT} parameter, or a
* {@link Types#REF_CURSOR REF_CURSOR} parameter as defined in Java 8.
*
* @apiNote Before Java 8, support for {@link ResultSet}-returning
* parameters was very uneven across database and drivers,
* leading to its inclusion as part of the {@code Dialect}
* contract.
*
* @param statement The callable statement.
* @param position The bind position at which to register the output param.
*
* @return The number of (contiguous) bind positions used.
*
* @throws SQLException Indicates problems registering the param.
*/
public int registerResultSetOutParameter(CallableStatement statement, int position)
throws SQLException {
throw new UnsupportedOperationException(
getClass().getName() +
" does not support resultsets via stored procedures"
);
}
/**
* Registers a parameter capable of returning a {@link ResultSet}
* by name, either an {@code OUT} parameter, or a
* {@link Types#REF_CURSOR REF_CURSOR} parameter as defined in Java 8.
*
* @apiNote Before Java 8, support for {@link ResultSet}-returning
* parameters was very uneven across database and drivers,
* leading to its inclusion as part of the {@code Dialect}
* contract.
*
* @param statement The callable statement.
* @param name The parameter name (for drivers which support named parameters).
*
* @return The number of (contiguous) bind positions used.
*
* @throws SQLException Indicates problems registering the param.
*/
@SuppressWarnings("UnusedParameters")
public int registerResultSetOutParameter(CallableStatement statement, String name)
throws SQLException {
throw new UnsupportedOperationException(
getClass().getName() +
" does not support resultsets via stored procedures"
);
}
/**
* Given a {@linkplain CallableStatement callable statement} previously
* processed by {@link #registerResultSetOutParameter}, extract the
* {@link ResultSet} from the {@code OUT} parameter.
*
* @param statement The callable statement.
* @return The extracted result set.
* @throws SQLException Indicates problems extracting the result set.
*/
public ResultSet getResultSet(CallableStatement statement) throws SQLException {
throw new UnsupportedOperationException(
getClass().getName() + " does not support resultsets via stored procedures"
);
}
/**
* Given a {@linkplain CallableStatement callable statement} previously
* processed by {@link #registerResultSetOutParameter}, extract the
* {@link ResultSet} from the positional {@code OUT} parameter.
*
* @param statement The callable statement.
* @param position The bind position at which to register the output param.
*
* @return The extracted result set.
*
* @throws SQLException Indicates problems extracting the result set.
*/
@SuppressWarnings("UnusedParameters")
public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException {
throw new UnsupportedOperationException(
getClass().getName() + " does not support resultsets via stored procedures"
);
}
/**
* Given a {@linkplain CallableStatement callable statement} previously
* processed by {@link #registerResultSetOutParameter}, extract the
* {@link ResultSet} from the named {@code OUT} parameter.
*
* @param statement The callable statement.
* @param name The parameter name (for drivers which support named parameters).
*
* @return The extracted result set.
*
* @throws SQLException Indicates problems extracting the result set.
*/
@SuppressWarnings("UnusedParameters")
public ResultSet getResultSet(CallableStatement statement, String name) throws SQLException {
throw new UnsupportedOperationException(
getClass().getName() + " does not support resultsets via stored procedures"
);
}
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Does this dialect support some way to retrieve the current timestamp
* value from the database?
*
* @return True if the current timestamp can be retrieved; false otherwise.
*/
public boolean supportsCurrentTimestampSelection() {
return false;
}
/**
* Is the command returned by {@link #getCurrentTimestampSelectString}
* treated as callable?
*
* Typically, this indicates the use of the JDBC escape syntax.
*
* @return {@code} if the {@link #getCurrentTimestampSelectString} is
* treated as callable; false otherwise.
*/
public boolean isCurrentTimestampSelectStringCallable() {
throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
/**
* The command used to retrieve the current timestamp from the database.
*/
public String getCurrentTimestampSelectString() {
throw new UnsupportedOperationException( "Database not known to define a current timestamp function" );
}
/**
* Does this dialect have an ANSI SQL {@code current_timestamp} function?
*/
public boolean supportsStandardCurrentTimestampFunction() {
return true;
}
// SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* An instance of {@link SQLExceptionConversionDelegate} for interpreting
* dialect-specific {@linkplain SQLException#getErrorCode() error} or
* {@linkplain SQLException#getSQLState() SQLState} codes.
*
* If this method is overridden to return a non-null value, the default
* {@link SQLExceptionConverter} will use the returned
* {@link SQLExceptionConversionDelegate} in addition to the following
* standard delegates:
*
* - a "static" delegate based on the JDBC4-defined {@link SQLException}
* hierarchy, and
*
- a delegate that interprets SQLState codes as either X/Open or
* SQL-2003 codes, depending on what is
* {@linkplain java.sql.DatabaseMetaData#getSQLStateType reported}
* by the JDBC driver.
*
*
* It is strongly recommended that every {@code Dialect} implementation
* override this method, since interpretation of a SQL error is much
* more accurate when based on the vendor-specific
* {@linkplain SQLException#getErrorCode() error code}, rather than on
* the SQLState.
*
* @return The {@link SQLExceptionConversionDelegate} for this dialect
*/
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return null;
}
private static final ViolatedConstraintNameExtractor EXTRACTOR = sqle -> null;
/**
* A {@link ViolatedConstraintNameExtractor} for extracting the name of
* a violated constraint from a {@link SQLException}.
*/
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR;
}
// union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Given a {@linkplain Types JDBC type code}, return the expression
* for a literal null value of that type, to use in a {@code select}
* clause.
*
* The {@code select} query will be an element of a {@code UNION}
* or {@code UNION ALL}.
*
* @implNote Some databases require an explicit type cast.
*
* @param sqlType The {@link Types} type code.
* @param typeConfiguration The type configuration
* @return The appropriate select clause value fragment.
*/
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
return "null";
}
/**
* Does this dialect support {@code UNION ALL}?
*
* @return True if {@code UNION ALL} is supported; false otherwise.
*/
public boolean supportsUnionAll() {
return true;
}
/**
* Does this dialect support {@code UNION} in a subquery.
*
* @return True if {@code UNION} is supported in a subquery; false otherwise.
*/
public boolean supportsUnionInSubquery() {
return supportsUnionAll();
}
// miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The fragment used to insert a row without specifying any column values,
* usually just {@code ()}, but sometimes {@code default values}.
*
* @implNote On the other hand, this is simply not possible on some databases!
*
* @return The appropriate empty values clause.
*
* @deprecated Override the method {@code renderInsertIntoNoColumns()}
* on the {@link #getSqlAstTranslatorFactory() translator}
* returned by this dialect.
*/
@Deprecated( since = "6" )
public String getNoColumnsInsertString() {
return "values ( )";
}
/**
* Is the {@code INSERT} statement is allowed to contain no columns?
*
* @return if this dialect supports no-column {@code INSERT}.
*/
public boolean supportsNoColumnsInsert() {
return true;
}
/**
* The name of the SQL function that transforms a string to lowercase,
* almost always {@code lower}.
*
* @return The dialect-specific lowercase function.
*/
public String getLowercaseFunction() {
return "lower";
}
/**
* The name of the SQL operator that performs case-insensitive {@code LIKE}
* comparisons.
*
* @return The dialect-specific case-insensitive like operator.
*/
public String getCaseInsensitiveLike(){
return "like";
}
/**
* Does this dialect support case-insensitive {@code LIKE} comparisons?
*
* @return {@code true} if the database supports case-insensitive like
* comparisons, {@code false} otherwise.
* The default is {@code false}.
*/
public boolean supportsCaseInsensitiveLike(){
return false;
}
/**
* Does this dialect support truncation of values to a specified length
* via a {@code cast}?
*
* @return {@code true} if the database supports truncation via a cast,
* {@code false} otherwise.
* The default is {@code true}.
*/
public boolean supportsTruncateWithCast(){
return true;
}
/**
* Does this dialect support the {@code is true} and {@code is false}
* operators?
*
* @return {@code true} if the database supports {@code is true} and
* {@code is false}, or {@code false} if it does not. The
* default is {@code is false}.
*/
public boolean supportsIsTrue() {
return false;
}
/**
* Meant as a means for end users to affect the select strings being sent
* to the database and perhaps manipulate them in some fashion.
*
* @param select The select command
* @return The mutated select command, or the same as was passed in.
*/
public String transformSelectString(String select) {
return select;
}
/**
* What is the maximum length Hibernate can use for generated aliases?
*
* @implNote
* The maximum here should account for the fact that Hibernate often needs
* to append "uniqueing" information to the end of generated aliases.
* That "uniqueing" information will be added to the end of an identifier
* generated to the length specified here; so be sure to leave some room
* (generally speaking 5 positions will suffice).
*
* @return The maximum length.
*/
public int getMaxAliasLength() {
return 10;
}
/**
* What is the maximum identifier length supported by this dialect?
*
* @return The maximum length.
*/
public int getMaxIdentifierLength() {
return Integer.MAX_VALUE;
}
/**
* The SQL literal expression representing the given boolean value.
*
* @param bool The boolean value
* @return The appropriate SQL literal.
*/
public String toBooleanValueString(boolean bool) {
final StringBuilder sb = new StringBuilder();
appendBooleanValueString( new StringBuilderSqlAppender( sb ), bool );
return sb.toString();
}
/**
* Append the SQL literal expression representing the given boolean
* value to the given {@link SqlAppender}.
*
* @param bool The boolean value
* @param appender The {@link SqlAppender} to append the literal expression to
*/
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
appender.appendSql( bool ? '1' : '0' );
}
// keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Register a keyword.
*
* @param word a reserved word in this SQL dialect
*/
protected void registerKeyword(String word) {
// When tokens are checked for keywords, they are always compared against the lowercase version of the token.
// For instance, Template#renderWhereStringTemplate transforms all tokens to lowercase too.
sqlKeywords.add( word.toLowerCase( Locale.ROOT ) );
}
/**
* The keywords of this SQL dialect.
*/
public Set getKeywords() {
return sqlKeywords;
}
/**
* The {@link IdentifierHelper} indicated by this dialect for handling identifier conversions.
* Returning {@code null} is allowed and indicates that Hibernate should fall back to building
* a "standard" helper. In the fallback path, any changes made to the IdentifierHelperBuilder
* during this call will still be incorporated into the built IdentifierHelper.
*
* The incoming builder will have the following set:
*
* - {@link IdentifierHelperBuilder#isGloballyQuoteIdentifiers()}
* - {@link IdentifierHelperBuilder#getUnquotedCaseStrategy()} - initialized to UPPER
* - {@link IdentifierHelperBuilder#getQuotedCaseStrategy()} - initialized to MIXED
*
*
* By default, Hibernate will do the following:
*
* - Call {@link IdentifierHelperBuilder#applyIdentifierCasing(DatabaseMetaData)}
*
- Call {@link IdentifierHelperBuilder#applyReservedWords(DatabaseMetaData)}
*
- Applies {@link AnsiSqlKeywords#sql2003()} as reserved words
* - Applies the {#link #sqlKeywords} collected here as reserved words
* - Applies the Dialect's {@link NameQualifierSupport}, if it defines one
*
*
* @param builder A partially-configured {@link IdentifierHelperBuilder}.
* @param dbMetaData Access to the metadata returned from the driver if needed and if available.
* WARNING: it may be {@code null}.
*
* @return The {@link IdentifierHelper} instance to use,
* or {@code null} to indicate Hibernate should use its fallback path
*
* @throws SQLException Accessing the {@link DatabaseMetaData} can throw it.
* Just rethrow and Hibernate will handle it.
*
* @see #getNameQualifierSupport()
*/
public IdentifierHelper buildIdentifierHelper(
IdentifierHelperBuilder builder,
DatabaseMetaData dbMetaData) throws SQLException {
builder.applyIdentifierCasing( dbMetaData );
builder.applyReservedWords( sqlKeywords );
builder.setNameQualifierSupport( getNameQualifierSupport() );
return builder.build();
}
// identifier quoting support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The character specific to this dialect used to begin a quoted identifier.
*
* @return The dialect-specific open quote character.
*/
public char openQuote() {
return '"';
}
/**
* The character specific to this dialect used to close a quoted identifier.
*
* @return The dialect-specific close quote character.
*/
public char closeQuote() {
return '"';
}
/**
* Apply dialect-specific quoting.
*
* @param name The value to be quoted.
* @return The quoted value.
* @see #openQuote()
* @see #closeQuote()
*/
public String toQuotedIdentifier(String name) {
if ( name == null ) {
return null;
}
return openQuote() + name + closeQuote();
}
/**
* Apply dialect-specific quoting if the given name is quoted using backticks.
*
* By default, the incoming name is checked to see if its first character is
* a backtick ({@code `}). If it is, the dialect specific quoting is applied.
*
* @param name The value to be quoted.
* @return The quoted (or unmodified, if not starting with backtick) value.
* @see #openQuote()
* @see #closeQuote()
*/
public String quote(String name) {
if ( name == null ) {
return null;
}
if ( name.charAt( 0 ) == '`' ) {
return openQuote() + name.substring( 1, name.length() - 1 ) + closeQuote();
}
else {
return name;
}
}
// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* The {@link SchemaManagementTool} to use if none is explicitly specified.
*
* @apiNote Allows implementations to override how schema tooling works by default
*
* @return a {@link HibernateSchemaManagementTool} by default
*/
@Incubating
public SchemaManagementTool getFallbackSchemaManagementTool(
Map configurationValues,
ServiceRegistryImplementor registry) {
return new HibernateSchemaManagementTool();
}
private final StandardTableExporter tableExporter = new StandardTableExporter( this );
private final StandardUserDefinedTypeExporter userDefinedTypeExporter = new StandardUserDefinedTypeExporter( this );
private final StandardSequenceExporter sequenceExporter = new StandardSequenceExporter( this );
private final StandardIndexExporter indexExporter = new StandardIndexExporter( this );
private final StandardForeignKeyExporter foreignKeyExporter = new StandardForeignKeyExporter( this );
private final StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this );
private final StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this );
private final StandardTemporaryTableExporter temporaryTableExporter = new StandardTemporaryTableExporter( this );
private final StandardTableMigrator tableMigrator = new StandardTableMigrator( this );
private final StandardTableCleaner tableCleaner = new StandardTableCleaner( this );
/**
* Get an {@link Exporter} for {@link Table}s,
* usually {@link StandardTableExporter}.
*/
public Exporter getTableExporter() {
return tableExporter;
}
/**
* Get a {@link TableMigrator},
* usually {@link StandardTableMigrator}.
*/
public TableMigrator getTableMigrator() {
return tableMigrator;
}
/**
* Get a schema {@link Cleaner},
* usually {@link StandardTableCleaner}.
*/
public Cleaner getTableCleaner() {
return tableCleaner;
}
/**
* Get an {@link Exporter} for {@link UserDefinedType}s,
* usually {@link StandardUserDefinedTypeExporter}.
*/
public Exporter getUserDefinedTypeExporter() {
return userDefinedTypeExporter;
}
/**
* Get an {@link Exporter} for {@link Sequence}s,
* usually {@link StandardSequenceExporter}.
*/
public Exporter getSequenceExporter() {
return sequenceExporter;
}
/**
* Get an {@link Exporter} for {@link Index}es,
* usually {@link StandardIndexExporter}.
*/
public Exporter getIndexExporter() {
return indexExporter;
}
/**
* Get an {@link Exporter} for {@link ForeignKey}s,
* usually {@link StandardForeignKeyExporter}.
*/
public Exporter getForeignKeyExporter() {
return foreignKeyExporter;
}
/**
* Get an {@link Exporter} for unique key {@link Constraint}s,
* usually {@link StandardUniqueKeyExporter}.
*/
public Exporter getUniqueKeyExporter() {
return uniqueKeyExporter;
}
/**
* Get an {@link Exporter} for {@link AuxiliaryDatabaseObject}s,
* usually {@link StandardAuxiliaryDatabaseObjectExporter}.
*/
public Exporter getAuxiliaryDatabaseObjectExporter() {
return auxiliaryObjectExporter;
}
// Temporary table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Get a {@link TemporaryTableExporter},
* usually {@link StandardTemporaryTableExporter}.
*/
public TemporaryTableExporter getTemporaryTableExporter() {
return temporaryTableExporter;
}
/**
* The kind of temporary tables that are supported on this database.
*/
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.PERSISTENT;
}
/**
* An arbitrary SQL fragment appended to the end of the statement to
* create a temporary table, specifying dialect-specific options, or
* {@code null} if there are no options to specify.
*/
public String getTemporaryTableCreateOptions() {
return null;
}
/**
* The command to create a temporary table.
*/
public String getTemporaryTableCreateCommand() {
final TemporaryTableKind kind = getSupportedTemporaryTableKind();
switch ( kind ) {
case PERSISTENT:
return "create table";
case LOCAL:
return "create local temporary table";
case GLOBAL:
return "create global temporary table";
}
throw new UnsupportedOperationException( "Unsupported kind: " + kind );
}
/**
* The command to drop a temporary table.
*/
public String getTemporaryTableDropCommand() {
return "drop table";
}
/**
* The command to truncate a temporary table.
*/
public String getTemporaryTableTruncateCommand() {
return "delete from";
}
/**
* Annotation to be appended to the end of each COLUMN clause for temporary tables.
*
* @param sqlTypeCode The SQL type code
* @return The annotation to be appended, for example, {@code COLLATE DATABASE_DEFAULT} in SQL Server
*/
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
return "";
}
/**
* The sort of {@linkplain TempTableDdlTransactionHandling transaction handling}
* to use when creating or dropping temporary tables.
*/
public TempTableDdlTransactionHandling getTemporaryTableDdlTransactionHandling() {
return TempTableDdlTransactionHandling.NONE;
}
/**
* The action to take after finishing use of a temporary table.
*/
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.CLEAN;
}
/**
* The action to take before beginning use of a temporary table.
*/
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.NONE;
}
// Catalog / schema creation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Does this dialect support creating and dropping catalogs?
*
* @return True if the dialect supports catalog creation; false otherwise.
*/
public boolean canCreateCatalog() {
return false;
}
/**
* Get the SQL command used to create the named catalog.
*
* @param catalogName The name of the catalog to be created.
*
* @return The creation commands
*/
public String[] getCreateCatalogCommand(String catalogName) {
throw new UnsupportedOperationException( "No create catalog syntax supported by " + getClass().getName() );
}
/**
* Get the SQL command used to drop the named catalog.
*
* @param catalogName The name of the catalog to be dropped.
*
* @return The drop commands
*/
public String[] getDropCatalogCommand(String catalogName) {
throw new UnsupportedOperationException( "No drop catalog syntax supported by " + getClass().getName() );
}
/**
* Does this dialect support creating and dropping schema?
*
* @return True if the dialect supports schema creation; false otherwise.
*/
public boolean canCreateSchema() {
return true;
}
/**
* Get the SQL command used to create the named schema.
*
* @param schemaName The name of the schema to be created.
*
* @return The creation commands
*/
public String[] getCreateSchemaCommand(String schemaName) {
return new String[] {"create schema " + schemaName};
}
/**
* Get the SQL command used to drop the named schema.
*
* @param schemaName The name of the schema to be dropped.
*
* @return The drop commands
*/
public String[] getDropSchemaCommand(String schemaName) {
return new String[] {"drop schema " + schemaName};
}
/**
* Get the SQL command used to retrieve the current schema name.
*
* Works in conjunction with {@link #getSchemaNameResolver()},
* unless the resulting {@link SchemaNameResolver} does not need
* this information. For example, a custom implementation might
* make use of the Java 1.7 {@link Connection#getSchema()} method.
*
* @return The current schema retrieval SQL
*/
public String getCurrentSchemaCommand() {
return null;
}
/**
* Get the strategy for determining the schema name from a JDBC
* {@link Connection}, usually {@link DefaultSchemaNameResolver}.
*
* @return The schema name resolver strategy
*/
public SchemaNameResolver getSchemaNameResolver() {
return DefaultSchemaNameResolver.INSTANCE;
}
/**
* Does the database/driver have bug in deleting rows that refer to
* other rows being deleted in the same query?
*
* @implNote The main culprit is MySQL.
*
* @return {@code true} if the database/driver has this bug
*/
public boolean hasSelfReferentialForeignKeyBug() {
return false;
}
/**
* The keyword used to specify a nullable column, usually {@code ""},
* but sometimes {@code " null"}.
*/
public String getNullColumnString() {
return "";
}
/**
* The keyword used to specify a nullable column of the given SQL type.
*
* @implNote The culprit is {@code timestamp} columns on MySQL.
*/
public String getNullColumnString(String columnType) {
return getNullColumnString();
}
/**
* Quote the given collation name if necessary.
*/
public String quoteCollation(String collation) {
return collation;
}
/**
* Does this dialect support commenting on tables and columns?
*
* @return {@code true} if commenting is supported
*/
public boolean supportsCommentOn() {
return false;
}
/**
* Get the comment into a form supported for table definition.
*
* @param comment The comment to apply
*
* @return The comment fragment
*/
public String getTableComment(String comment) {
return "";
}
/**
* Get the comment into a form supported for UDT definition.
*
* @param comment The comment to apply
*
* @return The comment fragment
*/
public String getUserDefinedTypeComment(String comment) {
return "";
}
/**
* Get the comment into a form supported for column definition.
*
* @param comment The comment to apply
*
* @return The comment fragment
*/
public String getColumnComment(String comment) {
return "";
}
/**
* Does this dialect support column-level check constraints?
*
* @return True if column-level {@code check} constraints are supported;
* false otherwise.
*/
public boolean supportsColumnCheck() {
return true;
}
/**
* Does this dialect support table-level check constraints?
*
* @return True if table-level {@code check} constraints are supported;
* false otherwise.
*/
public boolean supportsTableCheck() {
return true;
}
/**
* Does this dialect support {@code on delete} actions in foreign key definitions?
*
* @return {@code true} if the dialect does support the {@code on delete} clause.
*/
public boolean supportsCascadeDelete() {
return true;
}
/**
* The keyword that specifies that a {@code drop table} operation
* should be cascaded to its constraints, typically
* {@code " cascade"} where the leading space is required, or
* the empty string if there is no such keyword in this dialect.
*
* @return The cascade drop keyword, if any, with a leading space
*/
public String getCascadeConstraintsString() {
return "";
}
/**
* A {@link ColumnAliasExtractor}, usually just {@link ResultSetMetaData#getColumnLabel}.
*/
public ColumnAliasExtractor getColumnAliasExtractor() {
return ColumnAliasExtractor.COLUMN_LABEL_EXTRACTOR;
}
// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* Should LOBs (both BLOB and CLOB) be bound using stream operations,
* that is, using {@link PreparedStatement#setBinaryStream}).
*
* @return True if BLOBs and CLOBs should be bound using stream operations.
*
* @since 3.2
*/
public boolean useInputStreamToInsertBlob() {
return true;
}
/**
* Does this dialect support parameters within the {@code SELECT} clause
* of {@code INSERT ... SELECT ...} statements?
*
* @return True if this is supported; false otherwise.
*
* @since 3.2
*
* @deprecated This seems to be supported on all platforms, and we don't
* call this except in test suite
*/
@Deprecated(since = "6", forRemoval = true)
public boolean supportsParametersInInsertSelect() {
return true;
}
/**
* Does this dialect support references to result variables
* (i.e, select items) by column positions (1-origin) as defined
* by the select clause?
* @return true if result variable references by column positions
* are supported; false otherwise.
*
* @since 6.0.0
*/
public boolean supportsOrdinalSelectItemReference() {
return true;
}
/**
* Returns the default ordering of null.
*
* @since 6.0.0
*/
public NullOrdering getNullOrdering() {
return NullOrdering.GREATEST;
}
/**
* Does this dialect support {@code nulls first} and {@code nulls last}?
*/
public boolean supportsNullPrecedence() {
return true;
}
/**
* A setting specific to {@link SybaseASEDialect}.
*
* @deprecated This is only called from {@link SybaseASESqlAstTranslator}
* so it doesn't need to be declared here.
*/
@Deprecated(since = "6")
public boolean isAnsiNullOn() {
return true;
}
/**
* Does this dialect/database require casting of non-string arguments
* in the {@code concat()} function?
*
* @return {@code true} if casting using {@code cast()} is required
*
* @since 6.2
*/
public boolean requiresCastForConcatenatingNonStrings() {
return false;
}
/**
* Does this dialect require that integer divisions be wrapped in
* {@code cast()} calls to tell the db parser the expected type.
*
* @implNote The culprit is HSQLDB.
*
* @return True if integer divisions must be {@code cast()}ed to float
*/
public boolean requiresFloatCastingOfIntegerDivision() {
return false;
}
/**
* Does this dialect support asking the result set its positioning
* information on forward-only cursors?
*
* Specifically, in the case of scrolling fetches, Hibernate needs
* to use {@link ResultSet#isAfterLast} and
* {@link ResultSet#isBeforeFirst}. Certain drivers do not allow
* access to these methods for forward-only cursors.
*
* @apiNote This is highly driver dependent!
*
* @return True if methods like {@link ResultSet#isAfterLast} and
* {@link ResultSet#isBeforeFirst} are supported for forward
* only cursors; false otherwise.
*
* @since 3.2
*/
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
return true;
}
/**
* Does this dialect support definition of cascade delete constraints
* which can cause circular chains?
*
* @return True if circular cascade delete constraints are supported;
* false otherwise.
*
* @since 3.2
*/
public boolean supportsCircularCascadeDeleteConstraints() {
return true;
}
/**
* Is a subselect supported as the left-hand side (LHS) of an {@code IN}
* predicates?
*
* In other words, is syntax like {@code IN (1, 2, 3)} supported?
*
* @return True if a subselect can appear as the LHS of an in-predicate;
* false otherwise.
*
* @since 3.2
*/
public boolean supportsSubselectAsInPredicateLHS() {
return true;
}
/**
* "Expected" LOB usage pattern is such that I can perform an insert via
* prepared statement with a parameter binding for a LOB value without
* crazy casting to JDBC driver implementation-specific classes.
*
* @implNote Part of the trickiness here is the fact that this is largely
* driver-dependent. For example, Oracle (which is notoriously
* bad with LOB support in their drivers historically) actually
* does a pretty good job with LOB support as of the 10.2.x v
* ersions of their driver.
*
* @return True if normal LOB usage patterns can be used with this driver;
* false if driver-specific hookiness needs to be applied.
*
* @since 3.2
*/
public boolean supportsExpectedLobUsagePattern() {
return true;
}
/**
* Does the dialect support propagating changes to LOB values back
* to the database? Talking about mutating the internal value of
* the locator, as opposed to supplying a new locator instance.
*
* - For BLOBs, the internal value might be changed by:
* {@link Blob#setBinaryStream},
* {@link Blob#setBytes(long, byte[])},
* {@link Blob#setBytes(long, byte[], int, int)},
* or {@link Blob#truncate(long)}.
*
- For CLOBs, the internal value might be changed by:
* {@link Clob#setAsciiStream(long)},
* {@link Clob#setCharacterStream(long)},
* {@link Clob#setString(long, String)},
* {@link Clob#setString(long, String, int, int)},
* or {@link Clob#truncate(long)}.
*
*
* @implNote I do not know the correct answer currently for databases
* which (1) are not part of the cruise control process, or
* (2) do not {@link #supportsExpectedLobUsagePattern}.
*
* @return True if the changes are propagated back to the database;
* false otherwise.
*
* @since 3.2
*/
public boolean supportsLobValueChangePropagation() {
// todo : pretty sure this is the same as the
// java.sql.DatabaseMetaData.locatorsUpdateCopy()
// method added in JDBC 4, see HHH-6046
return true;
}
/**
* Is it supported to materialize a LOB locator outside the transaction
* in which it was created?
*
* @implNote Again, part of the trickiness here is the fact that this is
* largely driver-dependent. All database I have tested which
* {@link #supportsExpectedLobUsagePattern()} also support the
* ability to materialize a LOB outside the owning transaction.
*
* @return True if unbounded materialization is supported; false otherwise.
*
* @since 3.2
*/
public boolean supportsUnboundedLobLocatorMaterialization() {
return true;
}
/**
* Does this dialect support referencing the table being mutated in a
* subquery? The "table being mutated" is the table referenced in an
* update or delete query. And so can that table then be referenced
* in a subquery of the update or delete query?
*
* For example, would the following two syntaxes be supported:
*
* - {@code delete from TABLE_A where ID not in (select ID from TABLE_A)}
*
- {@code update TABLE_A set NON_ID = 'something' where ID in (select ID from TABLE_A)}
*
*
* @return True if this dialect allows references the mutating table
* from a subquery.
*/
public boolean supportsSubqueryOnMutatingTable() {
return true;
}
/**
* Does the dialect support an exists statement in the select clause?
*
* @return True if exists checks are allowed in the select clause;
* false otherwise.
*/
public boolean supportsExistsInSelect() {
return true;
}
/**
* For the underlying database, is {@code READ_COMMITTED} isolation
* implemented by forcing readers to wait for write locks to be released?
*
* @return True if writers block readers to achieve {@code READ_COMMITTED};
* false otherwise.
*/
public boolean doesReadCommittedCauseWritersToBlockReaders() {
return false;
}
/**
* For the underlying database, is {@code REPEATABLE_READ} isolation
* implemented by forcing writers to wait for read locks to be released?
*
* @return True if readers block writers to achieve {@code REPEATABLE_READ};
* false otherwise.
*/
public boolean doesRepeatableReadCauseReadersToBlockWriters() {
return false;
}
/**
* Does this dialect support using a JDBC bind parameter as an argument
* to a function or procedure call?
*
* @return Returns {@code true} if the database supports accepting bind
* params as args, {@code false} otherwise. The default is
* {@code true}.
*/
public boolean supportsBindAsCallableArgument() {
return true;
}
/**
* Does this dialect support {@code count(a,b)}?
*
* @return True if the database supports counting tuples; false otherwise.
*/
public boolean supportsTupleCounts() {
return false;
}
/**
* If {@link #supportsTupleCounts()} is true, does this dialect require
* the tuple to be delimited with parentheses?
*
* @return boolean
*/
public boolean requiresParensForTupleCounts() {
return supportsTupleCounts();
}
/**
* Does this dialect support {@code count(distinct a,b)}?
*
* @return True if the database supports counting distinct tuples;
* false otherwise.
*/
public boolean supportsTupleDistinctCounts() {
// oddly most database in fact seem to, so true is the default.
return true;
}
/**
* If {@link #supportsTupleDistinctCounts()} is true, does this dialect
* require the tuple to be delimited with parentheses?
*
* @return boolean
*/
public boolean requiresParensForTupleDistinctCounts() {
return false;
}
/**
* Return the limit that the underlying database places on the number of
* elements in an {@code IN} predicate. If the database defines no such
* limits, simply return zero or a number smaller than zero.
*
* @return The limit, or a non-positive integer to indicate no limit.
*/
public int getInExpressionCountLimit() {
return 0;
}
/**
* Return the limit that the underlying database places on the number of parameters
* that can be defined for a PreparedStatement. If the database defines no such
* limits, simply return zero or a number smaller than zero. By default, Dialect
* returns the same value as {@link #getInExpressionCountLimit()}.
*
* @return The limit, or a non-positive integer to indicate no limit.
*/
public int getParameterCountLimit() {
return getInExpressionCountLimit();
}
/**
* Must LOB values occur last in inserts and updates?
*
* @implNote Oracle is the culprit here, see HHH-4635.
*
* @return boolean True if Lob values should be last, false if it
* does not matter.
*/
public boolean forceLobAsLastValue() {
return false;
}
/**
* Return whether the dialect considers an empty string value to be null.
*
* @implNote Once again, the culprit is Oracle.
*
* @return boolean True if an empty string is treated as null, false otherwise.
*/
public boolean isEmptyStringTreatedAsNull() {
return false;
}
/**
* Some dialects have trouble applying pessimistic locking depending
* upon what other query options are specified (paging, ordering, etc).
* This method allows these dialects to request that locking be applied
* by subsequent selects.
*
* @return {@code true} indicates that the dialect requests that locking
* be applied by subsequent select;
* {@code false} (the default) indicates that locking
* should be applied to the main SQL statement.
*
* @since 5.2
*/
public boolean useFollowOnLocking(String sql, QueryOptions queryOptions) {
return false;
}
/**
* Get the {@link UniqueDelegate} supported by this dialect
*
* @return The UniqueDelegate
*/
public UniqueDelegate getUniqueDelegate() {
return new AlterTableUniqueDelegate( this );
}
/**
* Apply a hint to the given SQL query.
*
* The entire query is provided, allowing full control over the placement
* and syntax of the hint.
*
* By default, ignore the hint and simply return the query.
*
* @param query The query to which to apply the hint.
* @param hintList The hints to apply
* @return The modified SQL
*/
public String getQueryHintString(String query, List hintList) {
final String hints = String.join( ", ", hintList );
return StringHelper.isEmpty( hints ) ? query : getQueryHintString( query, hints);
}
/**
* Apply a hint to the given SQL query.
*
* The entire query is provided, allowing full control over the placement
* and syntax of the hint.
*
* By default, ignore the hint and simply return the query.
*
* @param query The query to which to apply the hint.
* @param hints The hints to apply
* @return The modified SQL
*/
public String getQueryHintString(String query, String hints) {
return query;
}
/**
* A default {@link ScrollMode} to be used by {@link Query#scroll()}.
*
* @apiNote Certain dialects support a subset of {@link ScrollMode}s.
*
* @return the default {@link ScrollMode} to use.
*/
public ScrollMode defaultScrollMode() {
return ScrollMode.SCROLL_INSENSITIVE;
}
/**
* Does this dialect support {@code offset} in subqueries?
*
* For example:
*
* select * from Table1 where col1 in (select col1 from Table2 order by col2 limit 1 offset 1)
*
*
* @return {@code true} if it does
*/
public boolean supportsOffsetInSubquery() {
return false;
}
/**
* Does this dialect support the {@code order by} clause in subqueries?
*
* For example:
*
* select * from Table1 where col1 in (select col1 from Table2 order by col2 limit 1)
*
*
* @return {@code true} if it does
*/
public boolean supportsOrderByInSubquery() {
return true;
}
/**
* Does this dialect support subqueries in the {@code select} clause?
*
* For example:
*
* select col1, (select col2 from Table2 where ...) from Table1
*
*
* @return {@code true} if it does
*/
public boolean supportsSubqueryInSelect() {
return true;
}
/**
* Does this dialect fully support returning arbitrary generated column values
* after execution of an {@code insert} statement, using native SQL syntax?
*
* Support for identity columns is insufficient here, we require something like:
*
* - {@code insert ... returning ...}
*
- {@code select from final table (insert ... )}
*
*
* @return {@code true} if {@link org.hibernate.id.insert.InsertReturningDelegate}
* works for any sort of primary key column (not just identity columns), or
* {@code false} if {@code InsertReturningDelegate} does not work, or only
* works for specialized identity/"autoincrement" columns
*
* @see org.hibernate.generator.OnExecutionGenerator#getGeneratedIdentifierDelegate
* @see org.hibernate.id.insert.InsertReturningDelegate
*
* @since 6.2
*/
public boolean supportsInsertReturning() {
return false;
}
/**
* Does this dialect fully support returning arbitrary generated column values
* after execution of an {@code insert} statement, using the JDBC method
* {@link Connection#prepareStatement(String, String[])}.
*
* Support for returning the generated value of an identity column via the JDBC
* method {@link Connection#prepareStatement(String, int)} is insufficient here.
*
* @return {@code true} if {@link org.hibernate.id.insert.GetGeneratedKeysDelegate}
* works for any sort of primary key column (not just identity columns), or
* {@code false} if {@code GetGeneratedKeysDelegate} does not work, or only
* works for specialized identity/"autoincrement" columns
*
* @see org.hibernate.generator.OnExecutionGenerator#getGeneratedIdentifierDelegate
* @see org.hibernate.id.insert.GetGeneratedKeysDelegate
*
* @since 6.2
*/
public boolean supportsInsertReturningGeneratedKeys() {
return false;
}
/**
* Does this dialect support the given {@code FETCH} clause type.
*
* @param type The fetch clause type
* @return {@code true} if the underlying database supports the given
* fetch clause type, {@code false} otherwise.
* The default is {@code false}.
*/
public boolean supportsFetchClause(FetchClauseType type) {
return false;
}
/**
* Does this dialect support window functions like {@code row_number() over (..)}?
*
* @return {@code true} if the underlying database supports window
* functions, {@code false} otherwise.
* The default is {@code false}.
*/
public boolean supportsWindowFunctions() {
return false;
}
/**
* Does this dialect support the SQL {@code lateral} keyword or a
* proprietary alternative?
*
* @return {@code true} if the underlying database supports lateral,
* {@code false} otherwise. The default is {@code false}.
*/
public boolean supportsLateral() {
return false;
}
/**
* The {@link CallableStatementSupport} for this database.
* Does this database support returning cursors?
*/
public CallableStatementSupport getCallableStatementSupport() {
// most databases do not support returning cursors (ref_cursor)...
return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE;
}
/**
* The {@linkplain NameQualifierSupport support for qualified identifiers}.
*
* By default, decide based on {@link DatabaseMetaData}.
*
* @return The {@link NameQualifierSupport}, or null to use {@link DatabaseMetaData}.
*/
public NameQualifierSupport getNameQualifierSupport() {
return null;
}
/**
* The strategy used to determine the appropriate number of keys
* to load in a single SQL query with multi-key loading.
* @see org.hibernate.Session#byMultipleIds
* @see org.hibernate.Session#byMultipleNaturalId
*/
public MultiKeyLoadSizingStrategy getMultiKeyLoadSizingStrategy() {
return STANDARD_MULTI_KEY_LOAD_SIZING_STRATEGY;
}
/**
* The strategy used to determine the appropriate number of keys
* to load in a single SQL query with batch-fetch loading.
*
* @implNote By default, the same as {@linkplain #getMultiKeyLoadSizingStrategy}
*
* @see org.hibernate.annotations.BatchSize
*/
public MultiKeyLoadSizingStrategy getBatchLoadSizingStrategy() {
return getMultiKeyLoadSizingStrategy();
}
protected final MultiKeyLoadSizingStrategy STANDARD_MULTI_KEY_LOAD_SIZING_STRATEGY = (numberOfColumns, numberOfKeys, pad) -> {
numberOfKeys = pad ? MathHelper.ceilingPowerOfTwo( numberOfKeys ) : numberOfKeys;
final long parameterCount = (long) numberOfColumns * numberOfKeys;
final int limit = getParameterCountLimit();
if ( limit > 0 ) {
// the Dialect reported a limit - see if the parameter count exceeds the limit
if ( parameterCount >= limit ) {
return limit / numberOfColumns;
}
}
return numberOfKeys;
};
/**
* Is JDBC statement warning logging enabled by default?
*
* @since 5.1
*/
public boolean isJdbcLogWarningsEnabledByDefault() {
return true;
}
public void augmentPhysicalTableTypes(List tableTypesList) {
// nothing to do
}
public void augmentRecognizedTableTypes(List tableTypesList) {
// nothing to do
}
/**
* Does is dialect support {@code partition by}?
*
* @since 5.2
*/
public boolean supportsPartitionBy() {
return false;
}
/**
* Override {@link DatabaseMetaData#supportsNamedParameters()}.
*
* @throws SQLException Accessing the {@link DatabaseMetaData} cause
* an exception. Just rethrow and Hibernate will
* handle it.
*/
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException {
return databaseMetaData != null && databaseMetaData.supportsNamedParameters();
}
/**
* Determines whether this database requires the use of explicitly
* nationalized character (Unicode) data types.
*
* That is, whether the use of {@link Types#NCHAR}, {@link Types#NVARCHAR},
* and {@link Types#NCLOB} is required for nationalized character data.
*/
public NationalizationSupport getNationalizationSupport() {
return NationalizationSupport.EXPLICIT;
}
/**
* How does this dialect support aggregate types like {@link SqlTypes#STRUCT}.
*
* @since 6.2
*/
public AggregateSupport getAggregateSupport() {
return AggregateSupportImpl.INSTANCE;
}
/**
* Does this database have native support for ANSI SQL standard arrays which
* are expressed in terms of the element type name: {@code integer array}.
*
* @implNote Oracle doesn't have this; we must instead use named array types.
*
* @return boolean
* @since 6.1
*/
public boolean supportsStandardArrays() {
return false;
}
/**
* Does this database prefer to use array types for multi-valued parameters.
*
* @return boolean
*
* @since 6.3
*/
public boolean useArrayForMultiValuedParameters() {
return supportsStandardArrays() && getPreferredSqlTypeCodeForArray() == SqlTypes.ARRAY;
}
/**
* The SQL type name for the array type with elements of the given type name.
*
* The ANSI-standard syntax is {@code integer array}.
*
* @since 6.1
*/
public String getArrayTypeName(String javaElementTypeName, String elementTypeName, Integer maxLength) {
if ( supportsStandardArrays() ) {
return maxLength == null
? elementTypeName + " array"
: elementTypeName + " array[" + maxLength + "]";
}
else {
return null;
}
}
/**
* Append an array literal with the given elements to the given {@link SqlAppender}.
*/
public void appendArrayLiteral(
SqlAppender appender,
Object[] literal,
JdbcLiteralFormatter