org.hibernate.dialect.HSQLDialect Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of beangle-hibernate-core Show documentation
Show all versions of beangle-hibernate-core Show documentation
Hibernate Orm Core Shade,Support Scala Collection
/*
* 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.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.TrimFunction;
import org.hibernate.dialect.identity.HSQLIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
import org.hibernate.dialect.sequence.HSQLSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.dialect.unique.CreateTableUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
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.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.sqm.CastType;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.sqm.TemporalUnit;
import org.hibernate.query.sqm.mutation.internal.temptable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
import org.hibernate.sql.ast.SqlAstTranslator;
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
import org.hibernate.sql.ast.spi.SqlAppender;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorHSQLDBDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.DOUBLE;
import static org.hibernate.type.SqlTypes.NCLOB;
/**
* A {@linkplain Dialect SQL dialect} for HSQLDB (HyperSQL) 2.6.1 and above.
*
* @author Christoph Sturm
* @author Phillip Baird
* @author Fred Toussi
*/
public class HSQLDialect extends Dialect {
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 2, 6, 1 );
private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
private final HSQLIdentityColumnSupport identityColumnSupport;
public HSQLDialect(DialectResolutionInfo info) {
super( info );
this.identityColumnSupport = new HSQLIdentityColumnSupport( getVersion() );
}
public HSQLDialect() {
this( MINIMUM_VERSION );
}
public HSQLDialect(DatabaseVersion version) {
super( version );
this.identityColumnSupport = new HSQLIdentityColumnSupport( getVersion() );
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
@Override
protected void registerDefaultKeywords() {
super.registerDefaultKeywords();
registerKeyword( "period" );
}
@Override
protected String columnType(int sqlTypeCode) {
// Note that all floating point types are synonyms for 'double'
// Note that the HSQL type 'longvarchar' and 'longvarbinary' are
// synonyms for 'varchar(16M)' and 'varbinary(16M)' respectively.
// But using these types results in schema validation issue as
// described in HHH-9693.
switch ( sqlTypeCode ) {
//HSQL has no 'nclob' type, but 'clob' is Unicode (See HHH-10364)
case NCLOB:
return "clob";
default:
return super.columnType( sqlTypeCode );
}
}
@Override
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
switch ( baseTypeName ) {
case "DOUBLE":
return DOUBLE;
}
return super.resolveSqlTypeCode( typeName, baseTypeName, typeConfiguration );
}
@Override
public int getDefaultStatementBatchSize() {
return 15;
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT );
functionFactory.cot();
functionFactory.radians();
functionFactory.degrees();
functionFactory.log10();
functionFactory.rand();
functionFactory.trunc_dateTrunc_trunc();
functionFactory.pi();
functionFactory.soundex();
functionFactory.reverse();
functionFactory.space();
functionFactory.repeat();
functionFactory.translate();
functionFactory.bitand();
functionFactory.bitor();
functionFactory.bitxor();
functionFactory.bitnot();
functionFactory.yearMonthDay();
functionFactory.hourMinuteSecond();
functionFactory.dayofweekmonthyear();
functionFactory.weekQuarter();
functionFactory.daynameMonthname();
functionFactory.lastDay();
functionFactory.trim1();
functionFactory.toCharNumberDateTimestamp();
functionFactory.concat_pipeOperator();
functionFactory.localtimeLocaltimestamp();
functionFactory.bitLength();
functionFactory.octetLength();
functionFactory.ascii();
functionFactory.chr_char();
functionFactory.instr();
functionFactory.substr();
//also natively supports ANSI-style substring()
functionFactory.position();
functionFactory.nowCurdateCurtime();
functionFactory.insert();
functionFactory.overlay();
functionFactory.median();
functionFactory.stddevPopSamp();
functionFactory.varPopSamp();
functionFactory.addMonths();
functionFactory.monthsBetween();
functionFactory.collate_quoted();
//SYSDATE is similar to LOCALTIMESTAMP but it returns the timestamp when it is called
functionFactory.sysdate();
// from v. 2.2.0 ROWNUM() is supported in all modes as the equivalent of Oracle ROWNUM
functionFactory.rownum();
functionFactory.listagg_groupConcat();
functionFactory.array_hsql();
functionFactory.arrayAggregate();
functionFactory.arrayPosition_hsql();
functionFactory.arrayPositions_hsql();
functionFactory.arrayLength_cardinality();
functionFactory.arrayConcat_operator();
functionFactory.arrayPrepend_operator();
functionFactory.arrayAppend_operator();
functionFactory.arrayContains_hsql();
functionFactory.arrayIntersects_hsql();
functionFactory.arrayGet_unnest();
functionFactory.arraySet_hsql();
functionFactory.arrayRemove_hsql();
functionFactory.arrayRemoveIndex_unnest( false );
functionFactory.arraySlice_unnest();
functionFactory.arrayReplace_unnest();
functionFactory.arrayTrim_trim_array();
functionFactory.arrayFill_hsql();
functionFactory.arrayToString_hsql();
//trim() requires parameters to be cast when used as trim character
functionContributions.getFunctionRegistry().register( "trim", new TrimFunction(
this,
functionContributions.getTypeConfiguration(),
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER
) );
}
@Override
public String currentTime() {
return "localtime";
}
@Override
public String currentTimestamp() {
return "localtimestamp";
}
@Override
public String currentTimestampWithTimeZone() {
return "current_timestamp";
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@Override
protected SqlAstTranslator buildTranslator(
SessionFactoryImplementor sessionFactory, Statement statement) {
return new HSQLSqlAstTranslator<>( sessionFactory, statement );
}
};
}
@Override
public String castPattern(CastType from, CastType to) {
String result;
switch ( to ) {
case INTEGER:
case LONG:
result = BooleanDecoder.toInteger( from );
if ( result != null ) {
return result;
}
break;
case BOOLEAN:
result = from == CastType.STRING
? buildStringToBooleanCastDecode( "true", "false" )
: BooleanDecoder.toBoolean( from );
if ( result != null ) {
return result;
}
break;
case INTEGER_BOOLEAN:
result = from == CastType.STRING
? buildStringToBooleanCastDecode( "1", "0" )
: BooleanDecoder.toIntegerBoolean( from );
if ( result != null ) {
return result;
}
break;
case YN_BOOLEAN:
result = from == CastType.STRING
? buildStringToBooleanCastDecode( "'Y'", "'N'" )
: BooleanDecoder.toYesNoBoolean( from );
if ( result != null ) {
return result;
}
break;
case TF_BOOLEAN:
result = from == CastType.STRING
? buildStringToBooleanCastDecode( "'T'", "'F'" )
: BooleanDecoder.toTrueFalseBoolean( from );
if ( result != null ) {
return result;
}
break;
case STRING:
result = BooleanDecoder.toString( from );
if ( result != null ) {
return "trim(" + result + ')';
}
break;
}
return super.castPattern( from, to );
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
StringBuilder pattern = new StringBuilder();
boolean castTo = temporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
switch (unit) {
case NANOSECOND:
case NATIVE:
pattern.append("timestampadd(sql_tsi_frac_second,?2,"); //nanos
break;
case WEEK:
pattern.append("dateadd('day',?2*7,");
break;
case SECOND:
//TODO: if we have an integral number of seconds
// (the common case) this is unnecessary
pattern.append("timestampadd(sql_tsi_frac_second, ?2*1e9,");
break;
default:
pattern.append("dateadd('?1',?2,");
}
if (castTo) {
pattern.append("cast(?3 as timestamp)");
}
else {
pattern.append("?3");
}
pattern.append(")");
return pattern.toString();
}
@Override
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
StringBuilder pattern = new StringBuilder();
boolean castFrom = fromTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
boolean castTo = toTemporalType != TemporalType.TIMESTAMP && !unit.isDateUnit();
switch (unit) {
case NANOSECOND:
case NATIVE:
pattern.append("timestampdiff(sql_tsi_frac_second"); //nanos
break;
case WEEK:
pattern.append("(datediff('day'");
default:
pattern.append("datediff('?1'");
}
pattern.append(',');
if (castFrom) {
pattern.append("cast(?2 as timestamp)");
}
else {
pattern.append("?2");
}
pattern.append(',');
if (castTo) {
pattern.append("cast(?3 as timestamp)");
}
else {
pattern.append("?3");
}
pattern.append(")");
if ( unit == TemporalUnit.WEEK ) {
pattern.append( "/7)" );
}
return pattern.toString();
}
@Override
public String extractPattern(TemporalUnit unit) {
if ( unit == TemporalUnit.EPOCH ) {
return "unix_timestamp(?2)";
}
else {
return super.extractPattern(unit);
}
}
@Override
public boolean supportsDistinctFromPredicate() {
return true;
}
@Override
public boolean supportsLockTimeouts() {
return false;
}
@Override
public LimitHandler getLimitHandler() {
return OffsetFetchLimitHandler.INSTANCE;
}
// Note: HSQLDB actually supports IF EXISTS before AND after the table name.
// But as CASCADE has to be after IF EXISTS in case it's after the table name,
// we put the IF EXISTS before the table name to be able to add CASCADE after.
@Override
public boolean supportsIfExistsAfterTableName() {
return false;
}
@Override
public boolean supportsIfExistsBeforeTableName() {
return true;
}
@Override
public SequenceSupport getSequenceSupport() {
return HSQLSequenceSupport.INSTANCE;
}
@Override
public String getQuerySequencesString() {
return "select * from information_schema.sequences";
}
@Override
public SequenceInformationExtractor getSequenceInformationExtractor() {
return SequenceInformationExtractorHSQLDBDatabaseImpl.INSTANCE;
}
@Override
public boolean supportsStandardArrays() {
return true;
}
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR_20;
}
private static final ViolatedConstraintNameExtractor EXTRACTOR_20 =
// messages may be localized, therefore use the common, non-locale element " table: "
new TemplatedViolatedConstraintNameExtractor( sqle -> {
switch ( JdbcExceptionHelper.extractErrorCode( sqle ) ) {
case -8:
case -9:
case -104:
case -177:
return extractUsingTemplate(
"; ", " table: ",
sqle.getMessage()
);
}
return null;
} );
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
switch ( errorCode ) {
case -104:
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
}
return null;
};
}
@Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
switch ( sqlType ) {
case Types.LONGVARCHAR:
case Types.VARCHAR:
case Types.CHAR:
return "cast(null as varchar(100))";
case Types.LONGVARBINARY:
case Types.VARBINARY:
case Types.BINARY:
return "cast(null as varbinary(100))";
case Types.CLOB:
return "cast(null as clob)";
case Types.BLOB:
return "cast(null as blob)";
case Types.DATE:
return "cast(null as date)";
case Types.TIMESTAMP:
case Types.TIMESTAMP_WITH_TIMEZONE:
return "cast(null as timestamp)";
case Types.BOOLEAN:
return "cast(null as boolean)";
case Types.BIT:
return "cast(null as bit)";
case Types.TIME:
return "cast(null as time)";
default:
return "cast(null as int)";
}
}
@Override
public NullOrdering getNullOrdering() {
return NullOrdering.FIRST;
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
// Hibernate uses this information for temporary tables that it uses for its own operations
// therefore the appropriate strategy is taken with different versions of HSQLDB
// All versions of HSQLDB support GLOBAL TEMPORARY tables where the table
// definition is shared by all users but data is private to the session
// HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where
// the definition and data is private to the session and table declaration
// can happen in the middle of a transaction
return new LocalTemporaryTableMutationStrategy(
// With HSQLDB 2.0, the table name is qualified with SESSION to assist the drop
// statement (in-case there is a global name beginning with HT_)
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
// Hibernate uses this information for temporary tables that it uses for its own operations
// therefore the appropriate strategy is taken with different versions of HSQLDB
// All versions of HSQLDB support GLOBAL TEMPORARY tables where the table
// definition is shared by all users but data is private to the session
// HSQLDB 2.0 also supports session-based LOCAL TEMPORARY tables where
// the definition and data is private to the session and table declaration
// can happen in the middle of a transaction
return new LocalTemporaryTableInsertStrategy(
// With HSQLDB 2.0, the table name is qualified with SESSION to assist the drop
// statement (in-case there is a global name beginning with HT_)
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateCommand() {
return "declare local temporary table";
}
@Override
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.DROP;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsCurrentTimestampSelection() {
return true;
}
@Override
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
@Override
public String getCurrentTimestampSelectString() {
return "values current_timestamp";
}
@Override
public boolean doesRoundTemporalOnOverflow() {
// HSQLDB does truncation
return false;
}
@Override
public boolean supportsCommentOn() {
return true;
}
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean doesReadCommittedCauseWritersToBlockReaders() {
return true;
}
@Override
public boolean doesRepeatableReadCauseReadersToBlockWriters() {
return true;
}
@Override
public boolean supportsLobValueChangePropagation() {
return false;
}
@Override
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
appender.appendSql( bool );
}
@Override
public boolean supportsTupleCounts() {
return true;
}
@Override
public boolean supportsTupleDistinctCounts() {
// 2.2.9 is added support for COUNT(DISTINCT ...) with multiple arguments
return true;
}
@Override
public boolean supportsOffsetInSubquery() {
return true;
}
@Override
public boolean supportsLateral() {
return true;
}
@Override
public boolean supportsRecursiveCTE() {
return getVersion().isSameOrAfter( 2 );
}
@Override
public boolean requiresFloatCastingOfIntegerDivision() {
return true;
}
@Override
public boolean supportsValuesList() {
return true;
}
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
return identityColumnSupport;
}
@Override
public NameQualifierSupport getNameQualifierSupport() {
return NameQualifierSupport.SCHEMA;
}
@Override
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
return false;
}
@Override
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
return FunctionalDependencyAnalysisSupportImpl.TABLE_REFERENCE;
}
// Do not drop constraints explicitly, just do this by cascading instead.
@Override
public boolean dropConstraints() {
return false;
}
@Override
public String getCascadeConstraintsString() {
return " cascade ";
}
@Override
public void appendDatetimeFormat(SqlAppender appender, String format) {
appender.appendSql(
OracleDialect.datetimeFormat( format, false, false )
// HSQL is case-sensitive i.e. requires MONTH and DAY instead of Month and Day
.replace("MMMM", "MONTH")
.replace("EEEE", "DAY")
.replace("SSSSSS", "FF")
.replace("SSSSS", "FF")
.replace("SSSS", "FF")
.replace("SSS", "FF")
.replace("SS", "FF")
.replace("S", "FF")
.result()
);
}
@Override
public String translateExtractField(TemporalUnit unit) {
//TODO: does not support MICROSECOND, but on the
// other hand it doesn't support microsecond
// precision in timestamps either so who cares?
switch (unit) {
case WEEK: return "week_of_year"; //this is the ISO week number, I believe
default: return unit.toString();
}
}
@Override
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
throws SQLException {
builder.setAutoQuoteInitialUnderscore( true );
return super.buildIdentifierHelper( builder, dbMetaData );
}
@Override
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
@Override
public String quoteCollation(String collation) {
return '\"' + collation + '\"';
}
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public String getFromDualForSelectOnly() {
return " from " + getDual();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy