HHH-15181 / HHH-15197 / HHH-15198
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
11fb9440ef
commit
ec49356c77
|
@ -0,0 +1,826 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import java.sql.CallableStatement;
|
||||||
|
import java.sql.DatabaseMetaData;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.OracleDialect;
|
||||||
|
import org.hibernate.dialect.function.CastingConcatFunction;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
import org.hibernate.dialect.function.CountFunction;
|
||||||
|
import org.hibernate.dialect.function.DB2FormatEmulation;
|
||||||
|
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.pagination.DB2LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
|
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.LegacyDB2SequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
import org.hibernate.dialect.unique.DB2UniqueDelegate;
|
||||||
|
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.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.exception.LockTimeoutException;
|
||||||
|
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
|
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||||
|
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||||
|
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
import org.hibernate.query.sqm.mutation.internal.cte.CteInsertStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.internal.cte.CteMutationStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||||
|
import org.hibernate.query.sqm.produce.function.FunctionParameterType;
|
||||||
|
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.tool.schema.extract.internal.SequenceInformationExtractorDB2DatabaseImpl;
|
||||||
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||||
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
|
import org.hibernate.type.JavaObjectType;
|
||||||
|
import org.hibernate.type.StandardBasicTypes;
|
||||||
|
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.CharJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.DecimalJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.ObjectNullResolvingJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||||
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||||
|
import static org.hibernate.type.SqlTypes.CLOB;
|
||||||
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
|
import static org.hibernate.type.SqlTypes.NUMERIC;
|
||||||
|
import static org.hibernate.type.SqlTypes.SQLXML;
|
||||||
|
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 DB2.
|
||||||
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class DB2LegacyDialect extends Dialect {
|
||||||
|
|
||||||
|
private static final int BIND_PARAMETERS_NUMBER_LIMIT = 32_767;
|
||||||
|
|
||||||
|
private static final String FOR_READ_ONLY_SQL = " for read only with rs";
|
||||||
|
private static final String FOR_SHARE_SQL = FOR_READ_ONLY_SQL + " use and keep share locks";
|
||||||
|
private static final String FOR_UPDATE_SQL = FOR_READ_ONLY_SQL + " use and keep update locks";
|
||||||
|
private static final String SKIP_LOCKED_SQL = " skip locked data";
|
||||||
|
private static final String FOR_SHARE_SKIP_LOCKED_SQL = FOR_SHARE_SQL + SKIP_LOCKED_SQL;
|
||||||
|
private static final String FOR_UPDATE_SKIP_LOCKED_SQL = FOR_UPDATE_SQL + SKIP_LOCKED_SQL;
|
||||||
|
|
||||||
|
private final LimitHandler limitHandler = getDB2Version().isBefore( 11, 1 )
|
||||||
|
? LegacyDB2LimitHandler.INSTANCE
|
||||||
|
: DB2LimitHandler.INSTANCE;
|
||||||
|
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
||||||
|
|
||||||
|
public DB2LegacyDialect() {
|
||||||
|
this( DatabaseVersion.make( 9, 0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2LegacyDialect(DialectResolutionInfo info) {
|
||||||
|
super( info );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2LegacyDialect(DatabaseVersion version) {
|
||||||
|
super( version );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerDefaultKeywords() {
|
||||||
|
super.registerDefaultKeywords();
|
||||||
|
//not keywords, at least not in DB2 11,
|
||||||
|
//but perhaps they were in older versions?
|
||||||
|
registerKeyword( "current" );
|
||||||
|
registerKeyword( "date" );
|
||||||
|
registerKeyword( "time" );
|
||||||
|
registerKeyword( "timestamp" );
|
||||||
|
registerKeyword( "fetch" );
|
||||||
|
registerKeyword( "first" );
|
||||||
|
registerKeyword( "rows" );
|
||||||
|
registerKeyword( "only" );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DB2 LUW Version
|
||||||
|
*/
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return this.getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultStatementBatchSize() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String columnType(int sqlTypeCode) {
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
case BOOLEAN:
|
||||||
|
// prior to DB2 11, the 'boolean' type existed,
|
||||||
|
// but was not allowed as a column type
|
||||||
|
return getDB2Version().isBefore( 11 ) ? "smallint" : super.columnType( 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 DB2 supports
|
||||||
|
return columnType( DECIMAL );
|
||||||
|
case BLOB:
|
||||||
|
return "blob";
|
||||||
|
case CLOB:
|
||||||
|
return "clob";
|
||||||
|
case TIMESTAMP_WITH_TIMEZONE:
|
||||||
|
return "timestamp($p)";
|
||||||
|
case TIME_WITH_TIMEZONE:
|
||||||
|
return "time";
|
||||||
|
case VARBINARY:
|
||||||
|
// should use 'varbinary' since version 11
|
||||||
|
return getDB2Version().isBefore( 11 ) ? "varchar($l) for bit data" : super.columnType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
return super.columnType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||||
|
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||||
|
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||||
|
|
||||||
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
|
// should use 'binary' since version 11
|
||||||
|
ddlTypeRegistry.addDescriptor(
|
||||||
|
CapacityDependentDdlType.builder( BINARY, "varchar($l) for bit data", this )
|
||||||
|
.withTypeCapacity( 254, "char($l) for bit data" )
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UniqueDelegate createUniqueDelegate() {
|
||||||
|
return new DB2UniqueDelegate( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxVarcharLength() {
|
||||||
|
return 32_672;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultDecimalPrecision() {
|
||||||
|
//this is the maximum allowed in DB2
|
||||||
|
return 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDistinctFromPredicate() {
|
||||||
|
return getDB2Version().isSameOrAfter( 11, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
|
// 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.degrees();
|
||||||
|
functionFactory.log();
|
||||||
|
functionFactory.log10();
|
||||||
|
functionFactory.radians();
|
||||||
|
functionFactory.rand();
|
||||||
|
functionFactory.soundex();
|
||||||
|
functionFactory.trim2();
|
||||||
|
functionFactory.space();
|
||||||
|
functionFactory.repeat();
|
||||||
|
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substr" )
|
||||||
|
.setInvariantType(
|
||||||
|
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
|
||||||
|
)
|
||||||
|
.setArgumentCountBetween( 2, 4 )
|
||||||
|
.setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY)
|
||||||
|
.setArgumentListSignature( "(STRING string, INTEGER start[, INTEGER length[, units]])" )
|
||||||
|
.register();
|
||||||
|
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "substring" )
|
||||||
|
.setInvariantType(
|
||||||
|
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.STRING )
|
||||||
|
)
|
||||||
|
.setArgumentCountBetween( 2, 4 )
|
||||||
|
.setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.INTEGER, FunctionParameterType.INTEGER, FunctionParameterType.ANY)
|
||||||
|
.setArgumentListSignature( "(STRING string{ INTEGER from|,} start[{ INTEGER for|,} length[, units]])" )
|
||||||
|
.register();
|
||||||
|
functionFactory.translate();
|
||||||
|
functionFactory.bitand();
|
||||||
|
functionFactory.bitor();
|
||||||
|
functionFactory.bitxor();
|
||||||
|
functionFactory.bitnot();
|
||||||
|
functionFactory.yearMonthDay();
|
||||||
|
functionFactory.hourMinuteSecond();
|
||||||
|
functionFactory.dayofweekmonthyear();
|
||||||
|
functionFactory.weekQuarter();
|
||||||
|
functionFactory.daynameMonthname();
|
||||||
|
functionFactory.lastDay();
|
||||||
|
functionFactory.toCharNumberDateTimestamp();
|
||||||
|
functionFactory.dateTimeTimestamp();
|
||||||
|
functionFactory.concat_pipeOperator();
|
||||||
|
functionFactory.octetLength();
|
||||||
|
functionFactory.ascii();
|
||||||
|
functionFactory.char_chr();
|
||||||
|
functionFactory.position();
|
||||||
|
functionFactory.trunc();
|
||||||
|
functionFactory.truncate();
|
||||||
|
functionFactory.insert();
|
||||||
|
functionFactory.overlayCharacterLength_overlay();
|
||||||
|
functionFactory.median();
|
||||||
|
functionFactory.stddev();
|
||||||
|
functionFactory.stddevPopSamp();
|
||||||
|
functionFactory.regrLinearRegressionAggregates();
|
||||||
|
functionFactory.variance();
|
||||||
|
functionFactory.stdevVarianceSamp();
|
||||||
|
functionFactory.addYearsMonthsDaysHoursMinutesSeconds();
|
||||||
|
functionFactory.yearsMonthsDaysHoursMinutesSecondsBetween();
|
||||||
|
functionFactory.dateTrunc();
|
||||||
|
functionFactory.bitLength_pattern( "length(?1)*8" );
|
||||||
|
|
||||||
|
// DB2 wants parameter operands to be casted to allow lengths bigger than 255
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"concat",
|
||||||
|
new CastingConcatFunction(
|
||||||
|
this,
|
||||||
|
"||",
|
||||||
|
true,
|
||||||
|
SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER,
|
||||||
|
queryEngine.getTypeConfiguration()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
// For the count distinct emulation distinct
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"count",
|
||||||
|
new CountFunction(
|
||||||
|
this,
|
||||||
|
queryEngine.getTypeConfiguration(),
|
||||||
|
SqlAstNodeRenderingMode.DEFAULT,
|
||||||
|
"||",
|
||||||
|
queryEngine.getTypeConfiguration().getDdlTypeRegistry().getDescriptor( VARCHAR )
|
||||||
|
.getCastTypeName(
|
||||||
|
queryEngine.getTypeConfiguration()
|
||||||
|
.getBasicTypeRegistry()
|
||||||
|
.resolve( StandardBasicTypes.STRING ),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null
|
||||||
|
),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"format",
|
||||||
|
new DB2FormatEmulation( queryEngine.getTypeConfiguration() )
|
||||||
|
);
|
||||||
|
|
||||||
|
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "posstr" )
|
||||||
|
.setInvariantType(
|
||||||
|
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER )
|
||||||
|
)
|
||||||
|
.setExactArgumentCount( 2 )
|
||||||
|
.setParameterTypes(FunctionParameterType.STRING, FunctionParameterType.STRING)
|
||||||
|
.setArgumentListSignature("(STRING string, STRING pattern)")
|
||||||
|
.register();
|
||||||
|
|
||||||
|
functionFactory.windowFunctions();
|
||||||
|
if ( getDB2Version().isSameOrAfter( 9, 5 ) ) {
|
||||||
|
functionFactory.listagg( null );
|
||||||
|
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||||
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getDropSchemaCommand(String schemaName) {
|
||||||
|
return new String[] {"drop schema " + schemaName + " restrict"};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Since we're using {@code seconds_between()} and
|
||||||
|
* {@code add_seconds()}, it makes sense to use
|
||||||
|
* seconds as the "native" precision.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getFractionalSecondPrecisionInNanos() {
|
||||||
|
//Note that DB2 actually supports all the way up to
|
||||||
|
//thousands-of-nanoseconds precision for timestamps!
|
||||||
|
//i.e. timestamp(12)
|
||||||
|
return 1_000_000_000; //seconds
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 NATIVE:
|
||||||
|
case NANOSECOND:
|
||||||
|
pattern.append("(seconds_between(");
|
||||||
|
break;
|
||||||
|
//note: DB2 does have weeks_between()
|
||||||
|
case MONTH:
|
||||||
|
case QUARTER:
|
||||||
|
// the months_between() function results
|
||||||
|
// in a non-integral value, so trunc() it
|
||||||
|
pattern.append("trunc(months_between(");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pattern.append("?1s_between(");
|
||||||
|
}
|
||||||
|
if (castTo) {
|
||||||
|
pattern.append("cast(?3 as timestamp)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern.append("?3");
|
||||||
|
}
|
||||||
|
pattern.append(",");
|
||||||
|
if (castFrom) {
|
||||||
|
pattern.append("cast(?2 as timestamp)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern.append("?2");
|
||||||
|
}
|
||||||
|
pattern.append(")");
|
||||||
|
switch (unit) {
|
||||||
|
case NATIVE:
|
||||||
|
pattern.append("+(microsecond(?3)-microsecond(?2))/1e6)");
|
||||||
|
break;
|
||||||
|
case NANOSECOND:
|
||||||
|
pattern.append("*1e9+(microsecond(?3)-microsecond(?2))*1e3)");
|
||||||
|
break;
|
||||||
|
case MONTH:
|
||||||
|
pattern.append(")");
|
||||||
|
break;
|
||||||
|
case QUARTER:
|
||||||
|
pattern.append("/3)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return pattern.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
|
final StringBuilder pattern = new StringBuilder();
|
||||||
|
final boolean castTo;
|
||||||
|
if ( unit.isDateUnit() ) {
|
||||||
|
castTo = temporalType == TemporalType.TIME;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
castTo = temporalType == TemporalType.DATE;
|
||||||
|
}
|
||||||
|
if (castTo) {
|
||||||
|
pattern.append("cast(?3 as timestamp)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern.append("?3");
|
||||||
|
}
|
||||||
|
pattern.append("+(");
|
||||||
|
// DB2 supports temporal arithmetic. See https://www.ibm.com/support/knowledgecenter/en/SSEPGG_9.7.0/com.ibm.db2.luw.sql.ref.doc/doc/r0023457.html
|
||||||
|
switch (unit) {
|
||||||
|
case NATIVE:
|
||||||
|
// AFAICT the native format is seconds with fractional parts after the decimal point
|
||||||
|
pattern.append("?2) seconds");
|
||||||
|
break;
|
||||||
|
case NANOSECOND:
|
||||||
|
pattern.append("(?2)/1e9) seconds");
|
||||||
|
break;
|
||||||
|
case WEEK:
|
||||||
|
pattern.append("(?2)*7) days");
|
||||||
|
break;
|
||||||
|
case QUARTER:
|
||||||
|
pattern.append("(?2)*3) months");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pattern.append("?2) ?1s");
|
||||||
|
}
|
||||||
|
return pattern.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLowercaseFunction() {
|
||||||
|
return getDB2Version().isBefore( 9, 7 ) ? "lcase" : super.getLowercaseFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dropConstraints() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceSupport getSequenceSupport() {
|
||||||
|
return getDB2Version().isBefore( 9, 7 )
|
||||||
|
? LegacyDB2SequenceSupport.INSTANCE
|
||||||
|
: DB2SequenceSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuerySequencesString() {
|
||||||
|
return "select * from syscat.sequences";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceInformationExtractor getSequenceInformationExtractor() {
|
||||||
|
if ( getQuerySequencesString() == null ) {
|
||||||
|
return SequenceInformationExtractorNoOpImpl.INSTANCE;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return SequenceInformationExtractorDB2DatabaseImpl.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateString() {
|
||||||
|
return FOR_UPDATE_SQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSkipLocked() {
|
||||||
|
// Introduced in 11.5: https://www.ibm.com/docs/en/db2/11.5?topic=statement-concurrent-access-resolution-clause
|
||||||
|
return getDB2Version().isSameOrAfter( 11, 5 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateSkipLockedString() {
|
||||||
|
return supportsSkipLocked()
|
||||||
|
? FOR_UPDATE_SKIP_LOCKED_SQL
|
||||||
|
: FOR_UPDATE_SQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateSkipLockedString(String aliases) {
|
||||||
|
return getForUpdateSkipLockedString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteLockString(int timeout) {
|
||||||
|
return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked()
|
||||||
|
? FOR_UPDATE_SKIP_LOCKED_SQL
|
||||||
|
: FOR_UPDATE_SQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReadLockString(int timeout) {
|
||||||
|
return timeout == LockOptions.SKIP_LOCKED && supportsSkipLocked()
|
||||||
|
? FOR_SHARE_SKIP_LOCKED_SQL
|
||||||
|
: FOR_SHARE_SQL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsOuterJoinForUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsExistsInSelect() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLockTimeouts() {
|
||||||
|
//as far as I know, DB2 doesn't support this
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getSelectClauseNullString(int sqlType, TypeConfiguration typeConfiguration) {
|
||||||
|
return selectNullString(sqlType);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String selectNullString(int sqlType) {
|
||||||
|
String literal;
|
||||||
|
switch ( sqlType ) {
|
||||||
|
case Types.VARCHAR:
|
||||||
|
case Types.CHAR:
|
||||||
|
literal = "''";
|
||||||
|
break;
|
||||||
|
case Types.DATE:
|
||||||
|
literal = "'2000-1-1'";
|
||||||
|
break;
|
||||||
|
case Types.TIME:
|
||||||
|
literal = "'00:00:00'";
|
||||||
|
break;
|
||||||
|
case Types.TIMESTAMP:
|
||||||
|
case Types.TIMESTAMP_WITH_TIMEZONE:
|
||||||
|
literal = "'2000-1-1 00:00:00'";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
literal = "0";
|
||||||
|
}
|
||||||
|
return "nullif(" + literal + "," + literal + ')';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
|
||||||
|
return col;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet getResultSet(CallableStatement ps) throws SQLException {
|
||||||
|
boolean isResultSet = ps.execute();
|
||||||
|
// This assumes you will want to ignore any update counts
|
||||||
|
while ( !isResultSet && ps.getUpdateCount() != -1 ) {
|
||||||
|
isResultSet = ps.getMoreResults();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ps.getResultSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCommentOn() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
|
||||||
|
EntityMappingType rootEntityDescriptor,
|
||||||
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
|
return new CteMutationStrategy( rootEntityDescriptor, runtimeModelCreationContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
|
||||||
|
EntityMappingType rootEntityDescriptor,
|
||||||
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
|
return new CteInsertStrategy( rootEntityDescriptor, runtimeModelCreationContext );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCurrentTimestampSelection() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentTimestampSelectString() {
|
||||||
|
return "values current timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCurrentTimestampSelectStringCallable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsParametersInInsertSelect() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLobValueChangePropagation() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesReadCommittedCauseWritersToBlockReaders() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTupleDistinctCounts() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
|
|
||||||
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
|
||||||
|
|
||||||
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.BOOLEAN, SmallIntJdbcType.INSTANCE );
|
||||||
|
// Binary literals were only added in 11. See https://www.ibm.com/support/knowledgecenter/SSEPGG_11.1.0/com.ibm.db2.luw.sql.ref.doc/doc/r0000731.html#d79816e393
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.VARBINARY, VarbinaryJdbcType.INSTANCE_WITHOUT_LITERALS );
|
||||||
|
if ( getDB2Version().isBefore( 9, 7 ) ) {
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// See HHH-12753
|
||||||
|
// It seems that DB2's JDBC 4.0 support as of 9.5 does not
|
||||||
|
// support the N-variant methods like NClob or NString.
|
||||||
|
// Therefore here we overwrite the sql type descriptors to
|
||||||
|
// use the non-N variants which are supported.
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.NCHAR, CharJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor(
|
||||||
|
Types.NCLOB,
|
||||||
|
useInputStreamToInsertBlob()
|
||||||
|
? ClobJdbcType.STREAM_BINDING
|
||||||
|
: ClobJdbcType.CLOB_BINDING
|
||||||
|
);
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, VarcharJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.NUMERIC, DecimalJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
jdbcTypeRegistry.addDescriptor( XmlJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
// DB2 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 )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) {
|
||||||
|
appender.appendSql( "BX'" );
|
||||||
|
PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||||
|
return (sqlException, message, sql) -> {
|
||||||
|
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||||
|
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||||
|
|
||||||
|
if ( -952 == errorCode && "57014".equals( sqlState ) ) {
|
||||||
|
throw new LockTimeoutException( message, sqlException, sql );
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UniqueDelegate getUniqueDelegate() {
|
||||||
|
return uniqueDelegate;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxIdentifierLength() {
|
||||||
|
return 128;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitHandler getLimitHandler() {
|
||||||
|
return limitHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNullPrecedence() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
@Override
|
||||||
|
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||||
|
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
return new DB2LegacySqlAstTranslator<>( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
|
return new DB2IdentityColumnSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsValuesList() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsPartitionBy() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNonQueryWithCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsOffsetInSubquery() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsWindowFunctions() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
return getDB2Version().isSameOrAfter( 9, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||||
|
//DB2 does not need nor support FM
|
||||||
|
appender.appendSql( OracleDialect.datetimeFormat( format, false, false ).result() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translateExtractField(TemporalUnit unit) {
|
||||||
|
switch ( unit ) {
|
||||||
|
//WEEK means the ISO week number on DB2
|
||||||
|
case DAY_OF_MONTH: return "day";
|
||||||
|
case DAY_OF_YEAR: return "doy";
|
||||||
|
case DAY_OF_WEEK: return "dow";
|
||||||
|
default: return super.translateExtractField( unit );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
|
||||||
|
if ( getDB2Version().isBefore( 11 ) ) {
|
||||||
|
appender.appendSql( bool ? '1' : '0' );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appender.appendSql( bool );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String extractPattern(TemporalUnit unit) {
|
||||||
|
if ( unit == TemporalUnit.WEEK ) {
|
||||||
|
// Not sure why, but `extract(week from '2019-05-27')` wrongly returns 21 and week_iso behaves correct
|
||||||
|
return "week_iso(?2)";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return super.extractPattern( unit );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInExpressionCountLimit() {
|
||||||
|
return BIND_PARAMETERS_NUMBER_LIMIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generatedAs(String generatedAs) {
|
||||||
|
return " generated always as (" + generatedAs + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||||
|
throws SQLException {
|
||||||
|
builder.setAutoQuoteInitialUnderscore(true);
|
||||||
|
return super.buildIdentifierHelper(builder, dbMetaData);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,311 @@
|
||||||
|
/*
|
||||||
|
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.query.sqm.FetchClauseType;
|
||||||
|
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||||
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||||
|
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.delete.DeleteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.insert.InsertStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||||
|
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SQL AST translator for DB2.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||||
|
|
||||||
|
public DB2LegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
super( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
|
expression.accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
|
if ( getDB2Version().isSameOrAfter( 11 ) ) {
|
||||||
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( "not(" );
|
||||||
|
}
|
||||||
|
booleanExpressionPredicate.getExpression().accept( this );
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitBooleanExpressionPredicate( booleanExpressionPredicate );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DB2 does not allow CASE expressions where all result arms contain plain parameters.
|
||||||
|
// At least one result arm must provide some type context for inference,
|
||||||
|
// so we cast the first result arm if we encounter this condition
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitAnsiCaseSearchedExpression(
|
||||||
|
CaseSearchedExpression caseSearchedExpression,
|
||||||
|
Consumer<Expression> resultRenderer) {
|
||||||
|
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSearchedExpression ) ) {
|
||||||
|
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
||||||
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
|
super.visitAnsiCaseSearchedExpression(
|
||||||
|
caseSearchedExpression,
|
||||||
|
e -> {
|
||||||
|
if ( e == firstResult ) {
|
||||||
|
renderCasted( e );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultRenderer.accept( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitAnsiCaseSearchedExpression( caseSearchedExpression, resultRenderer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitAnsiCaseSimpleExpression(
|
||||||
|
CaseSimpleExpression caseSimpleExpression,
|
||||||
|
Consumer<Expression> resultRenderer) {
|
||||||
|
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSimpleExpression ) ) {
|
||||||
|
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
||||||
|
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||||
|
super.visitAnsiCaseSimpleExpression(
|
||||||
|
caseSimpleExpression,
|
||||||
|
e -> {
|
||||||
|
if ( e == firstResult ) {
|
||||||
|
renderCasted( e );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
resultRenderer.accept( e );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitAnsiCaseSimpleExpression( caseSimpleExpression, resultRenderer );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getForUpdate() {
|
||||||
|
return " for read only with rs use and keep update locks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getForShare(int timeoutMillis) {
|
||||||
|
return " for read only with rs use and keep share locks";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getSkipLocked() {
|
||||||
|
return " skip locked data";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||||
|
// Percent fetches or ties fetches aren't supported in DB2
|
||||||
|
// According to LegacyDB2LimitHandler, variable limit also isn't supported before 11.1
|
||||||
|
// Check if current query part is already row numbering to avoid infinite recursion
|
||||||
|
return getQueryPartForRowNumbering() != queryPart && (
|
||||||
|
useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart )
|
||||||
|
|| getDB2Version().isBefore( 11, 1 ) && ( queryPart.isRoot() && hasLimit() || !( queryPart.getFetchClauseExpression() instanceof Literal ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean supportsOffsetClause() {
|
||||||
|
return getDB2Version().isSameOrAfter( 11, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||||
|
final boolean emulateFetchClause = shouldEmulateFetchClause( queryGroup );
|
||||||
|
if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( queryGroup ) ) {
|
||||||
|
emulateFetchOffsetWithWindowFunctions( queryGroup, emulateFetchClause );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitQueryGroup( queryGroup );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQuerySpec(QuerySpec querySpec) {
|
||||||
|
final boolean emulateFetchClause = shouldEmulateFetchClause( querySpec );
|
||||||
|
if ( emulateFetchClause || !supportsOffsetClause() && hasOffset( querySpec ) ) {
|
||||||
|
emulateFetchOffsetWithWindowFunctions( querySpec, emulateFetchClause );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitQuerySpec( querySpec );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
if ( supportsOffsetClause() || !hasOffset( queryPart ) ) {
|
||||||
|
renderOffsetFetchClause( queryPart, true );
|
||||||
|
}
|
||||||
|
else if ( queryPart.isRoot() && hasLimit() ) {
|
||||||
|
renderFetch( getLimitParameter(), null, FetchClauseType.ROWS_ONLY );
|
||||||
|
}
|
||||||
|
else if ( queryPart.getFetchClauseExpression() != null ) {
|
||||||
|
renderFetch( queryPart.getFetchClauseExpression(), null, queryPart.getFetchClauseType() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitDeleteStatementOnly(DeleteStatement statement) {
|
||||||
|
final boolean closeWrapper = renderReturningClause( statement );
|
||||||
|
super.visitDeleteStatementOnly( statement );
|
||||||
|
if ( closeWrapper ) {
|
||||||
|
appendSql( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitUpdateStatementOnly(UpdateStatement statement) {
|
||||||
|
final boolean closeWrapper = renderReturningClause( statement );
|
||||||
|
super.visitUpdateStatementOnly( statement );
|
||||||
|
if ( closeWrapper ) {
|
||||||
|
appendSql( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitInsertStatementOnly(InsertStatement statement) {
|
||||||
|
final boolean closeWrapper = renderReturningClause( statement );
|
||||||
|
super.visitInsertStatementOnly( statement );
|
||||||
|
if ( closeWrapper ) {
|
||||||
|
appendSql( ')' );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean renderReturningClause(MutationStatement statement) {
|
||||||
|
final List<ColumnReference> returningColumns = statement.getReturningColumns();
|
||||||
|
final int size = returningColumns.size();
|
||||||
|
if ( size == 0 ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
appendSql( "select " );
|
||||||
|
String separator = "";
|
||||||
|
for ( int i = 0; i < size; i++ ) {
|
||||||
|
appendSql( separator );
|
||||||
|
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||||
|
separator = ",";
|
||||||
|
}
|
||||||
|
if ( statement instanceof DeleteStatement ) {
|
||||||
|
appendSql( " from old table (" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appendSql( " from final table (" );
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||||
|
renderComparisonStandard( lhs, operator, rhs );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderComparisonEmulateDecode( lhs, operator, rhs );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderSelectExpression(Expression expression) {
|
||||||
|
renderSelectExpressionWithCastedOrInlinedPlainParameters( expression );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderSelectTupleComparison(
|
||||||
|
List<SqlSelection> lhsExpressions,
|
||||||
|
SqlTuple tuple,
|
||||||
|
ComparisonOperator operator) {
|
||||||
|
emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderPartitionItem(Expression expression) {
|
||||||
|
if ( expression instanceof Literal ) {
|
||||||
|
appendSql( "()" );
|
||||||
|
}
|
||||||
|
else if ( expression instanceof Summarization ) {
|
||||||
|
Summarization summarization = (Summarization) expression;
|
||||||
|
appendSql( summarization.getKind().sqlText() );
|
||||||
|
appendSql( OPEN_PARENTHESIS );
|
||||||
|
renderCommaSeparated( summarization.getGroupings() );
|
||||||
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expression.accept( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntax() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getFromDual() {
|
||||||
|
return " from sysibm.dual";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getFromDualForSelectOnly() {
|
||||||
|
return getFromDual();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitReturningColumns(MutationStatement mutationStatement) {
|
||||||
|
// For DB2 we use #renderReturningClause to render a wrapper around the DML statement
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return this.getDialect().getVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
import org.hibernate.dialect.identity.DB2390IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.DB2IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.pagination.FetchLimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
|
import org.hibernate.dialect.sequence.DB2iSequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
import org.hibernate.dialect.unique.DefaultUniqueDelegate;
|
||||||
|
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||||
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
|
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An SQL dialect for DB2 for iSeries previously known as DB2/400.
|
||||||
|
*
|
||||||
|
* @author Peter DeGregorio (pdegregorio)
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class DB2iLegacyDialect extends DB2LegacyDialect {
|
||||||
|
|
||||||
|
final static DatabaseVersion DB2_LUW_VERSION9 = DatabaseVersion.make( 9, 0);
|
||||||
|
|
||||||
|
public DB2iLegacyDialect(DialectResolutionInfo info) {
|
||||||
|
this( info.makeCopy() );
|
||||||
|
registerKeywords( info );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2iLegacyDialect() {
|
||||||
|
this( DatabaseVersion.make(7) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2iLegacyDialect(DatabaseVersion version) {
|
||||||
|
super(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
if ( getVersion().isSameOrAfter( 7, 2 ) ) {
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
|
functionFactory.listagg( null );
|
||||||
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return DB2_LUW_VERSION9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UniqueDelegate createUniqueDelegate() {
|
||||||
|
return getVersion().isSameOrAfter(7, 3)
|
||||||
|
? new DefaultUniqueDelegate(this)
|
||||||
|
: super.createUniqueDelegate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDistinctFromPredicate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No support for sequences.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public SequenceSupport getSequenceSupport() {
|
||||||
|
return getVersion().isSameOrAfter(7, 3)
|
||||||
|
? DB2iSequenceSupport.INSTANCE : NoSequenceSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuerySequencesString() {
|
||||||
|
if ( getVersion().isSameOrAfter(7,3) ) {
|
||||||
|
return "select distinct sequence_name from qsys2.syssequences " +
|
||||||
|
"where current_schema='*LIBL' and sequence_schema in (select schema_name from qsys2.library_list_info) " +
|
||||||
|
"or sequence_schema=current_schema";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitHandler getLimitHandler() {
|
||||||
|
return getVersion().isSameOrAfter(7, 3)
|
||||||
|
? FetchLimitHandler.INSTANCE : LegacyDB2LimitHandler.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
|
return getVersion().isSameOrAfter(7, 3)
|
||||||
|
? new DB2IdentityColumnSupport()
|
||||||
|
: new DB2390IdentityColumnSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSkipLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
return getVersion().isSameOrAfter( 7, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
@Override
|
||||||
|
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||||
|
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
return new DB2iLegacySqlAstTranslator<>( sessionFactory, statement, getVersion() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
import static org.hibernate.community.dialect.DB2iLegacyDialect.DB2_LUW_VERSION9;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SQL AST translator for DB2i.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class DB2iLegacySqlAstTranslator<T extends JdbcOperation> extends DB2LegacySqlAstTranslator<T> {
|
||||||
|
|
||||||
|
private final DatabaseVersion version;
|
||||||
|
|
||||||
|
public DB2iLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement, DatabaseVersion version) {
|
||||||
|
super( sessionFactory, statement );
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||||
|
// Percent fetches or ties fetches aren't supported in DB2 iSeries
|
||||||
|
// According to LegacyDB2LimitHandler, variable limit also isn't supported before 7.1
|
||||||
|
return getQueryPartForRowNumbering() != queryPart && (
|
||||||
|
useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart )
|
||||||
|
|| version.isBefore(7, 10) && ( queryPart.isRoot() && hasLimit() || !( queryPart.getFetchClauseExpression() instanceof Literal ) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsOffsetClause() {
|
||||||
|
return version.isSameOrAfter(7, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
renderComparisonStandard( lhs, operator, rhs );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return DB2_LUW_VERSION9;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,189 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.dialect.TimeZoneSupport;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
import org.hibernate.dialect.identity.DB2390IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.pagination.FetchLimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
|
||||||
|
import org.hibernate.dialect.sequence.DB2zSequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.query.spi.QueryEngine;
|
||||||
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||||
|
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An SQL dialect for DB2 for z/OS, previously known as known as Db2 UDB for z/OS and Db2 UDB for z/OS and OS/390.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class DB2zLegacyDialect extends DB2LegacyDialect {
|
||||||
|
|
||||||
|
final static DatabaseVersion DB2_LUW_VERSION9 = DatabaseVersion.make( 9, 0);
|
||||||
|
|
||||||
|
public DB2zLegacyDialect(DialectResolutionInfo info) {
|
||||||
|
this( info.makeCopy() );
|
||||||
|
registerKeywords( info );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2zLegacyDialect() {
|
||||||
|
this( DatabaseVersion.make( 7 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public DB2zLegacyDialect(DatabaseVersion version) {
|
||||||
|
super(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
if ( getVersion().isSameOrAfter( 12 ) ) {
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
|
functionFactory.listagg( null );
|
||||||
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String columnType(int sqlTypeCode) {
|
||||||
|
if ( sqlTypeCode == TIMESTAMP_WITH_TIMEZONE && getVersion().isAfter( 10 ) ) {
|
||||||
|
// See https://www.ibm.com/support/knowledgecenter/SSEPEK_10.0.0/wnew/src/tpc/db2z_10_timestamptimezone.html
|
||||||
|
return "timestamp with time zone";
|
||||||
|
}
|
||||||
|
return super.columnType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return DB2_LUW_VERSION9;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDistinctFromPredicate() {
|
||||||
|
// Supported at least since DB2 z/OS 9.0
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeZoneSupport getTimeZoneSupport() {
|
||||||
|
return getVersion().isAfter(10) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceSupport getSequenceSupport() {
|
||||||
|
return getVersion().isBefore(8)
|
||||||
|
? NoSequenceSupport.INSTANCE
|
||||||
|
: DB2zSequenceSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuerySequencesString() {
|
||||||
|
return getVersion().isBefore(8) ? null : "select * from sysibm.syssequences";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitHandler getLimitHandler() {
|
||||||
|
return getVersion().isBefore(12)
|
||||||
|
? FetchLimitHandler.INSTANCE
|
||||||
|
: OffsetFetchLimitHandler.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
|
return new DB2390IdentityColumnSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSkipLocked() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
|
StringBuilder pattern = new StringBuilder();
|
||||||
|
final boolean castTo;
|
||||||
|
if ( unit.isDateUnit() ) {
|
||||||
|
castTo = temporalType == TemporalType.TIME;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
castTo = temporalType == TemporalType.DATE;
|
||||||
|
}
|
||||||
|
pattern.append("add_");
|
||||||
|
switch (unit) {
|
||||||
|
case NATIVE:
|
||||||
|
case NANOSECOND:
|
||||||
|
pattern.append("second");
|
||||||
|
break;
|
||||||
|
case WEEK:
|
||||||
|
//note: DB2 does not have add_weeks()
|
||||||
|
pattern.append("day");
|
||||||
|
break;
|
||||||
|
case QUARTER:
|
||||||
|
pattern.append("month");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pattern.append("?1");
|
||||||
|
}
|
||||||
|
pattern.append("s(");
|
||||||
|
if (castTo) {
|
||||||
|
pattern.append("cast(?3 as timestamp)");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
pattern.append("?3");
|
||||||
|
}
|
||||||
|
pattern.append(",");
|
||||||
|
switch (unit) {
|
||||||
|
case NANOSECOND:
|
||||||
|
pattern.append("(?2)/1e9");
|
||||||
|
break;
|
||||||
|
case WEEK:
|
||||||
|
pattern.append("(?2)*7");
|
||||||
|
break;
|
||||||
|
case QUARTER:
|
||||||
|
pattern.append("(?2)*3");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
pattern.append("?2");
|
||||||
|
}
|
||||||
|
pattern.append(")");
|
||||||
|
return pattern.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
@Override
|
||||||
|
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||||
|
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
return new DB2zLegacySqlAstTranslator<>( sessionFactory, statement, getVersion() );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* 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 http://www.gnu.org/licenses/lgpl-2.1.html
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
import static org.hibernate.community.dialect.DB2zLegacyDialect.DB2_LUW_VERSION9;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SQL AST translator for DB2z.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class DB2zLegacySqlAstTranslator<T extends JdbcOperation> extends DB2LegacySqlAstTranslator<T> {
|
||||||
|
|
||||||
|
private final DatabaseVersion version;
|
||||||
|
|
||||||
|
public DB2zLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement, DatabaseVersion version) {
|
||||||
|
super( sessionFactory, statement );
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||||
|
// Percent fetches or ties fetches aren't supported in DB2 z/OS
|
||||||
|
// Also, variable limit isn't supported before 12.0
|
||||||
|
return getQueryPartForRowNumbering() != queryPart && (
|
||||||
|
useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart )
|
||||||
|
|| version.isBefore(12) && queryPart.isRoot() && hasLimit()
|
||||||
|
|| version.isBefore(12) && queryPart.getFetchClauseExpression() != null && !( queryPart.getFetchClauseExpression() instanceof Literal )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsOffsetClause() {
|
||||||
|
return version.isSameOrAfter(12);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||||
|
// Supported at least since DB2 z/OS 9.0
|
||||||
|
renderComparisonStandard( lhs, operator, rhs );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||||
|
if ( tableReference instanceof NamedTableReference ) {
|
||||||
|
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||||
|
}
|
||||||
|
// DB2 z/OS we need the "table" qualifier for table valued functions or lateral sub-queries
|
||||||
|
append( "table " );
|
||||||
|
tableReference.accept( this );
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitFunctionTableReference(FunctionTableReference tableReference) {
|
||||||
|
// For the table qualifier we need parenthesis on DB2 z/OS
|
||||||
|
append( OPEN_PARENTHESIS );
|
||||||
|
tableReference.getFunctionExpression().accept( this );
|
||||||
|
append( CLOSE_PARENTHESIS );
|
||||||
|
renderDerivedTableReference( tableReference );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DatabaseVersion getDB2Version() {
|
||||||
|
return DB2_LUW_VERSION9;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.community.dialect;
|
||||||
|
|
||||||
|
import java.sql.Types;
|
||||||
|
|
||||||
|
import org.hibernate.engine.jdbc.Size;
|
||||||
|
import org.hibernate.query.spi.Limit;
|
||||||
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DB2 dialect related test cases
|
||||||
|
*
|
||||||
|
* @author Hardy Ferentschik
|
||||||
|
*/
|
||||||
|
|
||||||
|
public class DB2LegacyDialectTestCase extends BaseUnitTestCase {
|
||||||
|
private final DB2LegacyDialect dialect = new DB2LegacyDialect();
|
||||||
|
private TypeConfiguration typeConfiguration;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
typeConfiguration = new TypeConfiguration();
|
||||||
|
dialect.contributeTypes( () -> typeConfiguration, null );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-6866")
|
||||||
|
public void testGetDefaultBinaryTypeName() {
|
||||||
|
String actual = typeConfiguration.getDdlTypeRegistry().getTypeName( Types.BINARY, dialect );
|
||||||
|
assertEquals(
|
||||||
|
"The default column length is 255, but char length on DB2 is limited to 254",
|
||||||
|
"varchar($l) for bit data",
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-6866")
|
||||||
|
public void testGetExplicitBinaryTypeName() {
|
||||||
|
// lower bound
|
||||||
|
String actual = typeConfiguration.getDdlTypeRegistry().getTypeName( Types.BINARY, Size.length( 1) );
|
||||||
|
assertEquals(
|
||||||
|
"Wrong binary type",
|
||||||
|
"char(1) for bit data",
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// upper bound
|
||||||
|
actual = typeConfiguration.getDdlTypeRegistry().getTypeName( Types.BINARY, Size.length( 254) );
|
||||||
|
assertEquals(
|
||||||
|
"Wrong binary type. 254 is the max length in DB2",
|
||||||
|
"char(254) for bit data",
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
|
||||||
|
// exceeding upper bound
|
||||||
|
actual = typeConfiguration.getDdlTypeRegistry().getTypeName( Types.BINARY, Size.length( 255) );
|
||||||
|
assertEquals(
|
||||||
|
"Wrong binary type. Should be varchar for length > 254",
|
||||||
|
"varchar(255) for bit data",
|
||||||
|
actual
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestForIssue(jiraKey = "HHH-12369")
|
||||||
|
public void testIntegerOverflowForMaxResults() {
|
||||||
|
Limit rowSelection = new Limit();
|
||||||
|
rowSelection.setFirstRow(1);
|
||||||
|
rowSelection.setMaxRows(Integer.MAX_VALUE);
|
||||||
|
String sql = dialect.getLimitHandler().processSql( "select a.id from tbl_a a order by a.id", rowSelection );
|
||||||
|
assertTrue(
|
||||||
|
"Integer overflow for max rows in: " + sql,
|
||||||
|
sql.contains("fetch first 2147483647 rows only")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@ import org.hibernate.dialect.pagination.DB2LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
|
import org.hibernate.dialect.pagination.LegacyDB2LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.LimitHandler;
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
||||||
import org.hibernate.dialect.sequence.LegacyDB2SequenceSupport;
|
|
||||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
import org.hibernate.dialect.unique.DB2UniqueDelegate;
|
import org.hibernate.dialect.unique.DB2UniqueDelegate;
|
||||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||||
|
@ -77,6 +76,7 @@ import static org.hibernate.type.SqlTypes.*;
|
||||||
*/
|
*/
|
||||||
public class DB2Dialect extends Dialect {
|
public class DB2Dialect extends Dialect {
|
||||||
|
|
||||||
|
final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 10, 5 );
|
||||||
private static final int BIND_PARAMETERS_NUMBER_LIMIT = 32_767;
|
private static final int BIND_PARAMETERS_NUMBER_LIMIT = 32_767;
|
||||||
|
|
||||||
private static final String FOR_READ_ONLY_SQL = " for read only with rs";
|
private static final String FOR_READ_ONLY_SQL = " for read only with rs";
|
||||||
|
@ -92,7 +92,7 @@ public class DB2Dialect extends Dialect {
|
||||||
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
private final UniqueDelegate uniqueDelegate = createUniqueDelegate();
|
||||||
|
|
||||||
public DB2Dialect() {
|
public DB2Dialect() {
|
||||||
this( DatabaseVersion.make( 9, 0 ) );
|
this( MINIMUM_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB2Dialect(DialectResolutionInfo info) {
|
public DB2Dialect(DialectResolutionInfo info) {
|
||||||
|
@ -103,6 +103,11 @@ public class DB2Dialect extends Dialect {
|
||||||
super( version );
|
super( version );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||||
|
return MINIMUM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void registerDefaultKeywords() {
|
protected void registerDefaultKeywords() {
|
||||||
super.registerDefaultKeywords();
|
super.registerDefaultKeywords();
|
||||||
|
@ -165,15 +170,11 @@ public class DB2Dialect extends Dialect {
|
||||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||||
|
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||||
|
ddlTypeRegistry.addDescriptor(
|
||||||
if ( getDB2Version().isBefore( 11 ) ) {
|
CapacityDependentDdlType.builder( BINARY, "varchar($l) for bit data", this )
|
||||||
// should use 'binary' since version 11
|
.withTypeCapacity( 254, "char($l) for bit data" )
|
||||||
ddlTypeRegistry.addDescriptor(
|
.build()
|
||||||
CapacityDependentDdlType.builder( BINARY, "varchar($l) for bit data", this )
|
);
|
||||||
.withTypeCapacity( 254, "char($l) for bit data" )
|
|
||||||
.build()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected UniqueDelegate createUniqueDelegate() {
|
protected UniqueDelegate createUniqueDelegate() {
|
||||||
|
@ -310,12 +311,10 @@ public class DB2Dialect extends Dialect {
|
||||||
.register();
|
.register();
|
||||||
|
|
||||||
functionFactory.windowFunctions();
|
functionFactory.windowFunctions();
|
||||||
if ( getDB2Version().isSameOrAfter( 9, 5 ) ) {
|
functionFactory.listagg( null );
|
||||||
functionFactory.listagg( null );
|
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||||
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
functionFactory.inverseDistributionOrderedSetAggregates();
|
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,11 +425,6 @@ public class DB2Dialect extends Dialect {
|
||||||
return pattern.toString();
|
return pattern.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLowercaseFunction() {
|
|
||||||
return getDB2Version().isBefore( 9, 7 ) ? "lcase" : super.getLowercaseFunction();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean dropConstraints() {
|
public boolean dropConstraints() {
|
||||||
return false;
|
return false;
|
||||||
|
@ -438,9 +432,7 @@ public class DB2Dialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceSupport getSequenceSupport() {
|
public SequenceSupport getSequenceSupport() {
|
||||||
return getDB2Version().isBefore( 9, 7 )
|
return DB2SequenceSupport.INSTANCE;
|
||||||
? LegacyDB2SequenceSupport.INSTANCE
|
|
||||||
: DB2SequenceSupport.INSTANCE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -745,7 +737,7 @@ public class DB2Dialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getDB2Version().isSameOrAfter( 9, 1 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,7 +35,8 @@ import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
*/
|
*/
|
||||||
public class DB2iDialect extends DB2Dialect {
|
public class DB2iDialect extends DB2Dialect {
|
||||||
|
|
||||||
final static DatabaseVersion DB2_LUW_VERSION9 = DatabaseVersion.make(9, 0);
|
private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 7, 1 );
|
||||||
|
final static DatabaseVersion DB2_LUW_VERSION = DB2Dialect.MINIMUM_VERSION;
|
||||||
|
|
||||||
public DB2iDialect(DialectResolutionInfo info) {
|
public DB2iDialect(DialectResolutionInfo info) {
|
||||||
this( info.makeCopy() );
|
this( info.makeCopy() );
|
||||||
|
@ -43,13 +44,18 @@ public class DB2iDialect extends DB2Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB2iDialect() {
|
public DB2iDialect() {
|
||||||
this( DatabaseVersion.make(7) );
|
this( MINIMUM_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB2iDialect(DatabaseVersion version) {
|
public DB2iDialect(DatabaseVersion version) {
|
||||||
super(version);
|
super(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||||
|
return MINIMUM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
super.initializeFunctionRegistry( queryEngine );
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
@ -63,7 +69,7 @@ public class DB2iDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatabaseVersion getDB2Version() {
|
public DatabaseVersion getDB2Version() {
|
||||||
return DB2_LUW_VERSION9;
|
return DB2_LUW_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -119,7 +125,7 @@ public class DB2iDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getVersion().isSameOrAfter( 7, 1 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -14,7 +14,7 @@ import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
import static org.hibernate.dialect.DB2iDialect.DB2_LUW_VERSION9;
|
import static org.hibernate.dialect.DB2iDialect.DB2_LUW_VERSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQL AST translator for DB2i.
|
* A SQL AST translator for DB2i.
|
||||||
|
@ -52,6 +52,6 @@ public class DB2iSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatabaseVersion getDB2Version() {
|
public DatabaseVersion getDB2Version() {
|
||||||
return DB2_LUW_VERSION9;
|
return DB2_LUW_VERSION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,9 @@ import jakarta.persistence.TemporalType;
|
||||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
import org.hibernate.dialect.identity.DB2390IdentityColumnSupport;
|
import org.hibernate.dialect.identity.DB2390IdentityColumnSupport;
|
||||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
import org.hibernate.dialect.pagination.FetchLimitHandler;
|
|
||||||
import org.hibernate.dialect.pagination.LimitHandler;
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
|
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
|
||||||
import org.hibernate.dialect.sequence.DB2zSequenceSupport;
|
import org.hibernate.dialect.sequence.DB2zSequenceSupport;
|
||||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
|
||||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
@ -38,7 +36,8 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||||
*/
|
*/
|
||||||
public class DB2zDialect extends DB2Dialect {
|
public class DB2zDialect extends DB2Dialect {
|
||||||
|
|
||||||
final static DatabaseVersion DB2_LUW_VERSION9 = DatabaseVersion.make(9, 0);
|
private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 12, 1 );
|
||||||
|
final static DatabaseVersion DB2_LUW_VERSION = DB2Dialect.MINIMUM_VERSION;
|
||||||
|
|
||||||
public DB2zDialect(DialectResolutionInfo info) {
|
public DB2zDialect(DialectResolutionInfo info) {
|
||||||
this( info.makeCopy() );
|
this( info.makeCopy() );
|
||||||
|
@ -46,13 +45,18 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB2zDialect() {
|
public DB2zDialect() {
|
||||||
this( DatabaseVersion.make( 7 ) );
|
this( MINIMUM_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
public DB2zDialect(DatabaseVersion version) {
|
public DB2zDialect(DatabaseVersion version) {
|
||||||
super(version);
|
super(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||||
|
return MINIMUM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
super.initializeFunctionRegistry( queryEngine );
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
@ -75,7 +79,7 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatabaseVersion getDB2Version() {
|
public DatabaseVersion getDB2Version() {
|
||||||
return DB2_LUW_VERSION9;
|
return DB2_LUW_VERSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -91,21 +95,17 @@ public class DB2zDialect extends DB2Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SequenceSupport getSequenceSupport() {
|
public SequenceSupport getSequenceSupport() {
|
||||||
return getVersion().isBefore(8)
|
return DB2zSequenceSupport.INSTANCE;
|
||||||
? NoSequenceSupport.INSTANCE
|
|
||||||
: DB2zSequenceSupport.INSTANCE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getQuerySequencesString() {
|
public String getQuerySequencesString() {
|
||||||
return getVersion().isBefore(8) ? null : "select * from sysibm.syssequences";
|
return "select * from sysibm.syssequences";
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LimitHandler getLimitHandler() {
|
public LimitHandler getLimitHandler() {
|
||||||
return getVersion().isBefore(12)
|
return OffsetFetchLimitHandler.INSTANCE;
|
||||||
? FetchLimitHandler.INSTANCE
|
|
||||||
: OffsetFetchLimitHandler.INSTANCE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -11,7 +11,6 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
import org.hibernate.query.sqm.ComparisonOperator;
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
|
||||||
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
import org.hibernate.sql.ast.tree.from.FunctionTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
@ -19,7 +18,7 @@ import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
import org.hibernate.sql.ast.tree.select.QueryPart;
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
import static org.hibernate.dialect.DB2zDialect.DB2_LUW_VERSION9;
|
import static org.hibernate.dialect.DB2zDialect.DB2_LUW_VERSION;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SQL AST translator for DB2z.
|
* A SQL AST translator for DB2z.
|
||||||
|
@ -39,11 +38,7 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
|
||||||
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||||
// Percent fetches or ties fetches aren't supported in DB2 z/OS
|
// Percent fetches or ties fetches aren't supported in DB2 z/OS
|
||||||
// Also, variable limit isn't supported before 12.0
|
// Also, variable limit isn't supported before 12.0
|
||||||
return getQueryPartForRowNumbering() != queryPart && (
|
return getQueryPartForRowNumbering() != queryPart && ( useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart ) );
|
||||||
useOffsetFetchClause( queryPart ) && !isRowsOnlyFetchClauseType( queryPart )
|
|
||||||
|| version.isBefore(12) && queryPart.isRoot() && hasLimit()
|
|
||||||
|| version.isBefore(12) && queryPart.getFetchClauseExpression() != null && !( queryPart.getFetchClauseExpression() instanceof Literal )
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -80,6 +75,6 @@ public class DB2zSqlAstTranslator<T extends JdbcOperation> extends DB2SqlAstTran
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DatabaseVersion getDB2Version() {
|
public DatabaseVersion getDB2Version() {
|
||||||
return DB2_LUW_VERSION9;
|
return DB2_LUW_VERSION;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,10 @@ import org.hibernate.MappingException;
|
||||||
* Sequence support for {@link org.hibernate.dialect.DB2Dialect}.
|
* Sequence support for {@link org.hibernate.dialect.DB2Dialect}.
|
||||||
*
|
*
|
||||||
* @author Gavin King
|
* @author Gavin King
|
||||||
|
*
|
||||||
|
* @deprecated use {@code DB2SequenceSupport}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class LegacyDB2SequenceSupport implements SequenceSupport {
|
public class LegacyDB2SequenceSupport implements SequenceSupport {
|
||||||
|
|
||||||
public static final SequenceSupport INSTANCE = new LegacyDB2SequenceSupport();
|
public static final SequenceSupport INSTANCE = new LegacyDB2SequenceSupport();
|
||||||
|
|
Loading…
Reference in New Issue