All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.dialect.Dialect Maven / Gradle / Ivy

There is a newer version: 6.5.0.CR2
Show newest version
/*
 * 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.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.hibernate.HibernateException;
import org.hibernate.LockMode;
import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.NullPrecedence;
import org.hibernate.ScrollMode;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.function.CastFunction;
import org.hibernate.dialect.function.SQLFunction;
import org.hibernate.dialect.function.SQLFunctionTemplate;
import org.hibernate.dialect.function.StandardAnsiSqlAggregationFunctions;
import org.hibernate.dialect.function.StandardSQLFunction;
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.LegacyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.LobCreator;
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.NameQualifierSupport;
import org.hibernate.engine.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.QueryParameters;
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.ViolatedConstraintNameExtracter;
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
import org.hibernate.hql.spi.id.persistent.PersistentTableBulkIdStrategy;
import org.hibernate.id.IdentityGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.io.StreamCopier;
import org.hibernate.loader.BatchLoadSizingStrategy;
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.persister.entity.Lockable;
import org.hibernate.procedure.internal.StandardCallableStatementSupport;
import org.hibernate.procedure.spi.CallableStatementSupport;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.sql.ANSICaseFragment;
import org.hibernate.sql.ANSIJoinFragment;
import org.hibernate.sql.CaseFragment;
import org.hibernate.sql.ForUpdateFragment;
import org.hibernate.sql.JoinFragment;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
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.StandardTableExporter;
import org.hibernate.tool.schema.internal.StandardUniqueKeyExporter;
import org.hibernate.tool.schema.spi.Exporter;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.sql.ClobTypeDescriptor;
import org.hibernate.type.descriptor.sql.SqlTypeDescriptor;

/**
 * Represents a dialect of SQL implemented by a particular RDBMS.  Subclasses implement Hibernate compatibility
 * with different systems.  Subclasses should provide a public default constructor that register a set of type
 * mappings and default Hibernate properties.  Subclasses should be immutable.
 *
 * @author Gavin King, David Channon
 */
@SuppressWarnings("deprecation")
public abstract class Dialect implements ConversionContext {
	private static final CoreMessageLogger LOG = CoreLogging.messageLogger( Dialect.class );

	/**
	 * Defines a default batch size constant
	 */
	public static final String DEFAULT_BATCH_SIZE = "15";

	/**
	 * Defines a "no batching" batch size constant
	 */
	public static final String NO_BATCH = "0";

	/**
	 * 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 final TypeNames typeNames = new TypeNames();
	private final TypeNames hibernateTypeNames = new TypeNames();

	private final Properties properties = new Properties();
	private final Map sqlFunctions = new HashMap<>();
	private final Set sqlKeywords = new HashSet<>();

	private final UniqueDelegate uniqueDelegate;

	private boolean legacyLimitHandlerBehavior;

	// constructors and factory methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	protected Dialect() {
		LOG.usingDialect( this );
		StandardAnsiSqlAggregationFunctions.primeFunctionMap( sqlFunctions );

		// standard sql92 functions (can be overridden by subclasses)
		registerFunction( "substring", new SQLFunctionTemplate( StandardBasicTypes.STRING, "substring(?1, ?2, ?3)" ) );
		registerFunction( "locate", new SQLFunctionTemplate( StandardBasicTypes.INTEGER, "locate(?1, ?2, ?3)" ) );
		registerFunction( "trim", new SQLFunctionTemplate( StandardBasicTypes.STRING, "trim(?1 ?2 ?3 ?4)" ) );
		registerFunction( "length", new StandardSQLFunction( "length", StandardBasicTypes.INTEGER ) );
		registerFunction( "bit_length", new StandardSQLFunction( "bit_length", StandardBasicTypes.INTEGER ) );
		registerFunction( "coalesce", new StandardSQLFunction( "coalesce" ) );
		registerFunction( "nullif", new StandardSQLFunction( "nullif" ) );
		registerFunction( "abs", new StandardSQLFunction( "abs" ) );
		registerFunction( "mod", new StandardSQLFunction( "mod", StandardBasicTypes.INTEGER) );
		registerFunction( "sqrt", new StandardSQLFunction( "sqrt", StandardBasicTypes.DOUBLE) );
		registerFunction( "upper", new StandardSQLFunction("upper") );
		registerFunction( "lower", new StandardSQLFunction("lower") );
		registerFunction( "cast", new CastFunction() );
		registerFunction( "extract", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(?1 ?2 ?3)") );

		//map second/minute/hour/day/month/year to ANSI extract(), override on subclasses
		registerFunction( "second", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(second from ?1)") );
		registerFunction( "minute", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(minute from ?1)") );
		registerFunction( "hour", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(hour from ?1)") );
		registerFunction( "day", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(day from ?1)") );
		registerFunction( "month", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(month from ?1)") );
		registerFunction( "year", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "extract(year from ?1)") );

		registerFunction( "str", new SQLFunctionTemplate(StandardBasicTypes.STRING, "cast(?1 as char)") );

		registerColumnType( Types.BIT, "bit" );
		registerColumnType( Types.BOOLEAN, "boolean" );
		registerColumnType( Types.TINYINT, "tinyint" );
		registerColumnType( Types.SMALLINT, "smallint" );
		registerColumnType( Types.INTEGER, "integer" );
		registerColumnType( Types.BIGINT, "bigint" );
		registerColumnType( Types.FLOAT, "float($p)" );
		registerColumnType( Types.DOUBLE, "double precision" );
		registerColumnType( Types.NUMERIC, "numeric($p,$s)" );
		registerColumnType( Types.REAL, "real" );

		registerColumnType( Types.DATE, "date" );
		registerColumnType( Types.TIME, "time" );
		registerColumnType( Types.TIMESTAMP, "timestamp" );

		registerColumnType( Types.VARBINARY, "bit varying($l)" );
		registerColumnType( Types.LONGVARBINARY, "bit varying($l)" );
		registerColumnType( Types.BLOB, "blob" );

		registerColumnType( Types.CHAR, "char($l)" );
		registerColumnType( Types.VARCHAR, "varchar($l)" );
		registerColumnType( Types.LONGVARCHAR, "varchar($l)" );
		registerColumnType( Types.CLOB, "clob" );

		registerColumnType( Types.NCHAR, "nchar($l)" );
		registerColumnType( Types.NVARCHAR, "nvarchar($l)" );
		registerColumnType( Types.LONGNVARCHAR, "nvarchar($l)" );
		registerColumnType( Types.NCLOB, "nclob" );

		// register hibernate types for default use in scalar sqlquery type auto detection
		registerHibernateType( Types.BIGINT, StandardBasicTypes.BIG_INTEGER.getName() );
		registerHibernateType( Types.BINARY, StandardBasicTypes.BINARY.getName() );
		registerHibernateType( Types.BIT, StandardBasicTypes.BOOLEAN.getName() );
		registerHibernateType( Types.BOOLEAN, StandardBasicTypes.BOOLEAN.getName() );
		registerHibernateType( Types.CHAR, StandardBasicTypes.CHARACTER.getName() );
		registerHibernateType( Types.CHAR, 1, StandardBasicTypes.CHARACTER.getName() );
		registerHibernateType( Types.CHAR, 255, StandardBasicTypes.STRING.getName() );
		registerHibernateType( Types.DATE, StandardBasicTypes.DATE.getName() );
		registerHibernateType( Types.DOUBLE, StandardBasicTypes.DOUBLE.getName() );
		registerHibernateType( Types.FLOAT, StandardBasicTypes.FLOAT.getName() );
		registerHibernateType( Types.INTEGER, StandardBasicTypes.INTEGER.getName() );
		registerHibernateType( Types.SMALLINT, StandardBasicTypes.SHORT.getName() );
		registerHibernateType( Types.TINYINT, StandardBasicTypes.BYTE.getName() );
		registerHibernateType( Types.TIME, StandardBasicTypes.TIME.getName() );
		registerHibernateType( Types.TIMESTAMP, StandardBasicTypes.TIMESTAMP.getName() );
		registerHibernateType( Types.VARCHAR, StandardBasicTypes.STRING.getName() );
		registerHibernateType( Types.NVARCHAR, StandardBasicTypes.NSTRING.getName() );
		registerHibernateType( Types.VARBINARY, StandardBasicTypes.BINARY.getName() );
		registerHibernateType( Types.LONGVARCHAR, StandardBasicTypes.TEXT.getName() );
		registerHibernateType( Types.LONGVARBINARY, StandardBasicTypes.IMAGE.getName() );
		registerHibernateType( Types.NUMERIC, StandardBasicTypes.BIG_DECIMAL.getName() );
		registerHibernateType( Types.DECIMAL, StandardBasicTypes.BIG_DECIMAL.getName() );
		registerHibernateType( Types.BLOB, StandardBasicTypes.BLOB.getName() );
		registerHibernateType( Types.CLOB, StandardBasicTypes.CLOB.getName() );
		registerHibernateType( Types.REAL, StandardBasicTypes.FLOAT.getName() );

		if(supportsPartitionBy()) {
			registerKeyword( "PARTITION" );
		}

		uniqueDelegate = new DefaultUniqueDelegate( this );
	}

	/**
	 * Get an instance of the dialect specified by the current System properties.
	 *
	 * @return The specified Dialect
	 * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
	 */
	public static Dialect getDialect() throws HibernateException {
		return instantiateDialect( Environment.getProperties().getProperty( Environment.DIALECT ) );
	}

	/**
	 * Get an instance of the dialect specified by the given properties or by
	 * the current System properties.
	 *
	 * @param props The properties to use for finding the dialect class to use.
	 * @return The specified Dialect
	 * @throws HibernateException If no dialect was specified, or if it could not be instantiated.
	 */
	public static Dialect getDialect(Properties props) throws HibernateException {
		final String dialectName = props.getProperty( Environment.DIALECT );
		if ( dialectName == null ) {
			return getDialect();
		}
		return instantiateDialect( dialectName );
	}

	private static Dialect instantiateDialect(String dialectName) throws HibernateException {
		if ( dialectName == null ) {
			throw new HibernateException( "The dialect was not set. Set the property hibernate.dialect." );
		}
		try {
			return (Dialect) ReflectHelper.classForName( dialectName ).newInstance();
		}
		catch ( ClassNotFoundException cnfe ) {
			throw new HibernateException( "Dialect class not found: " + dialectName );
		}
		catch ( Exception e ) {
			throw new HibernateException( "Could not instantiate given dialect class: " + dialectName, e );
		}
	}

	/**
	 * Retrieve a set of default Hibernate properties for this database.
	 *
	 * @return a set of Hibernate properties
	 */
	public final Properties getDefaultProperties() {
		return properties;
	}

	@Override
	public String toString() {
		return getClass().getName();
	}


	// database type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	/**
	 * Allows the Dialect to contribute additional types
	 *
	 * @param typeContributions Callback to contribute the types
	 * @param serviceRegistry The service registry
	 */
	public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
		resolveLegacyLimitHandlerBehavior( serviceRegistry );
	}

	/**
	 * Get the name of the database type associated with the given
	 * {@link java.sql.Types} typecode.
	 *
	 * @param code The {@link java.sql.Types} typecode
	 * @return the database type name
	 * @throws HibernateException If no mapping was specified for that type.
	 */
	public String getTypeName(int code) throws HibernateException {
		final String result = typeNames.get( code );
		if ( result == null ) {
			throw new HibernateException( "No default type mapping for (java.sql.Types) " + code );
		}
		return result;
	}

	/**
	 * Get the name of the database type associated with the given
	 * {@link java.sql.Types} typecode with the given storage specification
	 * parameters.
	 *
	 * @param code The {@link java.sql.Types} typecode
	 * @param length The datatype length
	 * @param precision The datatype precision
	 * @param scale The datatype scale
	 * @return the database type name
	 * @throws HibernateException If no mapping was specified for that type.
	 */
	public String getTypeName(int code, long length, int precision, int scale) throws HibernateException {
		final String result = typeNames.get( code, length, precision, scale );
		if ( result == null ) {
			throw new HibernateException(
					String.format( "No type mapping for java.sql.Types code: %s, length: %s", code, length )
			);
		}
		return result;
	}

	/**
	 * Get the name of the database type appropriate for casting operations
	 * (via the CAST() SQL function) for the given {@link java.sql.Types} typecode.
	 *
	 * @param code The {@link java.sql.Types} typecode
	 * @return The database type name
	 */
	public String getCastTypeName(int code) {
		return getTypeName( code, Column.DEFAULT_LENGTH, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE );
	}

	/**
	 * Return an expression casting the value to the specified type
	 *
	 * @param value The value to cast
	 * @param jdbcTypeCode The JDBC type code to cast to
	 * @param length The type length
	 * @param precision The type precision
	 * @param scale The type scale
	 *
	 * @return The cast expression
	 */
	public String cast(String value, int jdbcTypeCode, int length, int precision, int scale) {
		if ( jdbcTypeCode == Types.CHAR ) {
			return "cast(" + value + " as char(" + length + "))";
		}
		else {
			return "cast(" + value + "as " + getTypeName( jdbcTypeCode, length, precision, scale ) + ")";
		}
	}

	/**
	 * Return an expression casting the value to the specified type.  Simply calls
	 * {@link #cast(String, int, int, int, int)} passing {@link Column#DEFAULT_PRECISION} and
	 * {@link Column#DEFAULT_SCALE} as the precision/scale.
	 *
	 * @param value The value to cast
	 * @param jdbcTypeCode The JDBC type code to cast to
	 * @param length The type length
	 *
	 * @return The cast expression
	 */
	public String cast(String value, int jdbcTypeCode, int length) {
		return cast( value, jdbcTypeCode, length, Column.DEFAULT_PRECISION, Column.DEFAULT_SCALE );
	}

	/**
	 * Return an expression casting the value to the specified type.  Simply calls
	 * {@link #cast(String, int, int, int, int)} passing {@link Column#DEFAULT_LENGTH} as the length
	 *
	 * @param value The value to cast
	 * @param jdbcTypeCode The JDBC type code to cast to
	 * @param precision The type precision
	 * @param scale The type scale
	 *
	 * @return The cast expression
	 */
	public String cast(String value, int jdbcTypeCode, int precision, int scale) {
		return cast( value, jdbcTypeCode, Column.DEFAULT_LENGTH, precision, scale );
	}

	/**
	 * Subclasses register a type name for the given type code and maximum
	 * column length. $l in the type name with be replaced by the
	 * column length (if appropriate).
	 *
	 * @param code The {@link java.sql.Types} typecode
	 * @param capacity The maximum length of database type
	 * @param name The database type name
	 */
	protected void registerColumnType(int code, long capacity, String name) {
		typeNames.put( code, capacity, name );
	}

	/**
	 * Subclasses register a type name for the given type code. $l in
	 * the type name with be replaced by the column length (if appropriate).
	 *
	 * @param code The {@link java.sql.Types} typecode
	 * @param name The database type name
	 */
	protected void registerColumnType(int code, String name) {
		typeNames.put( code, name );
	}

	/**
	 * Allows the dialect to override a {@link SqlTypeDescriptor}.
	 * 

* If the passed {@code sqlTypeDescriptor} allows itself to be remapped (per * {@link org.hibernate.type.descriptor.sql.SqlTypeDescriptor#canBeRemapped()}), then this method uses * {@link #getSqlTypeDescriptorOverride} to get an optional override based on the SQL code returned by * {@link SqlTypeDescriptor#getSqlType()}. *

* If this dialect does not provide an override or if the {@code sqlTypeDescriptor} does not allow itself to be * remapped, then this method simply returns the original passed {@code sqlTypeDescriptor} * * @param sqlTypeDescriptor The {@link SqlTypeDescriptor} to override * @return The {@link SqlTypeDescriptor} that should be used for this dialect; * if there is no override, then original {@code sqlTypeDescriptor} is returned. * @throws IllegalArgumentException if {@code sqlTypeDescriptor} is null. * * @see #getSqlTypeDescriptorOverride */ public SqlTypeDescriptor remapSqlTypeDescriptor(SqlTypeDescriptor sqlTypeDescriptor) { if ( sqlTypeDescriptor == null ) { throw new IllegalArgumentException( "sqlTypeDescriptor is null" ); } if ( ! sqlTypeDescriptor.canBeRemapped() ) { return sqlTypeDescriptor; } final SqlTypeDescriptor overridden = getSqlTypeDescriptorOverride( sqlTypeDescriptor.getSqlType() ); return overridden == null ? sqlTypeDescriptor : overridden; } /** * Returns the {@link SqlTypeDescriptor} that should be used to handle the given JDBC type code. Returns * {@code null} if there is no override. * * @param sqlCode A {@link Types} constant indicating the SQL column type * @return The {@link SqlTypeDescriptor} to use as an override, or {@code null} if there is no override. */ protected SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) { SqlTypeDescriptor descriptor; switch ( sqlCode ) { case Types.CLOB: { descriptor = useInputStreamToInsertBlob() ? ClobTypeDescriptor.STREAM_BINDING : null; break; } default: { descriptor = null; break; } } return descriptor; } /** * The legacy behavior of Hibernate. LOBs are not processed by merge */ @SuppressWarnings( {"UnusedDeclaration"}) 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; } }; /** * Merge strategy based on transferring contents based on streams. */ @SuppressWarnings( {"UnusedDeclaration"}) 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().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().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().getSQLExceptionHelper().convert( e, "unable to merge NCLOB data" ); } } else { return NEW_LOCATOR_LOB_MERGE_STRATEGY.mergeNClob( original, target, session ); } } }; /** * Merge strategy 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; } try { final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session ); return original == null ? lobCreator.createBlob( ArrayHelper.EMPTY_BYTE_ARRAY ) : lobCreator.createBlob( original.getBinaryStream(), original.length() ); } catch (SQLException e) { throw session.getFactory().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; } try { final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session ); return original == null ? lobCreator.createClob( "" ) : lobCreator.createClob( original.getCharacterStream(), original.length() ); } catch (SQLException e) { throw session.getFactory().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; } try { final LobCreator lobCreator = session.getFactory().getServiceRegistry().getService( JdbcServices.class ).getLobCreator( session ); return original == null ? lobCreator.createNClob( "" ) : lobCreator.createNClob( original.getCharacterStream(), original.length() ); } catch (SQLException e) { throw session.getFactory().getSQLExceptionHelper().convert( e, "unable to merge NCLOB data" ); } } }; public LobMergeStrategy getLobMergeStrategy() { return NEW_LOCATOR_LOB_MERGE_STRATEGY; } // hibernate type mapping support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Get the name of the Hibernate {@link org.hibernate.type.Type} associated with the given * {@link java.sql.Types} type code. * * @param code The {@link java.sql.Types} type code * @return The Hibernate {@link org.hibernate.type.Type} name. * @throws HibernateException If no mapping was specified for that type. */ @SuppressWarnings( {"UnusedDeclaration"}) public String getHibernateTypeName(int code) throws HibernateException { final String result = hibernateTypeNames.get( code ); if ( result == null ) { throw new HibernateException( "No Hibernate type mapping for java.sql.Types code: " + code ); } return result; } /** * Whether or not the given type name has been registered for this dialect (including both hibernate type names and * custom-registered type names). * * @param typeName the type name. * * @return true if the given string has been registered either as a hibernate type or as a custom-registered one */ public boolean isTypeNameRegistered(final String typeName) { return this.typeNames.containsTypeName( typeName ); } /** * Get the name of the Hibernate {@link org.hibernate.type.Type} associated * with the given {@link java.sql.Types} typecode with the given storage * specification parameters. * * @param code The {@link java.sql.Types} typecode * @param length The datatype length * @param precision The datatype precision * @param scale The datatype scale * @return The Hibernate {@link org.hibernate.type.Type} name. * @throws HibernateException If no mapping was specified for that type. */ public String getHibernateTypeName(int code, int length, int precision, int scale) throws HibernateException { final String result = hibernateTypeNames.get( code, length, precision, scale ); if ( result == null ) { throw new HibernateException( String.format( "No Hibernate type mapping for type [code=%s, length=%s]", code, length ) ); } return result; } /** * Registers a Hibernate {@link org.hibernate.type.Type} name for the given * {@link java.sql.Types} type code and maximum column length. * * @param code The {@link java.sql.Types} typecode * @param capacity The maximum length of database type * @param name The Hibernate {@link org.hibernate.type.Type} name */ protected void registerHibernateType(int code, long capacity, String name) { hibernateTypeNames.put( code, capacity, name ); } /** * Registers a Hibernate {@link org.hibernate.type.Type} name for the given * {@link java.sql.Types} type code. * * @param code The {@link java.sql.Types} typecode * @param name The Hibernate {@link org.hibernate.type.Type} name */ protected void registerHibernateType(int code, String name) { hibernateTypeNames.put( code, name ); } // function support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ protected void registerFunction(String name, SQLFunction function) { // HHH-7721: SQLFunctionRegistry expects all lowercase. Enforce, // just in case a user's customer dialect uses mixed cases. sqlFunctions.put( name.toLowerCase( Locale.ROOT ), function ); } /** * Retrieves a map of the dialect's registered functions * (functionName => {@link org.hibernate.dialect.function.SQLFunction}). * * @return The map of registered functions. */ public final Map getFunctions() { return sqlFunctions; } // native identifier generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * The class (which implements {@link org.hibernate.id.IdentifierGenerator}) * which acts as this dialects native generation strategy. *

* Comes into play whenever the user specifies the native generator. * * @return The native generator class. * @deprecated use {@link #getNativeIdentifierGeneratorStrategy()} instead */ @Deprecated public Class getNativeIdentifierGeneratorClass() { if ( getIdentityColumnSupport().supportsIdentityColumns() ) { return IdentityGenerator.class; } else { return SequenceStyleGenerator.class; } } /** * Resolves the native generation strategy associated to this dialect. *

* Comes into play whenever the user specifies the native generator. * * @return The native generator strategy. */ public String getNativeIdentifierGeneratorStrategy() { if ( getIdentityColumnSupport().supportsIdentityColumns() ) { return "identity"; } else { return "sequence"; } } // IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Get the appropriate {@link IdentityColumnSupport} * * @return the IdentityColumnSupport * @since 5.1 */ public IdentityColumnSupport getIdentityColumnSupport(){ return new IdentityColumnSupportImpl(); } // SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Does this dialect support sequences? * * @return True if sequences supported; false otherwise. */ public boolean supportsSequences() { return false; } /** * Does this dialect support "pooled" sequences. Not aware of a better * name for this. Essentially can we specify the initial and increment values? * * @return True if such "pooled" sequences are supported; false otherwise. * @see #getCreateSequenceStrings(String, int, int) * @see #getCreateSequenceString(String, int, int) */ public boolean supportsPooledSequences() { return false; } /** * Generate the appropriate select statement to to retrieve the next value * of a sequence. *

* This should be a "stand alone" select statement. * * @param sequenceName the name of the sequence * @return String The "nextval" select string. * @throws MappingException If sequences are not supported. */ public String getSequenceNextValString(String sequenceName) throws MappingException { throw new MappingException( getClass().getName() + " does not support sequences" ); } /** * Generate the select expression fragment that will retrieve the next * value of a sequence as part of another (typically DML) statement. *

* This differs from {@link #getSequenceNextValString(String)} in that this * should return an expression usable within another statement. * * @param sequenceName the name of the sequence * @return The "nextval" fragment. * @throws MappingException If sequences are not supported. */ public String getSelectSequenceNextValString(String sequenceName) throws MappingException { throw new MappingException( getClass().getName() + " does not support sequences" ); } /** * The multiline script used to create a sequence. * * @param sequenceName The name of the sequence * @return The sequence creation commands * @throws MappingException If sequences are not supported. * @deprecated Use {@link #getCreateSequenceString(String, int, int)} instead */ @Deprecated public String[] getCreateSequenceStrings(String sequenceName) throws MappingException { return new String[] { getCreateSequenceString( sequenceName ) }; } /** * An optional multi-line form for databases which {@link #supportsPooledSequences()}. * * @param sequenceName The name of the sequence * @param initialValue The initial value to apply to 'create sequence' statement * @param incrementSize The increment value to apply to 'create sequence' statement * @return The sequence creation commands * @throws MappingException If sequences are not supported. */ public String[] getCreateSequenceStrings(String sequenceName, int initialValue, int incrementSize) throws MappingException { return new String[] { getCreateSequenceString( sequenceName, initialValue, incrementSize ) }; } /** * Typically dialects which support sequences can create a sequence * with a single command. This is convenience form of * {@link #getCreateSequenceStrings} to help facilitate that. *

* Dialects which support sequences and can create a sequence in a * single command need *only* override this method. Dialects * which support sequences but require multiple commands to create * a sequence should instead override {@link #getCreateSequenceStrings}. * * @param sequenceName The name of the sequence * @return The sequence creation command * @throws MappingException If sequences are not supported. */ protected String getCreateSequenceString(String sequenceName) throws MappingException { throw new MappingException( getClass().getName() + " does not support sequences" ); } /** * Overloaded form of {@link #getCreateSequenceString(String)}, additionally * taking the initial value and increment size to be applied to the sequence * definition. *

* The default definition is to suffix {@link #getCreateSequenceString(String)} * with the string: " start with {initialValue} increment by {incrementSize}" where * {initialValue} and {incrementSize} are replacement placeholders. Generally * dialects should only need to override this method if different key phrases * are used to apply the allocation information. * * @param sequenceName The name of the sequence * @param initialValue The initial value to apply to 'create sequence' statement * @param incrementSize The increment value to apply to 'create sequence' statement * @return The sequence creation command * @throws MappingException If sequences are not supported. */ protected String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize) throws MappingException { if ( supportsPooledSequences() ) { return getCreateSequenceString( sequenceName ) + " start with " + initialValue + " increment by " + incrementSize; } throw new MappingException( getClass().getName() + " does not support pooled sequences" ); } /** * The multiline script used to drop a sequence. * * @param sequenceName The name of the sequence * @return The sequence drop commands * @throws MappingException If sequences are not supported. */ public String[] getDropSequenceStrings(String sequenceName) throws MappingException { return new String[]{getDropSequenceString( sequenceName )}; } /** * Typically dialects which support sequences can drop a sequence * with a single command. This is convenience form of * {@link #getDropSequenceStrings} to help facilitate that. *

* Dialects which support sequences and can drop a sequence in a * single command need *only* override this method. Dialects * which support sequences but require multiple commands to drop * a sequence should instead override {@link #getDropSequenceStrings}. * * @param sequenceName The name of the sequence * @return The sequence drop commands * @throws MappingException If sequences are not supported. */ protected String getDropSequenceString(String sequenceName) throws MappingException { throw new MappingException( getClass().getName() + " does not support sequences" ); } /** * Get the select command used retrieve the names of all sequences. * * @return The select command; or null if sequences are not supported. * @see org.hibernate.tool.hbm2ddl.SchemaUpdate */ public String getQuerySequencesString() { return null; } public SequenceInformationExtractor getSequenceInformationExtractor() { if ( getQuerySequencesString() == null ) { return SequenceInformationExtractorNoOpImpl.INSTANCE; } else { return SequenceInformationExtractorLegacyImpl.INSTANCE; } } // GUID support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Get the command used to select a GUID from the underlying database. *

* Optional operation. * * @return The appropriate command. */ public String getSelectGUIDString() { throw new UnsupportedOperationException( getClass().getName() + " does not support GUIDs" ); } // limit/offset support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Returns the delegate managing LIMIT clause. * * @return LIMIT clause delegate. */ public LimitHandler getLimitHandler() { return new LegacyLimitHandler( this ); } /** * Does this dialect support some form of limiting query results * via a SQL clause? * * @return True if this dialect supports some form of LIMIT. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean supportsLimit() { return false; } /** * Does this dialect's LIMIT support (if any) additionally * support specifying an offset? * * @return True if the dialect supports an offset within the limit support. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean supportsLimitOffset() { return supportsLimit(); } /** * Does this dialect support bind variables (i.e., prepared statement * parameters) for its limit/offset? * * @return True if bind variables can be used; false otherwise. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean supportsVariableLimit() { return supportsLimit(); } /** * ANSI SQL defines the LIMIT clause to be in the form LIMIT offset, limit. * Does this dialect require us to bind the parameters in reverse order? * * @return true if the correct order is limit, offset * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean bindLimitParametersInReverseOrder() { return false; } /** * Does the LIMIT clause come at the start of the * SELECT statement, rather than at the end? * * @return true if limit parameters should come before other parameters * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean bindLimitParametersFirst() { return false; } /** * Does the LIMIT clause take a "maximum" row number instead * of a total number of returned rows? *

* This is easiest understood via an example. Consider you have a table * with 20 rows, but you only want to retrieve rows number 11 through 20. * Generally, a limit with offset would say that the offset = 11 and the * limit = 10 (we only want 10 rows at a time); this is specifying the * total number of returned rows. Some dialects require that we instead * specify offset = 11 and limit = 20, where 20 is the "last" row we want * relative to offset (i.e. total number of rows = 20 - 11 = 9) *

* So essentially, is limit relative from offset? Or is limit absolute? * * @return True if limit is relative from offset; false otherwise. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean useMaxForLimit() { return false; } /** * Generally, if there is no limit applied to a Hibernate query we do not apply any limits * to the SQL query. This option forces that the limit be written to the SQL query. * * @return True to force limit into SQL query even if none specified in Hibernate query; false otherwise. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public boolean forceLimitUsage() { return false; } /** * Given a limit and an offset, apply the limit clause to the query. * * @param query The query to which to apply the limit. * @param offset The offset of the limit * @param limit The limit of the limit ;) * @return The modified query statement with the limit applied. * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public String getLimitString(String query, int offset, int limit) { return getLimitString( query, ( offset > 0 || forceLimitUsage() ) ); } /** * Apply s limit clause to the query. *

* Typically dialects utilize {@link #supportsVariableLimit() variable} * limit clauses when they support limits. Thus, when building the * select command we do not actually need to know the limit or the offest * since we will just be using placeholders. *

* Here we do still pass along whether or not an offset was specified * so that dialects not supporting offsets can generate proper exceptions. * In general, dialects will override one or the other of this method and * {@link #getLimitString(String, int, int)}. * * @param query The query to which to apply the limit. * @param hasOffset Is the query requesting an offset? * @return the modified SQL * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated protected String getLimitString(String query, boolean hasOffset) { throw new UnsupportedOperationException( "Paged queries not supported by " + getClass().getName()); } /** * Hibernate APIs explicitly state that setFirstResult() should be a zero-based offset. Here we allow the * Dialect a chance to convert that value based on what the underlying db or driver will expect. *

* NOTE: what gets passed into {@link #getLimitString(String,int,int)} is the zero-based offset. Dialects which * do not {@link #supportsVariableLimit} should take care to perform any needed first-row-conversion calls prior * to injecting the limit values into the SQL string. * * @param zeroBasedFirstResult The user-supplied, zero-based first row offset. * @return The corresponding db/dialect specific offset. * @see org.hibernate.Query#setFirstResult * @see org.hibernate.Criteria#setFirstResult * @deprecated {@link #getLimitHandler()} should be overridden instead. */ @Deprecated public int convertToFirstRowValue(int zeroBasedFirstResult) { return zeroBasedFirstResult; } // lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Informational metadata about whether this dialect is known to support * specifying timeouts for requested lock acquisitions. * * @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 SQL string as parameters. The implication * is that Hibernate will need to bind the timeout value as a parameter * in the {@link java.sql.PreparedStatement}. If true, the param position * is always handled as the last parameter; if the dialect specifies the * lock timeout elsewhere in the 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 SQL * string as a parameter; false otherwise. */ public boolean isLockTimeoutParameterized() { return false; } /** * Get a strategy instance which knows how to acquire a database-level lock * of the specified mode for this dialect. * * @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 LockOptions (lockMode, timeout), determine the appropriate for update fragment to use. * * @param lockOptions contains the lock mode to apply. * @return The appropriate for update fragment. */ public String getForUpdateString(LockOptions lockOptions) { final LockMode lockMode = lockOptions.getLockMode(); return getForUpdateString( lockMode, lockOptions.getTimeOut() ); } @SuppressWarnings( {"deprecation"}) private String getForUpdateString(LockMode lockMode, int timeout){ switch ( lockMode ) { case UPGRADE: return getForUpdateString(); case PESSIMISTIC_READ: return getReadLockString( timeout ); case PESSIMISTIC_WRITE: return getWriteLockString( timeout ); case UPGRADE_NOWAIT: case FORCE: case PESSIMISTIC_FORCE_INCREMENT: return getForUpdateNowaitString(); case UPGRADE_SKIPLOCKED: return getForUpdateSkipLockedString(); default: return ""; } } /** * Given a lock mode, determine the appropriate for update fragment to use. * * @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 SELECT statements to acquire locks * for this dialect. * * @return The appropriate FOR UPDATE clause string. */ public String getForUpdateString() { return " for update"; } /** * Get the string to append to SELECT statements to acquire WRITE locks * for this dialect. Location of the of the returned string is treated * the same as getForUpdateString. * * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. * @return The appropriate LOCK clause string. */ public String getWriteLockString(int timeout) { return getForUpdateString(); } /** * Get the string to append to SELECT statements to acquire WRITE locks * for this dialect given the aliases of the columns to be write locked. * Location of the of the returned string is treated * the same as 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 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 SELECT statements to acquire READ locks * for this dialect. Location of the of the returned string is treated * the same as getForUpdateString. * * @param timeout in milliseconds, -1 for indefinite wait and 0 for no wait. * @return The appropriate LOCK clause string. */ public String getReadLockString(int timeout) { return getForUpdateString(); } /** * Get the string to append to SELECT statements to acquire READ locks * for this dialect given the aliases of the columns to be read locked. * Location of the of the returned string is treated * the same as 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 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 ); } /** * Is FOR UPDATE OF syntax supported? * * @return True if the database supports FOR UPDATE OF syntax; * false otherwise. */ public boolean forUpdateOfColumns() { // by default we report no support return false; } /** * Does this dialect support FOR UPDATE in conjunction with * outer joined rows? * * @return True if outer joined rows can be locked via FOR UPDATE. */ public boolean supportsOuterJoinForUpdate() { return true; } /** * Get the 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 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 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. * @param lockOptions the lock options to apply * @return The appropriate FOR UPDATE OF column_list clause string. */ @SuppressWarnings({"unchecked", "UnusedParameters"}) public String getForUpdateString(String aliases, LockOptions lockOptions) { LockMode lockMode = lockOptions.getLockMode(); final Iterator> itr = lockOptions.getAliasLockIterator(); while ( itr.hasNext() ) { // seek the highest lock mode final Map.Entryentry = itr.next(); final LockMode lm = entry.getValue(); if ( lm.greaterThan( lockMode ) ) { lockMode = lm; } } lockOptions.setLockMode( lockMode ); return getForUpdateString( lockOptions ); } /** * Retrieves the FOR UPDATE NOWAIT syntax specific to this dialect. * * @return The appropriate FOR UPDATE NOWAIT clause string. */ public String getForUpdateNowaitString() { // by default we report no support for NOWAIT lock semantics return getForUpdateString(); } /** * Retrieves the FOR UPDATE SKIP LOCKED syntax specific to this dialect. * * @return The appropriate FOR UPDATE SKIP LOCKED clause string. */ public String getForUpdateSkipLockedString() { // by default we report no support for SKIP_LOCKED lock semantics return getForUpdateString(); } /** * Get the 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 FOR UPDATE OF colunm_list NOWAIT clause string. */ public String getForUpdateNowaitString(String aliases) { return getForUpdateString( aliases ); } /** * Get the 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 FOR UPDATE colunm_list SKIP LOCKED clause string. */ public String getForUpdateSkipLockedString(String aliases) { return getForUpdateString( aliases ); } /** * Some dialects support an alternative means to SELECT FOR UPDATE, * whereby a "lock hint" is appends to the table name in the from clause. *

* contributed by Helge Schulz * * @param mode The lock mode to apply * @param tableName The name of the table to which to apply the lock hint. * @return The table with any required lock hints. * @deprecated use {@code appendLockHint(LockOptions,String)} instead */ @Deprecated public String appendLockHint(LockMode mode, String tableName) { return appendLockHint( new LockOptions( mode ), tableName ); } /** * Some dialects support an alternative means to SELECT FOR UPDATE, * whereby a "lock hint" is appends to the table name in the from clause. *

* contributed by Helge Schulz * * @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 by applying the appropriate updates for the specified * lock modes and key columns. *

* The behavior here is that of an ANSI SQL SELECT FOR UPDATE. This * method is really intended to allow dialects which do not support * SELECT FOR UPDATE to achieve this in their own fashion. * * @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(); } // table support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Command used to create a table. * * @return The command used to create a table. */ public String getCreateTableString() { return "create table"; } /** * Command used to alter a table. * * @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(); } /** * 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. *

* Most databases do not care about the distinction; originally added for * Teradata support which does care. * * @return The command used to create a multiset table. */ public String getCreateMultisetTableString() { return getCreateTableString(); } public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() { return new PersistentTableBulkIdStrategy(); } // callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Registers a parameter (either OUT, or the new REF_CURSOR param type available in Java 8) capable of * returning {@link java.sql.ResultSet} *by position*. Pre-Java 8, registering such ResultSet-returning * parameters varied greatly across database and drivers; hence its inclusion as part of the 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 (either OUT, or the new REF_CURSOR param type available in Java 8) capable of * returning {@link java.sql.ResultSet} *by name*. Pre-Java 8, registering such ResultSet-returning * parameters varied greatly across database and drivers; hence its inclusion as part of the 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 callable statement previously processed by {@link #registerResultSetOutParameter}, * extract the {@link java.sql.ResultSet} from the 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 callable statement previously processed by {@link #registerResultSetOutParameter}, * extract the {@link java.sql.ResultSet}. * * @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 callable statement previously processed by {@link #registerResultSetOutParameter}, * extract the {@link java.sql.ResultSet} from the 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 a way to retrieve the database's current * timestamp value? * * @return True if the current timestamp can be retrieved; false otherwise. */ public boolean supportsCurrentTimestampSelection() { return false; } /** * Should the value returned by {@link #getCurrentTimestampSelectString} * be treated as callable. Typically this indicates that JDBC escape * syntax is being used... * * @return True if the {@link #getCurrentTimestampSelectString} return * is callable; false otherwise. */ public boolean isCurrentTimestampSelectStringCallable() { throw new UnsupportedOperationException( "Database not known to define a current timestamp function" ); } /** * Retrieve the command used to retrieve the current timestamp from the * database. * * @return The command. */ public String getCurrentTimestampSelectString() { throw new UnsupportedOperationException( "Database not known to define a current timestamp function" ); } /** * The name of the database-specific SQL function for retrieving the * current timestamp. * * @return The function name. */ public String getCurrentTimestampSQLFunctionName() { // the standard SQL function name is current_timestamp... return "current_timestamp"; } // SQLException support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Build an instance of the SQLExceptionConverter preferred by this dialect for * converting SQLExceptions into Hibernate's JDBCException hierarchy. *

* The preferred method is to not override this method; if possible, * {@link #buildSQLExceptionConversionDelegate()} should be overridden * instead. * * If this method is not overridden, the default SQLExceptionConverter * implementation executes 3 SQLException converter delegates: *

    *
  1. a "static" delegate based on the JDBC 4 defined SQLException hierarchy;
  2. *
  3. the vendor-specific delegate returned by {@link #buildSQLExceptionConversionDelegate()}; * (it is strongly recommended that specific Dialect implementations * override {@link #buildSQLExceptionConversionDelegate()})
  4. *
  5. a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, * depending on java.sql.DatabaseMetaData#getSQLStateType
  6. *
*

* If this method is overridden, it is strongly recommended that the * returned {@link SQLExceptionConverter} interpret SQL errors based on * vendor-specific error codes rather than the SQLState since the * interpretation is more accurate when using vendor-specific ErrorCodes. * * @return The Dialect's preferred SQLExceptionConverter, or null to * indicate that the default {@link SQLExceptionConverter} should be used. * * @see {@link #buildSQLExceptionConversionDelegate()} * @deprecated {@link #buildSQLExceptionConversionDelegate()} should be * overridden instead. */ @Deprecated public SQLExceptionConverter buildSQLExceptionConverter() { return null; } /** * Build an instance of a {@link SQLExceptionConversionDelegate} for * interpreting dialect-specific error or SQLState codes. *

* When {@link #buildSQLExceptionConverter} returns null, the default * {@link SQLExceptionConverter} is used to interpret SQLState and * error 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: *

    *
  1. a "static" delegate based on the JDBC 4 defined SQLException hierarchy;
  2. *
  3. a delegate that interprets SQLState codes for either X/Open or SQL-2003 codes, * depending on java.sql.DatabaseMetaData#getSQLStateType
  4. *
*

* It is strongly recommended that specific Dialect implementations override this * method, since interpretation of a SQL error is much more accurate when based on * the a vendor-specific ErrorCode rather than the SQLState. *

* Specific Dialects may override to return whatever is most appropriate for that vendor. * * @return The SQLExceptionConversionDelegate for this dialect */ public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { return null; } private static final ViolatedConstraintNameExtracter EXTRACTER = new ViolatedConstraintNameExtracter() { public String extractConstraintName(SQLException sqle) { return null; } }; public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { return EXTRACTER; } // union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Given a {@link java.sql.Types} type code, determine an appropriate * null value to use in a select clause. *

* One thing to consider here is that certain databases might * require proper casting for the nulls here since the select here * will be part of a UNION/UNION ALL. * * @param sqlType The {@link java.sql.Types} type code. * @return The appropriate select clause value fragment. */ public String getSelectClauseNullString(int sqlType) { return "null"; } /** * Does this dialect support UNION ALL, which is generally a faster * variant of UNION? * * @return True if UNION ALL is supported; false otherwise. */ public boolean supportsUnionAll() { return false; } // miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Create a {@link org.hibernate.sql.JoinFragment} strategy responsible * for handling this dialect's variations in how joins are handled. * * @return This dialect's {@link org.hibernate.sql.JoinFragment} strategy. */ public JoinFragment createOuterJoinFragment() { return new ANSIJoinFragment(); } /** * Create a {@link org.hibernate.sql.CaseFragment} strategy responsible * for handling this dialect's variations in how CASE statements are * handled. * * @return This dialect's {@link org.hibernate.sql.CaseFragment} strategy. */ public CaseFragment createCaseFragment() { return new ANSICaseFragment(); } /** * The fragment used to insert a row without specifying any column values. * This is not possible on some databases. * * @return The appropriate empty values clause. */ public String getNoColumnsInsertString() { return "values ( )"; } /** * Check if the INSERT statement is allowed to contain no column. * * @return if the Dialect supports no-column INSERT. */ public boolean supportsNoColumnsInsert() { return true; } /** * The name of the SQL function that transforms a string to * lowercase * * @return The dialect-specific lowercase function. */ public String getLowercaseFunction() { return "lower"; } /** * The name of the SQL function that can do case insensitive like comparison. * * @return The dialect-specific "case insensitive" like function. */ public String getCaseInsensitiveLike(){ return "like"; } /** * Does this dialect support case insensitive LIKE restrictions? * * @return {@code true} if the underlying database supports case insensitive like comparison, * {@code false} otherwise. The default is {@code false}. */ public boolean supportsCaseInsensitiveLike(){ 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. *

* The recommend approach is to instead use * {@link org.hibernate.Interceptor#onPrepareStatement(String)}. * * @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? *

* 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 a 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; } /** * The SQL literal value to which this database maps boolean values. * * @param bool The boolean value * @return The appropriate SQL literal. */ public String toBooleanValueString(boolean bool) { return bool ? "1" : "0"; } // keyword support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ protected void registerKeyword(String word) { // When tokens are checked for keywords, they are always compared against the lower-case version of the token. // For instance, Template#renderWhereStringTemplate transforms all tokens to lower-case too. sqlKeywords.add( word.toLowerCase( Locale.ROOT ) ); } /** * @deprecated These are only ever used (if at all) from the code that handles identifier quoting. So * see {@link #buildIdentifierHelper} instead */ @Deprecated public Set getKeywords() { return sqlKeywords; } /** * Build the IdentifierHelper indicated by this Dialect for handling identifier conversions. * Returning {@code null} is allowed and indicates that Hibernate should fallback 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 NameQualifierSupport, if it defines one
  • *
* * @param builder A semi-configured IdentifierHelper builder. * @param dbMetaData Access to the metadata returned from the driver if needed and if available. WARNING: may be {@code null} * * @return The IdentifierHelper instance to use, or {@code null} to indicate Hibernate should use its fallback path * * @throws SQLException Accessing the DatabaseMetaData can throw it. Just re-throw and Hibernate will handle. * * @see #getNameQualifierSupport() */ public IdentifierHelper buildIdentifierHelper( IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) throws SQLException { builder.applyIdentifierCasing( dbMetaData ); builder.applyReservedWords( dbMetaData ); builder.applyReservedWords( AnsiSqlKeywords.INSTANCE.sql2003() ); 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's specific open quote character. */ public char openQuote() { return '"'; } /** * The character specific to this dialect used to close a quoted identifier. * * @return The dialect's specific close quote character. */ public char closeQuote() { return '"'; } /** * Apply dialect-specific quoting. *

* By default, the incoming value is checked to see if its first character * is the back-tick (`). If so, the dialect specific quoting is applied. * * @param name The value to be quoted. * @return The quoted (or unmodified, if not starting with back-tick) value. * @see #openQuote() * @see #closeQuote() */ public final 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 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ private StandardTableExporter tableExporter = new StandardTableExporter( this ); private StandardSequenceExporter sequenceExporter = new StandardSequenceExporter( this ); private StandardIndexExporter indexExporter = new StandardIndexExporter( this ); private StandardForeignKeyExporter foreignKeyExporter = new StandardForeignKeyExporter( this ); private StandardUniqueKeyExporter uniqueKeyExporter = new StandardUniqueKeyExporter( this ); private StandardAuxiliaryDatabaseObjectExporter auxiliaryObjectExporter = new StandardAuxiliaryDatabaseObjectExporter( this ); public Exporter

getTableExporter() { return tableExporter; } public Exporter getSequenceExporter() { return sequenceExporter; } public Exporter getIndexExporter() { return indexExporter; } public Exporter getForeignKeyExporter() { return foreignKeyExporter; } public Exporter getUniqueKeyExporter() { return uniqueKeyExporter; } public Exporter getAuxiliaryDatabaseObjectExporter() { return auxiliaryObjectExporter; } /** * Does this dialect support catalog creation? * * @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 schema creation? * * @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 return from there does not need this * information. E.g., a custom impl might make use of the Java 1.7 addition of * the {@link java.sql.Connection#getSchema()} method * * @return The current schema retrieval SQL */ public String getCurrentSchemaCommand() { return null; } /** * Get the strategy for determining the schema name of a Connection * * @return The schema name resolver strategy */ public SchemaNameResolver getSchemaNameResolver() { return DefaultSchemaNameResolver.INSTANCE; } /** * Does this dialect support the ALTER TABLE syntax? * * @return True if we support altering of tables; false otherwise. */ public boolean hasAlterTable() { return true; } /** * 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; } /** * Do we need to qualify index names with the schema name? * * @return boolean */ public boolean qualifyIndexName() { return true; } /** * The syntax used to add a column to a table (optional). * * @return The "add column" fragment. */ public String getAddColumnString() { throw new UnsupportedOperationException( "No add column syntax supported by " + getClass().getName() ); } /** * The syntax for the suffix used to add a column to a table (optional). * * @return The suffix "add column" fragment. */ public String getAddColumnSuffixString() { return ""; } public String getDropForeignKeyString() { return " drop constraint "; } public String getTableTypeString() { // grrr... for differentiation of mysql storage engines return ""; } /** * The syntax used to add a foreign key constraint to a table. * * @param constraintName The FK constraint name. * @param foreignKey The names of the columns comprising the FK * @param referencedTable The table referenced by the FK * @param primaryKey The explicit columns in the referencedTable referenced * by this FK. * @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(); } public String getAddForeignKeyConstraintString( String constraintName, String foreignKeyDefinition) { return new StringBuilder( 30 ) .append( " add constraint " ) .append( quote( constraintName ) ) .append( " " ) .append( foreignKeyDefinition ) .toString(); } /** * 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 "; } /** * Does the database/driver have bug in deleting rows that refer to other rows being deleted in the same query? * * @return {@code true} if the database/driver has this bug */ public boolean hasSelfReferentialForeignKeyBug() { return false; } /** * The keyword used to specify a nullable column. * * @return String */ public String getNullColumnString() { return ""; } /** * Does this dialect/database support commenting on tables, columns, etc? * * @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 column definition. * * @param comment The comment to apply * * @return The comment fragment */ public String getColumnComment(String comment) { return ""; } /** * For dropping a table, can the phrase "if exists" be applied before the table name? *

* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsAfterTableName} should return true * * @return {@code true} if the "if exists" can be applied before the table name */ public boolean supportsIfExistsBeforeTableName() { return false; } /** * For dropping a table, can the phrase "if exists" be applied after the table name? *

* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsBeforeTableName} should return true * * @return {@code true} if the "if exists" can be applied after the table name */ public boolean supportsIfExistsAfterTableName() { return false; } /** * For dropping a constraint with an "alter table", can the phrase "if exists" be applied before the constraint name? *

* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsAfterConstraintName} should return true * * @return {@code true} if the "if exists" can be applied before the constraint name */ public boolean supportsIfExistsBeforeConstraintName() { return false; } /** * For dropping a constraint with an "alter table", can the phrase "if exists" be applied after the constraint name? *

* NOTE : Only one or the other (or neither) of this and {@link #supportsIfExistsBeforeConstraintName} should return true * * @return {@code true} if the "if exists" can be applied after the constraint name */ public boolean supportsIfExistsAfterConstraintName() { return false; } /** * For an "alter table", can the phrase "if exists" be applied? * * @return {@code true} if the "if exists" can be applied after ALTER TABLE * @since 5.2.11 */ public boolean supportsIfExistsAfterAlterTable() { return false; } /** * Generate a DROP TABLE statement * * @param tableName The name of the table to drop * * @return The DROP TABLE command */ 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(); } /** * Does this dialect support column-level check constraints? * * @return True if column-level CHECK constraints are supported; false * otherwise. */ public boolean supportsColumnCheck() { return true; } /** * Does this dialect support table-level check constraints? * * @return True if table-level CHECK constraints are supported; false * otherwise. */ public boolean supportsTableCheck() { return true; } /** * Does this dialect support cascaded delete on foreign key definitions? * * @return {@code true} indicates that the dialect does support cascaded delete on foreign keys. */ public boolean supportsCascadeDelete() { return true; } /** * Completely optional cascading drop clause * * @return String */ public String getCascadeConstraintsString() { return ""; } /** * Returns the separator to use for defining cross joins when translating HQL queries. *

* Typically this will be either [ cross join ] or [, ] *

* Note that the spaces are important! * * @return The cross join separator */ public String getCrossJoinSeparator() { return " cross join "; } public ColumnAliasExtractor getColumnAliasExtractor() { return ColumnAliasExtractor.COLUMN_LABEL_EXTRACTOR; } // Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Does this dialect support empty IN lists? *

* For example, is [where XYZ in ()] a supported construct? * * @return True if empty in lists are supported; false otherwise. * @since 3.2 */ public boolean supportsEmptyInList() { return true; } /** * Are string comparisons implicitly case insensitive. *

* In other words, does [where 'XYZ' = 'xyz'] resolve to true? * * @return True if comparisons are case insensitive. * @since 3.2 */ public boolean areStringComparisonsCaseInsensitive() { return false; } /** * Is this dialect known to support what ANSI-SQL terms "row value * constructor" syntax; sometimes called tuple syntax. *

* Basically, does it support syntax like * "... where (FIRST_NAME, LAST_NAME) = ('Steve', 'Ebersole') ...". * * @return True if this SQL dialect is known to support "row value * constructor" syntax; false otherwise. * @since 3.2 */ public boolean supportsRowValueConstructorSyntax() { // return false here, as most databases do not properly support this construct... return false; } /** * If the dialect supports {@link #supportsRowValueConstructorSyntax() row values}, * does it offer such support in IN lists as well? *

* For example, "... where (FIRST_NAME, LAST_NAME) IN ( (?, ?), (?, ?) ) ..." * * @return True if this SQL dialect is known to support "row value * constructor" syntax in the IN list; false otherwise. * @since 3.2 */ public boolean supportsRowValueConstructorSyntaxInInList() { return false; } /** * Should LOBs (both BLOB and CLOB) be bound using stream operations (i.e. * {@link java.sql.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 SELECT clause of * INSERT ... SELECT ... statements? * * @return True if this is supported; false otherwise. * @since 3.2 */ public boolean supportsParametersInInsertSelect() { return true; } /** * Does this dialect require that references to result variables * (i.e, select expresssion aliases) in an ORDER BY clause be * replaced by column positions (1-origin) as defined * by the select clause? * @return true if result variable references in the ORDER BY * clause should be replaced by column positions; * false otherwise. */ public boolean replaceResultVariableInOrderByClauseWithPosition() { return false; } /** * Renders an ordering fragment * * @param expression The SQL order expression. In case of {@code @OrderBy} annotation user receives property placeholder * (e.g. attribute name enclosed in '{' and '}' signs). * @param collation Collation string in format {@code collate IDENTIFIER}, or {@code null} * if expression has not been explicitly specified. * @param order Order direction. Possible values: {@code asc}, {@code desc}, or {@code null} * if expression has not been explicitly specified. * @param nulls Nulls precedence. Default value: {@link NullPrecedence#NONE}. * @return Renders single element of {@code ORDER BY} clause. */ public String renderOrderByElement(String expression, String collation, String order, NullPrecedence nulls) { final StringBuilder orderByElement = new StringBuilder( expression ); if ( collation != null ) { orderByElement.append( " " ).append( collation ); } if ( order != null ) { orderByElement.append( " " ).append( order ); } if ( nulls != NullPrecedence.NONE ) { orderByElement.append( " nulls " ).append( nulls.name().toLowerCase( Locale.ROOT ) ); } return orderByElement.toString(); } /** * Does this dialect require that parameters appearing in the SELECT clause be wrapped in cast() * calls to tell the db parser the expected type. * * @return True if select clause parameter must be cast()ed * @since 3.2 */ public boolean requiresCastingOfParametersInSelectClause() { 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 java.sql.ResultSet#isAfterLast} and * {@link java.sql.ResultSet#isBeforeFirst}. Certain drivers do not * allow access to these methods for forward only cursors. *

* NOTE : this is highly driver dependent! * * @return True if methods like {@link java.sql.ResultSet#isAfterLast} and * {@link java.sql.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; } /** * Are subselects supported as the left-hand-side (LHS) of * IN-predicates. *

* In other words, is syntax like "... IN (1, 2, 3) ..." supported? * * @return True if subselects 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... *

* 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 versions of their drivers... * * @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 java.sql.Blob#setBinaryStream}, * {@link java.sql.Blob#setBytes(long, byte[])}, * {@link java.sql.Blob#setBytes(long, byte[], int, int)}, * or {@link java.sql.Blob#truncate(long)}. *

* For CLOBs, the internal value might be changed by: * {@link java.sql.Clob#setAsciiStream(long)}, * {@link java.sql.Clob#setCharacterStream(long)}, * {@link java.sql.Clob#setString(long, String)}, * {@link java.sql.Clob#setString(long, String, int, int)}, * or {@link java.sql.Clob#truncate(long)}. *

* NOTE : 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 supportsLobValueChangePropogation() { // 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? *

* Again, part of the trickiness here is the fact that this is largely * driver dependent. *

* NOTE: 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 a DELETE query. And so can that table then be * referenced in a subquery of said UPDATE/DELETE query. *

* For example, would the following two syntaxes be supported:

    *
  • delete from TABLE_A where ID not in ( select ID from TABLE_A )
  • *
  • 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 READ_COMMITTED isolation implemented by * forcing readers to wait for write locks to be released? * * @return True if writers block readers to achieve READ_COMMITTED; false otherwise. */ public boolean doesReadCommittedCauseWritersToBlockReaders() { return false; } /** * For the underlying database, is REPEATABLE_READ isolation implemented by * forcing writers to wait for read locks to be released? * * @return True if readers block writers to achieve 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}. */ @SuppressWarnings( {"UnusedDeclaration"}) public boolean supportsBindAsCallableArgument() { return true; } /** * Does this dialect support `count(a,b)`? * * @return True if the database supports counting tuples; false otherwise. */ public boolean supportsTupleCounts() { return false; } /** * Does this dialect support `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 the Dialect require the tuple to be wrapped with parens? * * @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 less-than-zero. * * @return int The limit, or zero-or-less to indicate no limit. */ public int getInExpressionCountLimit() { return 0; } /** * HHH-4635 * Oracle expects all Lob values to be last in inserts and updates. * * @return boolean True if Lob values should be last, false if it * does not matter. */ public boolean forceLobAsLastValue() { 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.. * @deprecated Use {@link #useFollowOnLocking(QueryParameters)} instead. */ @Deprecated public boolean useFollowOnLocking() { return useFollowOnLocking( null ); } /** * 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. * * @param parameters query parameters * @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(QueryParameters parameters) { return false; } /** * Negate an expression * * @param expression The expression to negate * * @return The negated expression */ public String getNotExpression(String expression) { return "not " + expression; } /** * Get the UniqueDelegate supported by this dialect * * @return The UniqueDelegate */ public UniqueDelegate getUniqueDelegate() { return uniqueDelegate; } /** * Does this dialect support the UNIQUE column syntax? * * @return boolean * * @deprecated {@link #getUniqueDelegate()} should be overridden instead. */ @Deprecated public boolean supportsUnique() { return true; } /** * Does this dialect support adding Unique constraints via create and alter table ? * * @return boolean * * @deprecated {@link #getUniqueDelegate()} should be overridden instead. */ @Deprecated public boolean supportsUniqueConstraintInCreateAlterTable() { return true; } /** * The syntax used to add a unique constraint to a table. * * @param constraintName The name of the unique constraint. * @return The "add unique" fragment * * @deprecated {@link #getUniqueDelegate()} should be overridden instead. */ @Deprecated public String getAddUniqueConstraintString(String constraintName) { return " add constraint " + constraintName + " unique "; } /** * Is the combination of not-null and unique supported? * * @return deprecated * * @deprecated {@link #getUniqueDelegate()} should be overridden instead. */ @Deprecated public boolean supportsNotNullUnique() { return true; } /** * Apply a hint to the query. The entire query is provided, allowing the Dialect 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 ); if ( StringHelper.isEmpty( hints ) ) { return query; } return getQueryHintString( query, hints ); } /** * Apply a hint to the query. The entire query is provided, allowing the Dialect 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; } /** * Certain dialects support a subset of ScrollModes. Provide a default to be used by Criteria and Query. * * @return ScrollMode */ public ScrollMode defaultScrollMode() { return ScrollMode.SCROLL_INSENSITIVE; } /** * Does this dialect support tuples in subqueries? Ex: * delete from Table1 where (col1, col2) in (select col1, col2 from Table2) * * @return boolean */ public boolean supportsTuplesInSubqueries() { return true; } public CallableStatementSupport getCallableStatementSupport() { // most databases do not support returning cursors (ref_cursor)... return StandardCallableStatementSupport.NO_REF_CURSOR_INSTANCE; } /** * By default interpret this based on DatabaseMetaData. * * @return */ public NameQualifierSupport getNameQualifierSupport() { return null; } protected final BatchLoadSizingStrategy STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY = new BatchLoadSizingStrategy() { @Override public int determineOptimalBatchLoadSize(int numberOfKeyColumns, int numberOfKeys) { return 50; } }; public BatchLoadSizingStrategy getDefaultBatchLoadSizingStrategy() { return STANDARD_DEFAULT_BATCH_LOAD_SIZING_STRATEGY; } /** * Does the fetching JDBC statement warning for logging is enabled by default * * @return boolean * * @since 5.1 */ public boolean isJdbcLogWarningsEnabledByDefault() { return true; } public void augmentRecognizedTableTypes(List tableTypesList) { // noihing to do } /** * Does the underlying database support partition by * * @return boolean * * @since 5.2 */ public boolean supportsPartitionBy() { return false; } /** * Override the DatabaseMetaData#supportsNamedParameters() * * @return boolean * * @throws SQLException Accessing the DatabaseMetaData can throw it. Just re-throw and Hibernate will handle. */ public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { return databaseMetaData != null && databaseMetaData.supportsNamedParameters(); } /** * Does this dialect supports Nationalized Types * * @return boolean */ public boolean supportsNationalizedTypes() { return true; } /** * Does this dialect/database support non-query statements (e.g. INSERT, UPDATE, DELETE) with CTE (Common Table Expressions)? * * @return {@code true} if non-query statements are supported with CTE */ public boolean supportsNonQueryWithCTE() { return false; } /** * Does this dialect/database support VALUES list (e.g. VALUES (1), (2), (3) ) * * @return {@code true} if VALUES list are supported */ public boolean supportsValuesList() { return false; } /** * Does this dialect/database support SKIP_LOCKED timeout. * * @return {@code true} if SKIP_LOCKED is supported */ public boolean supportsSkipLocked() { return false; } /** * Does this dialect/database support NO_WAIT timeout. * * @return {@code true} if NO_WAIT is supported */ public boolean supportsNoWait() { return false; } public boolean isLegacyLimitHandlerBehaviorEnabled() { return legacyLimitHandlerBehavior; } /** * Inline String literal. * * @return escaped String */ public String inlineLiteral(String literal) { return String.format( "\'%s\'", escapeLiteral( literal ) ); } /** * Check whether the JDBC {@link java.sql.Connection} supports creating LOBs via {@link Connection#createBlob()}, * {@link Connection#createNClob()} or {@link Connection#createClob()}. * * @param databaseMetaData JDBC {@link DatabaseMetaData} which can be used if LOB creation is supported only starting from a given Driver version * * @return {@code true} if LOBs can be created via the JDBC Connection. */ public boolean supportsJdbcConnectionLobCreation(DatabaseMetaData databaseMetaData) { return true; } /** * Escape String literal. * * @return escaped String */ protected String escapeLiteral(String literal) { return literal.replace("'", "''"); } private void resolveLegacyLimitHandlerBehavior(ServiceRegistry serviceRegistry) { // HHH-11194 // Temporary solution to set whether legacy limit handler behavior should be used. final ConfigurationService configurationService = serviceRegistry.getService( ConfigurationService.class ); legacyLimitHandlerBehavior = configurationService.getSetting( AvailableSettings.USE_LEGACY_LIMIT_HANDLERS, StandardConverters.BOOLEAN, false ); } /** * Modify the SQL, adding hints or comments, if necessary * * @param sql original sql * @param parameters query parameters * @param commentsEnabled if comments are enabled */ public String addSqlHintOrComment( String sql, QueryParameters parameters, boolean commentsEnabled) { // Keep this here, rather than moving to Select. Some Dialects may need the hint to be appended to the very // end or beginning of the finalized SQL statement, so wait until everything is processed. if ( parameters.getQueryHints() != null && parameters.getQueryHints().size() > 0 ) { sql = getQueryHintString( sql, parameters.getQueryHints() ); } if ( commentsEnabled && parameters.getComment() != null ){ sql = prependComment( sql, parameters.getComment() ); } return sql; } protected String prependComment(String sql, String comment) { return "/* " + comment + " */ " + sql; } }