org.hibernate.community.dialect.DerbyDialect Maven / Gradle / Ivy
Show all versions of hibernate-community-dialects Show documentation
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.community.dialect;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Locale;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.dialect.DB2Dialect;
import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.NationalizationSupport;
import org.hibernate.dialect.RowLockStrategy;
import org.hibernate.dialect.function.CaseLeastGreatestEmulation;
import org.hibernate.dialect.function.CastingConcatFunction;
import org.hibernate.dialect.function.ChrLiteralEmulation;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.function.CountFunction;
import org.hibernate.community.dialect.function.DerbyLpadEmulation;
import org.hibernate.community.dialect.function.DerbyRpadEmulation;
import org.hibernate.dialect.function.InsertSubstringOverlayEmulation;
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.community.dialect.pagination.DerbyLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.community.dialect.sequence.DerbySequenceSupport;
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.Size;
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.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockTimeoutException;
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.common.TemporalUnit;
import org.hibernate.query.sqm.mutation.spi.BeforeUseAction;
import org.hibernate.query.sqm.mutation.internal.temptable.GlobalTemporaryTableMutationStrategy;
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.service.ServiceRegistry;
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.community.dialect.sequence.SequenceInformationExtractorDerbyDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.BigDecimalJavaType;
import org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType;
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.TemporalType;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.CLOB;
import static org.hibernate.type.SqlTypes.DECIMAL;
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
import static org.hibernate.type.SqlTypes.NCHAR;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NUMERIC;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.TIME;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
/**
* A {@linkplain Dialect SQL dialect} for Apache Derby 10.15.2 and above.
*
* @author Simon Johnston
* @author Gavin King
*
*/
public class DerbyDialect extends Dialect {
// KNOWN LIMITATIONS:
// no support for nationalized data (nchar, nvarchar, nclob)
// * limited set of fields for extract()
// (no 'day of xxxx', nor 'week of xxxx')
// * no support for format()
// * pad() can only pad with blanks
// * can't cast String to Binary
// * can't select a parameter unless wrapped
// in a cast or function call
private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 10, 15, 2 );
private final LimitHandler limitHandler = new DerbyLimitHandler( true );
private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
public DerbyDialect() {
this( MINIMUM_VERSION);
}
public DerbyDialect(DatabaseVersion version) {
super(version);
}
public DerbyDialect(DialectResolutionInfo info) {
super(info);
}
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
@Override
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case TINYINT:
//no tinyint
return "smallint";
case NUMERIC:
// HHH-12827: map them both to the same type to avoid problems with schema update
// Note that 31 is the maximum precision Derby supports
return columnType( DECIMAL );
case VARBINARY:
return "varchar($l) for bit data";
case NCHAR:
return columnType( CHAR );
case NVARCHAR:
return columnType( VARCHAR );
case BLOB:
return "blob";
case CLOB:
case NCLOB:
return "clob";
case TIME:
case TIME_WITH_TIMEZONE:
return "time";
case TIMESTAMP:
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp";
default:
return super.columnType( sqlTypeCode );
}
}
@Override
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.registerColumnTypes( typeContributions, serviceRegistry );
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
int varcharDdlTypeCapacity = 32_672;
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
VARBINARY,
isLob( LONG32VARBINARY )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32VARBINARY ),
columnType( VARBINARY ),
this
)
.withTypeCapacity( varcharDdlTypeCapacity, columnType( VARBINARY ) )
.build()
);
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
VARCHAR,
isLob( LONG32VARCHAR )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32VARCHAR ),
columnType( VARCHAR ),
this
)
.withTypeCapacity( varcharDdlTypeCapacity, columnType( VARCHAR ) )
.build()
);
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
NVARCHAR,
isLob( LONG32NVARCHAR )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32VARCHAR ),
columnType( NVARCHAR ),
this
)
.withTypeCapacity( varcharDdlTypeCapacity, columnType( NVARCHAR ) )
.build()
);
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
BINARY,
isLob( LONG32VARBINARY )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32VARBINARY ),
columnType( VARBINARY ),
this
)
.withTypeCapacity( 254, "char($l) for bit data" )
.withTypeCapacity( varcharDdlTypeCapacity, columnType( VARBINARY ) )
.build()
);
// This is the maximum size for the CHAR datatype on Derby
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
CHAR,
isLob( LONG32VARCHAR )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32VARCHAR ),
columnType( CHAR ),
this
)
.withTypeCapacity( 254, columnType( CHAR ) )
.withTypeCapacity( getMaxVarcharLength(), columnType( VARCHAR ) )
.build()
);
ddlTypeRegistry.addDescriptor(
CapacityDependentDdlType.builder(
NCHAR,
isLob( LONG32NVARCHAR )
? CapacityDependentDdlType.LobKind.BIGGEST_LOB
: CapacityDependentDdlType.LobKind.NONE,
columnType( LONG32NVARCHAR ),
columnType( NCHAR ),
this
)
.withTypeCapacity( 254, columnType( NCHAR ) )
.withTypeCapacity( getMaxVarcharLength(), columnType( NVARCHAR ) )
.build()
);
}
@Override
public int getMaxVarcharLength() {
return 32_672;
}
@Override
public int getMaxVarcharCapacity() {
return 32_700;
}
@Override
public int getDefaultDecimalPrecision() {
//this is the maximum allowed in Derby
return 31;
}
@Override
public NationalizationSupport getNationalizationSupport() {
return NationalizationSupport.IMPLICIT;
}
@Override
public int getDefaultStatementBatchSize() {
return 15;
}
@Override
public int getFloatPrecision() {
return 23;
}
@Override
public int getDoublePrecision() {
return 52;
}
@Override
public int getDefaultTimestampPrecision() {
return 9;
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry(functionContributions);
final BasicTypeRegistry basicTypeRegistry = functionContributions.getTypeConfiguration().getBasicTypeRegistry();
final BasicType stringType = basicTypeRegistry.resolve( StandardBasicTypes.STRING );
final DdlTypeRegistry ddlTypeRegistry = functionContributions.getTypeConfiguration().getDdlTypeRegistry();
final CommonFunctionFactory functionFactory = new CommonFunctionFactory(functionContributions);
// Derby needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
functionFactory.aggregates( this, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
functionContributions.getFunctionRegistry().register(
"count",
new CountFunction(
this,
functionContributions.getTypeConfiguration(),
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
"||",
ddlTypeRegistry.getDescriptor( VARCHAR )
.getCastTypeName( Size.nil(), stringType, ddlTypeRegistry ),
true
)
);
// 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 );
// Note that Derby does not have chr() / ascii() functions.
// It does have a function named char(), but it's really a
// sort of to_char() function.
// We register an emulation instead, that can at least translate integer literals
functionContributions.getFunctionRegistry().register(
"chr",
new ChrLiteralEmulation( functionContributions.getTypeConfiguration() )
);
functionFactory.concat_pipeOperator();
functionFactory.cot();
functionFactory.degrees();
functionFactory.radians();
functionFactory.log10();
functionFactory.sinh();
functionFactory.cosh();
functionFactory.tanh();
functionFactory.pi();
functionFactory.rand();
functionFactory.trim1();
functionFactory.hourMinuteSecond();
functionFactory.yearMonthDay();
functionFactory.varPopSamp();
functionFactory.stddevPopSamp();
functionFactory.substring_substr();
functionFactory.leftRight_substrLength();
functionFactory.characterLength_length( SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
functionFactory.power_expLn();
functionFactory.round_floor();
functionFactory.trunc_floor();
functionFactory.octetLength_pattern( "length(?1)", SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
functionFactory.bitLength_pattern( "length(?1)*8", SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
functionContributions.getFunctionRegistry().register(
"concat",
new CastingConcatFunction(
this,
"||",
true,
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
functionContributions.getTypeConfiguration()
)
);
//no way I can see to pad with anything other than spaces
functionContributions.getFunctionRegistry().register( "lpad", new DerbyLpadEmulation( functionContributions.getTypeConfiguration() ) );
functionContributions.getFunctionRegistry().register( "rpad", new DerbyRpadEmulation( functionContributions.getTypeConfiguration() ) );
functionContributions.getFunctionRegistry().register( "least", new CaseLeastGreatestEmulation( true ) );
functionContributions.getFunctionRegistry().register( "greatest", new CaseLeastGreatestEmulation( false ) );
functionContributions.getFunctionRegistry().register( "overlay", new InsertSubstringOverlayEmulation( functionContributions.getTypeConfiguration(), true ) );
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@Override
protected SqlAstTranslator buildTranslator(
SessionFactoryImplementor sessionFactory, Statement statement) {
return new DerbySqlAstTranslator<>( sessionFactory, statement );
}
};
}
/**
* Derby doesn't have an extract() function, and has
* no functions at all for calendaring, but we can
* emulate the most basic functionality of extract()
* using the functions it does have.
*
* The only supported {@link TemporalUnit}s are:
* {@link TemporalUnit#YEAR},
* {@link TemporalUnit#MONTH}
* {@link TemporalUnit#DAY},
* {@link TemporalUnit#HOUR},
* {@link TemporalUnit#MINUTE},
* {@link TemporalUnit#SECOND} (along with
* {@link TemporalUnit#NANOSECOND},
* {@link TemporalUnit#DATE}, and
* {@link TemporalUnit#TIME}, which are desugared
* by the parser).
*/
@Override
public String extractPattern(TemporalUnit unit) {
switch (unit) {
case DAY_OF_MONTH:
return "day(?2)";
case DAY_OF_YEAR:
return "({fn timestampdiff(sql_tsi_day,date(char(year(?2),4)||'-01-01'),?2)}+1)";
case DAY_OF_WEEK:
// Use the approach as outlined here: https://stackoverflow.com/questions/36357013/day-of-week-from-seconds-since-epoch
return "(mod(mod({fn timestampdiff(sql_tsi_day,{d '1970-01-01'},?2)}+4,7)+7,7)+1)";
case WEEK:
// Use the approach as outlined here: https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
// In SQL Server terms this is (DATEPART(dy,DATEADD(dd,DATEDIFF(dd,'17530101',@SomeDate)/7*7,'17530104'))+6)/7
return "(({fn timestampdiff(sql_tsi_day,date(char(year(?2),4)||'-01-01'),{fn timestampadd(sql_tsi_day,{fn timestampdiff(sql_tsi_day,{d '1753-01-01'},?2)}/7*7,{d '1753-01-04'})})}+7)/7)";
case QUARTER:
return "((month(?2)+2)/3)";
case EPOCH:
return "{fn timestampdiff(sql_tsi_second,{ts '1970-01-01 00:00:00'},?2)}";
default:
return "?1(?2)";
}
}
@Override
public String translateExtractField(TemporalUnit unit) {
switch (unit) {
case WEEK:
case DAY_OF_YEAR:
case DAY_OF_WEEK:
throw new UnsupportedOperationException("field type not supported on Derby: " + unit);
case DAY_OF_MONTH:
return "day";
default:
return super.translateExtractField(unit);
}
}
/**
* Derby does have a real {@link Types#BOOLEAN}
* type, but it doesn't know how to cast to it. Worse,
* Derby makes us use the {@code double()} function to
* cast things to its floating point types.
*/
@Override
public String castPattern(CastType from, CastType to) {
switch ( to ) {
case FLOAT:
return "cast(double(?1) as real)";
case DOUBLE:
return "double(?1)";
case STRING:
// Derby madness http://db.apache.org/derby/docs/10.8/ref/rrefsqlj33562.html
// With a nice rant: https://blog.jooq.org/2011/10/29/derby-casting-madness-the-sequel/
// See https://issues.apache.org/jira/browse/DERBY-2072
// Since numerics can't be cast to varchar directly, use char(254) i.e. with the maximum char capacity
// as an intermediate type before converting to varchar
switch ( from ) {
case FLOAT:
case DOUBLE:
// Derby can't cast to char directly, but needs to be cast to decimal first...
return "cast(trim(cast(cast(?1 as decimal(" + getDefaultDecimalPrecision() + "," + BigDecimalJavaType.INSTANCE.getDefaultSqlScale( this, null ) + ")) as char(254))) as ?2)";
case INTEGER:
case LONG:
case FIXED:
return "cast(trim(cast(?1 as char(254))) as ?2)";
case DATE:
// The maximum length of a date
return "cast(?1 as varchar(10))";
case TIME:
// The maximum length of a time
return "cast(?1 as varchar(8))";
case TIMESTAMP:
// The maximum length of a timestamp
return "cast(?1 as varchar(30))";
}
break;
}
return super.castPattern( from, to );
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
return "{fn timestampadd(sql_tsi_frac_second,mod(bigint(?2),1000000000),{fn timestampadd(sql_tsi_second,bigint((?2)/1000000000),?3)})}";
default:
final String addExpression = "{fn timestampadd(sql_tsi_?1,bigint(?2),?3)}";
// Since timestampadd will always produce a TIMESTAMP, we have to cast back to the intended type
return temporalType == TemporalType.TIMESTAMP
? addExpression
: "cast(" + addExpression + " as " + temporalType.name().toLowerCase( Locale.ROOT ) + ")" ;
}
}
@Override
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
return "{fn timestampdiff(sql_tsi_frac_second,?2,?3)}";
default:
return "{fn timestampdiff(sql_tsi_?1,?2,?3)}";
}
}
@Override
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
appender.appendSql( bool );
}
@Override
public SequenceSupport getSequenceSupport() {
return DerbySequenceSupport.INSTANCE;
}
@Override
public String getQuerySequencesString() {
return "select sys.sysschemas.schemaname as sequence_schema,sys.syssequences.* from sys.syssequences left join sys.sysschemas on sys.syssequences.schemaid=sys.sysschemas.schemaid";
}
@Override
public SequenceInformationExtractor getSequenceInformationExtractor() {
return SequenceInformationExtractorDerbyDatabaseImpl.INSTANCE;
}
@Override
public String[] getDropSchemaCommand(String schemaName) {
return new String[] {"drop schema " + schemaName + " restrict"};
}
@Override
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
return DB2Dialect.selectNullString( sqlType );
}
@Override
public boolean supportsCommentOn() {
//HHH-4531
return false;
}
@Override
public RowLockStrategy getReadRowLockStrategy() {
return RowLockStrategy.NONE;
}
@Override
public String getForUpdateString() {
return " for update with rs";
}
@Override
public String getWriteLockString(int timeout) {
return " for update with rs";
}
@Override
public String getReadLockString(int timeout) {
return " for read only with rs";
}
@Override
public boolean supportsOuterJoinForUpdate() {
//TODO: check this!
return false;
}
@Override
public boolean supportsExistsInSelect() {
//TODO: check this!
return false;
}
@Override
public boolean supportsLockTimeouts() {
// To enable the lock timeout, we need a dedicated call
// 'call SYSCS_UTIL.SYSCS_SET_DATABASE_PROPERTY('derby.locks.waitTimeout', '3')'
return false;
}
@Override
public boolean supportsCurrentTimestampSelection() {
return true;
}
@Override
public String getCurrentTimestampSelectString() {
return "values current timestamp";
}
@Override
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
@Override
public LimitHandler getLimitHandler() {
return limitHandler;
}
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
return DB2IdentityColumnSupport.INSTANCE;
}
@Override
public boolean doesReadCommittedCauseWritersToBlockReaders() {
//TODO: check this
return true;
}
@Override
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
return false;
}
@Override
public boolean supportsTupleDistinctCounts() {
//checked on Derby 10.14
return false;
}
@Override
public boolean supportsOrderByInSubquery() {
// As of version 10.5 Derby supports OFFSET and FETCH as well as ORDER BY in subqueries
return true;
}
@Override
public boolean requiresCastForConcatenatingNonStrings() {
return true;
}
@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
.getJdbcTypeRegistry();
jdbcTypeRegistry.addDescriptor( Types.TIMESTAMP_WITH_TIMEZONE, TimestampJdbcType.INSTANCE );
// Derby requires a custom binder for binding untyped nulls that resolves the type through the statement
typeContributions.contributeJdbcType( ObjectNullResolvingJdbcType.INSTANCE );
// Until we remove StandardBasicTypes, we have to keep this
typeContributions.contributeType(
new JavaObjectType(
ObjectNullResolvingJdbcType.INSTANCE,
typeContributions.getTypeConfiguration()
.getJavaTypeRegistry()
.getDescriptor( Object.class )
)
);
}
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsLobValueChangePropagation() {
return false;
}
@Override
public boolean supportsUnboundedLobLocatorMaterialization() {
return false;
}
@Override
public int getInExpressionCountLimit() {
// Derby does not have a limit on the number of expressions/parameters per-se (it may, I just
// don't know). It does, however, have a limit on the size of the SQL text it will accept as a
// PreparedStatement; so let's limit this to a sensible value to avoid that.
return 512;
}
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return new TemplatedViolatedConstraintNameExtractor( sqle -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqle );
if ( sqlState != null ) {
switch ( sqlState ) {
case "23505":
return TemplatedViolatedConstraintNameExtractor.extractUsingTemplate(
"'", "'",
sqle.getMessage()
);
}
}
return null;
} );
}
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
// final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
final String constraintName;
if ( sqlState != null ) {
switch ( sqlState ) {
case "23505":
// Unique constraint violation
constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
case "40XL1":
case "40XL2":
return new LockTimeoutException( message, sqlException, sql );
}
}
return null;
};
}
@Override
public void appendDatetimeFormat(SqlAppender appender, String format) {
throw new UnsupportedOperationException("format() function not supported on Derby");
}
@Override
protected void registerDefaultKeywords() {
super.registerDefaultKeywords();
registerKeyword( "ADD" );
registerKeyword( "ALL" );
registerKeyword( "ALLOCATE" );
registerKeyword( "ALTER" );
registerKeyword( "AND" );
registerKeyword( "ANY" );
registerKeyword( "ARE" );
registerKeyword( "AS" );
registerKeyword( "ASC" );
registerKeyword( "ASSERTION" );
registerKeyword( "AT" );
registerKeyword( "AUTHORIZATION" );
registerKeyword( "AVG" );
registerKeyword( "BEGIN" );
registerKeyword( "BETWEEN" );
registerKeyword( "BIT" );
registerKeyword( "BOOLEAN" );
registerKeyword( "BOTH" );
registerKeyword( "BY" );
registerKeyword( "CALL" );
registerKeyword( "CASCADE" );
registerKeyword( "CASCADED" );
registerKeyword( "CASE" );
registerKeyword( "CAST" );
registerKeyword( "CHAR" );
registerKeyword( "CHARACTER" );
registerKeyword( "CHECK" );
registerKeyword( "CLOSE" );
registerKeyword( "COLLATE" );
registerKeyword( "COLLATION" );
registerKeyword( "COLUMN" );
registerKeyword( "COMMIT" );
registerKeyword( "CONNECT" );
registerKeyword( "CONNECTION" );
registerKeyword( "CONSTRAINT" );
registerKeyword( "CONSTRAINTS" );
registerKeyword( "CONTINUE" );
registerKeyword( "CONVERT" );
registerKeyword( "CORRESPONDING" );
registerKeyword( "COUNT" );
registerKeyword( "CREATE" );
registerKeyword( "CURRENT" );
registerKeyword( "CURRENT_DATE" );
registerKeyword( "CURRENT_TIME" );
registerKeyword( "CURRENT_TIMESTAMP" );
registerKeyword( "CURRENT_USER" );
registerKeyword( "CURSOR" );
registerKeyword( "DEALLOCATE" );
registerKeyword( "DEC" );
registerKeyword( "DECIMAL" );
registerKeyword( "DECLARE" );
registerKeyword( "DEFERRABLE" );
registerKeyword( "DEFERRED" );
registerKeyword( "DELETE" );
registerKeyword( "DESC" );
registerKeyword( "DESCRIBE" );
registerKeyword( "DIAGNOSTICS" );
registerKeyword( "DISCONNECT" );
registerKeyword( "DISTINCT" );
registerKeyword( "DOUBLE" );
registerKeyword( "DROP" );
registerKeyword( "ELSE" );
registerKeyword( "END" );
registerKeyword( "ENDEXEC" );
registerKeyword( "ESCAPE" );
registerKeyword( "EXCEPT" );
registerKeyword( "EXCEPTION" );
registerKeyword( "EXEC" );
registerKeyword( "EXECUTE" );
registerKeyword( "EXISTS" );
registerKeyword( "EXPLAIN" );
registerKeyword( "EXTERNAL" );
registerKeyword( "FALSE" );
registerKeyword( "FETCH" );
registerKeyword( "FIRST" );
registerKeyword( "FLOAT" );
registerKeyword( "FOR" );
registerKeyword( "FOREIGN" );
registerKeyword( "FOUND" );
registerKeyword( "FROM" );
registerKeyword( "FULL" );
registerKeyword( "FUNCTION" );
registerKeyword( "GET" );
registerKeyword( "GET_CURRENT_CONNECTION" );
registerKeyword( "GLOBAL" );
registerKeyword( "GO" );
registerKeyword( "GOTO" );
registerKeyword( "GRANT" );
registerKeyword( "GROUP" );
registerKeyword( "HAVING" );
registerKeyword( "HOUR" );
registerKeyword( "IDENTITY" );
registerKeyword( "IMMEDIATE" );
registerKeyword( "IN" );
registerKeyword( "INDICATOR" );
registerKeyword( "INITIALLY" );
registerKeyword( "INNER" );
registerKeyword( "INOUT" );
registerKeyword( "INPUT" );
registerKeyword( "INSENSITIVE" );
registerKeyword( "INSERT" );
registerKeyword( "INT" );
registerKeyword( "INTEGER" );
registerKeyword( "INTERSECT" );
registerKeyword( "INTO" );
registerKeyword( "IS" );
registerKeyword( "ISOLATION" );
registerKeyword( "JOIN" );
registerKeyword( "KEY" );
registerKeyword( "LAST" );
registerKeyword( "LEFT" );
registerKeyword( "LIKE" );
registerKeyword( "LONGINT" );
registerKeyword( "LOWER" );
registerKeyword( "LTRIM" );
registerKeyword( "MATCH" );
registerKeyword( "MAX" );
registerKeyword( "MIN" );
registerKeyword( "MINUTE" );
registerKeyword( "NATIONAL" );
registerKeyword( "NATURAL" );
registerKeyword( "NCHAR" );
registerKeyword( "NVARCHAR" );
registerKeyword( "NEXT" );
registerKeyword( "NO" );
registerKeyword( "NOT" );
registerKeyword( "NULL" );
registerKeyword( "NULLIF" );
registerKeyword( "NUMERIC" );
registerKeyword( "OF" );
registerKeyword( "ON" );
registerKeyword( "ONLY" );
registerKeyword( "OPEN" );
registerKeyword( "OPTION" );
registerKeyword( "OR" );
registerKeyword( "ORDER" );
registerKeyword( "OUT" );
registerKeyword( "OUTER" );
registerKeyword( "OUTPUT" );
registerKeyword( "OVERLAPS" );
registerKeyword( "PAD" );
registerKeyword( "PARTIAL" );
registerKeyword( "PREPARE" );
registerKeyword( "PRESERVE" );
registerKeyword( "PRIMARY" );
registerKeyword( "PRIOR" );
registerKeyword( "PRIVILEGES" );
registerKeyword( "PROCEDURE" );
registerKeyword( "PUBLIC" );
registerKeyword( "READ" );
registerKeyword( "REAL" );
registerKeyword( "REFERENCES" );
registerKeyword( "RELATIVE" );
registerKeyword( "RESTRICT" );
registerKeyword( "REVOKE" );
registerKeyword( "RIGHT" );
registerKeyword( "ROLLBACK" );
registerKeyword( "ROWS" );
registerKeyword( "RTRIM" );
registerKeyword( "SCHEMA" );
registerKeyword( "SCROLL" );
registerKeyword( "SECOND" );
registerKeyword( "SELECT" );
registerKeyword( "SESSION_USER" );
registerKeyword( "SET" );
registerKeyword( "SMALLINT" );
registerKeyword( "SOME" );
registerKeyword( "SPACE" );
registerKeyword( "SQL" );
registerKeyword( "SQLCODE" );
registerKeyword( "SQLERROR" );
registerKeyword( "SQLSTATE" );
registerKeyword( "SUBSTR" );
registerKeyword( "SUBSTRING" );
registerKeyword( "SUM" );
registerKeyword( "SYSTEM_USER" );
registerKeyword( "TABLE" );
registerKeyword( "TEMPORARY" );
registerKeyword( "TIMEZONE_HOUR" );
registerKeyword( "TIMEZONE_MINUTE" );
registerKeyword( "TO" );
registerKeyword( "TRAILING" );
registerKeyword( "TRANSACTION" );
registerKeyword( "TRANSLATE" );
registerKeyword( "TRANSLATION" );
registerKeyword( "TRUE" );
registerKeyword( "UNION" );
registerKeyword( "UNIQUE" );
registerKeyword( "UNKNOWN" );
registerKeyword( "UPDATE" );
registerKeyword( "UPPER" );
registerKeyword( "USER" );
registerKeyword( "USING" );
registerKeyword( "VALUES" );
registerKeyword( "VARCHAR" );
registerKeyword( "VARYING" );
registerKeyword( "VIEW" );
registerKeyword( "WHENEVER" );
registerKeyword( "WHERE" );
registerKeyword( "WITH" );
registerKeyword( "WORK" );
registerKeyword( "WRITE" );
registerKeyword( "XML" );
registerKeyword( "XMLEXISTS" );
registerKeyword( "XMLPARSE" );
registerKeyword( "XMLSERIALIZE" );
registerKeyword( "YEAR" );
}
/**
* {@inheritDoc}
*
* From Derby docs:
*
* The DECLARE GLOBAL TEMPORARY TABLE statement defines a temporary table for the current connection.
*
*
* {@link DB2Dialect} returns a {@link GlobalTemporaryTableMutationStrategy} that
* will make temporary tables created at startup and hence unavailable for subsequent connections.
* see HHH-10238.
*/
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableMutationStrategy(
TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> "session." + TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy(
TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> "session." + TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
),
runtimeModelCreationContext.getSessionFactory()
);
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateOptions() {
return "not logged";
}
@Override
public boolean supportsTemporaryTablePrimaryKey() {
return false;
}
@Override
public String getTemporaryTableCreateCommand() {
return "declare global temporary table";
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public boolean supportsPartitionBy() {
return false;
}
@Override
public boolean supportsWindowFunctions() {
// It seems at least the row_number function is supported as of 10.4
return true;
}
@Override
public boolean supportsValuesList() {
return true;
}
@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 DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public String getDual() {
return "(values 0)";
}
@Override
public String getFromDualForSelectOnly() {
return " from " + getDual() + " dual";
}
}