HHH-15190 - Remove support for H2 versions older than 1.4.197
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
89f04d2274
commit
f33d3ed308
|
@ -0,0 +1,711 @@
|
||||||
|
/*
|
||||||
|
* 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.SQLException;
|
||||||
|
import java.sql.Types;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.hibernate.PessimisticLockException;
|
||||||
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.H2DurationIntervalSecondJdbcType;
|
||||||
|
import org.hibernate.dialect.OracleDialect;
|
||||||
|
import org.hibernate.dialect.Replacer;
|
||||||
|
import org.hibernate.dialect.SelectItemReferenceStrategy;
|
||||||
|
import org.hibernate.dialect.SimpleDatabaseVersion;
|
||||||
|
import org.hibernate.dialect.TimeZoneSupport;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
import org.hibernate.dialect.hint.IndexQueryHintHandler;
|
||||||
|
import org.hibernate.dialect.identity.H2IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.LimitOffsetLimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
|
||||||
|
import org.hibernate.dialect.sequence.H2V1SequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.H2V2SequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
import org.hibernate.dialect.temptable.TemporaryTable;
|
||||||
|
import org.hibernate.dialect.temptable.TemporaryTableKind;
|
||||||
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.exception.ConstraintViolationException;
|
||||||
|
import org.hibernate.exception.LockAcquisitionException;
|
||||||
|
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||||
|
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
|
||||||
|
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
|
||||||
|
import org.hibernate.internal.CoreLogging;
|
||||||
|
import org.hibernate.internal.CoreMessageLogger;
|
||||||
|
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.FetchClauseType;
|
||||||
|
import org.hibernate.query.sqm.IntervalType;
|
||||||
|
import org.hibernate.query.sqm.NullOrdering;
|
||||||
|
import org.hibernate.query.sqm.TemporalUnit;
|
||||||
|
import org.hibernate.query.sqm.mutation.internal.temptable.BeforeUseAction;
|
||||||
|
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableInsertStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.internal.temptable.LocalTemporaryTableMutationStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||||
|
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||||
|
import org.hibernate.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.SequenceInformationExtractorH2DatabaseImpl;
|
||||||
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorLegacyImpl;
|
||||||
|
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
|
||||||
|
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.InstantJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
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.query.sqm.TemporalUnit.SECOND;
|
||||||
|
import static org.hibernate.type.SqlTypes.ARRAY;
|
||||||
|
import static org.hibernate.type.SqlTypes.BINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.CHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.DECIMAL;
|
||||||
|
import static org.hibernate.type.SqlTypes.DOUBLE;
|
||||||
|
import static org.hibernate.type.SqlTypes.FLOAT;
|
||||||
|
import static org.hibernate.type.SqlTypes.GEOMETRY;
|
||||||
|
import static org.hibernate.type.SqlTypes.INTERVAL_SECOND;
|
||||||
|
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.NUMERIC;
|
||||||
|
import static org.hibernate.type.SqlTypes.NVARCHAR;
|
||||||
|
import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
|
import static org.hibernate.type.SqlTypes.UUID;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARBINARY;
|
||||||
|
import static org.hibernate.type.SqlTypes.VARCHAR;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A legacy {@linkplain Dialect SQL dialect} for H2.
|
||||||
|
*
|
||||||
|
* @author Thomas Mueller
|
||||||
|
*/
|
||||||
|
public class H2LegacyDialect extends Dialect {
|
||||||
|
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( H2LegacyDialect.class );
|
||||||
|
|
||||||
|
private final LimitHandler limitHandler;
|
||||||
|
|
||||||
|
private final boolean ansiSequence;
|
||||||
|
private final boolean cascadeConstraints;
|
||||||
|
private final boolean useLocalTime;
|
||||||
|
|
||||||
|
private final SequenceInformationExtractor sequenceInformationExtractor;
|
||||||
|
private final String querySequenceString;
|
||||||
|
|
||||||
|
public H2LegacyDialect(DialectResolutionInfo info) {
|
||||||
|
this( parseVersion( info ) );
|
||||||
|
registerKeywords( info );
|
||||||
|
}
|
||||||
|
|
||||||
|
public H2LegacyDialect() {
|
||||||
|
this( SimpleDatabaseVersion.ZERO_VERSION );
|
||||||
|
}
|
||||||
|
|
||||||
|
public H2LegacyDialect(DatabaseVersion version) {
|
||||||
|
super(version);
|
||||||
|
|
||||||
|
// https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440
|
||||||
|
limitHandler = version.isSameOrAfter( 1, 4, 195 )
|
||||||
|
? OffsetFetchLimitHandler.INSTANCE
|
||||||
|
: LimitOffsetLimitHandler.INSTANCE;
|
||||||
|
|
||||||
|
if ( version.isBefore( 1, 2, 139 ) ) {
|
||||||
|
LOG.unsupportedMultiTableBulkHqlJpaql( version.getMajor(), version.getMinor(), version.getMicro() );
|
||||||
|
}
|
||||||
|
|
||||||
|
// supportsTuplesInSubqueries = version.isSameOrAfter( 1, 4, 198 );
|
||||||
|
|
||||||
|
// Prior to 1.4.200 there was no support for 'current value for sequence_name'
|
||||||
|
// After 2.0.202 there is no support for 'sequence_name.nextval' and 'sequence_name.currval'
|
||||||
|
ansiSequence = version.isSameOrAfter( 1, 4, 200 );
|
||||||
|
|
||||||
|
// Prior to 1.4.200 the 'cascade' in 'drop table' was implicit
|
||||||
|
cascadeConstraints = version.isSameOrAfter( 1, 4, 200 );
|
||||||
|
// 1.4.200 introduced changes in current_time and current_timestamp
|
||||||
|
useLocalTime = version.isSameOrAfter( 1, 4, 200 );
|
||||||
|
|
||||||
|
if ( version.isSameOrAfter( 1, 4, 32 ) ) {
|
||||||
|
this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 )
|
||||||
|
? SequenceInformationExtractorLegacyImpl.INSTANCE
|
||||||
|
: SequenceInformationExtractorH2DatabaseImpl.INSTANCE;
|
||||||
|
this.querySequenceString = "select * from INFORMATION_SCHEMA.SEQUENCES";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE;
|
||||||
|
this.querySequenceString = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
|
||||||
|
return DatabaseVersion.make( info.getMajor(), info.getMinor(), parseBuildId( info ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseBuildId(DialectResolutionInfo info) {
|
||||||
|
final String databaseVersion = info.getDatabaseVersion();
|
||||||
|
if ( databaseVersion == null ) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String[] bits = databaseVersion.split("[. ]");
|
||||||
|
return bits.length > 2 ? Integer.parseInt( bits[2] ) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getDefaultNonContextualLobCreation() {
|
||||||
|
// http://code.google.com/p/h2database/issues/detail?id=235
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsStandardArrays() {
|
||||||
|
return getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String columnType(int sqlTypeCode) {
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
// prior to version 2.0, H2 reported NUMERIC columns as DECIMAL,
|
||||||
|
// which caused problems for schema update tool
|
||||||
|
case NUMERIC:
|
||||||
|
return getVersion().isBefore( 2 ) ? columnType( DECIMAL ) : super.columnType( sqlTypeCode );
|
||||||
|
case NCHAR:
|
||||||
|
return columnType( CHAR );
|
||||||
|
case NVARCHAR:
|
||||||
|
return columnType( VARCHAR );
|
||||||
|
}
|
||||||
|
return super.columnType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String castType(int sqlTypeCode) {
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
case CHAR:
|
||||||
|
case NCHAR:
|
||||||
|
return "char";
|
||||||
|
case VARCHAR:
|
||||||
|
case NVARCHAR:
|
||||||
|
case LONG32VARCHAR:
|
||||||
|
case LONG32NVARCHAR:
|
||||||
|
return "varchar";
|
||||||
|
case BINARY:
|
||||||
|
case VARBINARY:
|
||||||
|
case LONG32VARBINARY:
|
||||||
|
return "varbinary";
|
||||||
|
}
|
||||||
|
return super.castType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||||
|
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||||
|
|
||||||
|
if ( getVersion().isBefore( 2 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( ARRAY, "array", this ) );
|
||||||
|
}
|
||||||
|
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) );
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||||
|
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
|
|
||||||
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
|
.getJdbcTypeRegistry();
|
||||||
|
|
||||||
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantJdbcType.INSTANCE );
|
||||||
|
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDefaultStatementBatchSize() {
|
||||||
|
return 15;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasOddDstBehavior() {
|
||||||
|
// H2 1.4.200 has a bug: https://github.com/h2database/h2database/issues/3184
|
||||||
|
return getVersion().isSameOrAfter( 1, 4, 200 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
|
super.initializeFunctionRegistry( queryEngine );
|
||||||
|
|
||||||
|
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||||
|
|
||||||
|
// H2 needs an actual argument type for aggregates like SUM, AVG, MIN, MAX to determine the result type
|
||||||
|
functionFactory.aggregates( this, SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||||
|
// 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.NO_PLAIN_PARAMETER );
|
||||||
|
|
||||||
|
functionFactory.pi();
|
||||||
|
functionFactory.cot();
|
||||||
|
functionFactory.radians();
|
||||||
|
functionFactory.degrees();
|
||||||
|
functionFactory.log10();
|
||||||
|
functionFactory.rand();
|
||||||
|
functionFactory.truncate();
|
||||||
|
functionFactory.soundex();
|
||||||
|
functionFactory.translate();
|
||||||
|
functionFactory.bitand();
|
||||||
|
functionFactory.bitor();
|
||||||
|
functionFactory.bitxor();
|
||||||
|
functionFactory.bitAndOr();
|
||||||
|
functionFactory.yearMonthDay();
|
||||||
|
functionFactory.hourMinuteSecond();
|
||||||
|
functionFactory.dayOfWeekMonthYear();
|
||||||
|
functionFactory.weekQuarter();
|
||||||
|
functionFactory.daynameMonthname();
|
||||||
|
if ( useLocalTime ) {
|
||||||
|
functionFactory.localtimeLocaltimestamp();
|
||||||
|
}
|
||||||
|
functionFactory.bitLength();
|
||||||
|
functionFactory.octetLength();
|
||||||
|
functionFactory.ascii();
|
||||||
|
functionFactory.octetLength();
|
||||||
|
functionFactory.space();
|
||||||
|
functionFactory.repeat();
|
||||||
|
functionFactory.chr_char();
|
||||||
|
functionFactory.instr();
|
||||||
|
functionFactory.substr();
|
||||||
|
//also natively supports ANSI-style substring()
|
||||||
|
functionFactory.position();
|
||||||
|
functionFactory.trim1();
|
||||||
|
functionFactory.concat_pipeOperator();
|
||||||
|
functionFactory.nowCurdateCurtime();
|
||||||
|
functionFactory.sysdate();
|
||||||
|
functionFactory.insert();
|
||||||
|
// functionFactory.everyAny(); //this would work too
|
||||||
|
functionFactory.everyAny_boolAndOr();
|
||||||
|
functionFactory.median();
|
||||||
|
functionFactory.stddevPopSamp();
|
||||||
|
functionFactory.varPopSamp();
|
||||||
|
if ( getVersion().isSame( 1, 4, 200 ) ) {
|
||||||
|
// See https://github.com/h2database/h2database/issues/2518
|
||||||
|
functionFactory.format_toChar();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
functionFactory.format_formatdatetime();
|
||||||
|
}
|
||||||
|
functionFactory.rownum();
|
||||||
|
if ( getVersion().isSameOrAfter( 1, 4, 200 ) ) {
|
||||||
|
functionFactory.windowFunctions();
|
||||||
|
if ( getVersion().isSameOrAfter( 2 ) ) {
|
||||||
|
functionFactory.listagg( null );
|
||||||
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
|
functionFactory.hypotheticalOrderedSetAggregates();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Use group_concat until 2.x as listagg was buggy
|
||||||
|
functionFactory.listagg_groupConcat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
functionFactory.listagg_groupConcat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void augmentPhysicalTableTypes(List<String> tableTypesList) {
|
||||||
|
if ( getVersion().isSameOrAfter( 2 ) ) {
|
||||||
|
tableTypesList.add( "BASE TABLE" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer resolveSqlTypeCode(String columnTypeName, TypeConfiguration typeConfiguration) {
|
||||||
|
switch ( columnTypeName ) {
|
||||||
|
case "FLOAT(24)":
|
||||||
|
// Use REAL instead of FLOAT to get Float as recommended Java type
|
||||||
|
return Types.REAL;
|
||||||
|
}
|
||||||
|
return super.resolveSqlTypeCode( columnTypeName, typeConfiguration );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveSqlTypeDescriptor(
|
||||||
|
String columnTypeName,
|
||||||
|
int jdbcTypeCode,
|
||||||
|
int precision,
|
||||||
|
int scale,
|
||||||
|
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
|
// As of H2 2.0 we get a FLOAT type code even though it is a DOUBLE
|
||||||
|
if ( jdbcTypeCode == FLOAT && "DOUBLE PRECISION".equals( columnTypeName ) ) {
|
||||||
|
return jdbcTypeRegistry.getDescriptor( DOUBLE );
|
||||||
|
}
|
||||||
|
return super.resolveSqlTypeDescriptor( columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer resolveSqlTypeCode(String typeName, String baseTypeName, TypeConfiguration typeConfiguration) {
|
||||||
|
switch ( baseTypeName ) {
|
||||||
|
case "CHARACTER VARYING":
|
||||||
|
return VARCHAR;
|
||||||
|
}
|
||||||
|
return super.resolveSqlTypeCode( typeName, baseTypeName, typeConfiguration );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxVarcharLength() {
|
||||||
|
return 1_048_576;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currentTime() {
|
||||||
|
return useLocalTime ? "localtime" : super.currentTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currentTimestamp() {
|
||||||
|
return useLocalTime ? "localtimestamp" : super.currentTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String currentTimestampWithTimeZone() {
|
||||||
|
return "current_timestamp";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
@Override
|
||||||
|
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||||
|
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
return new H2LegacySqlAstTranslator<>( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In H2, the extract() function does not return
|
||||||
|
* fractional seconds for the field
|
||||||
|
* {@link TemporalUnit#SECOND}. We work around
|
||||||
|
* this here with two calls to extract().
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String extractPattern(TemporalUnit unit) {
|
||||||
|
return unit == SECOND
|
||||||
|
? "(" + super.extractPattern(unit) + "+extract(nanosecond from ?2)/1e9)"
|
||||||
|
: super.extractPattern(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
|
if ( intervalType != null ) {
|
||||||
|
return "(?2+?3)";
|
||||||
|
}
|
||||||
|
return "dateadd(?1,?2,?3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
|
if ( unit == null ) {
|
||||||
|
return "(?3-?2)";
|
||||||
|
}
|
||||||
|
return "datediff(?1,?2,?3)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTemporalLiteralOffset() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeZoneSupport getTimeZoneSupport() {
|
||||||
|
return TimeZoneSupport.NATIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
|
||||||
|
appender.appendSql( bool );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitHandler getLimitHandler() {
|
||||||
|
return limitHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDistinctFromPredicate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsAfterTableName() {
|
||||||
|
return !supportsIfExistsBeforeTableName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsBeforeTableName() {
|
||||||
|
return cascadeConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsAfterAlterTable() {
|
||||||
|
return cascadeConstraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsBeforeConstraintName() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCascadeConstraintsString() {
|
||||||
|
return cascadeConstraints ? " cascade "
|
||||||
|
: super.getCascadeConstraintsString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCommentOn() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dropConstraints() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceSupport getSequenceSupport() {
|
||||||
|
return ansiSequence ? H2V2SequenceSupport.INSTANCE: H2V1SequenceSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuerySequencesString() {
|
||||||
|
return querySequenceString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceInformationExtractor getSequenceInformationExtractor() {
|
||||||
|
return sequenceInformationExtractor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NullOrdering getNullOrdering() {
|
||||||
|
return NullOrdering.SMALLEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
|
return new LocalTemporaryTableMutationStrategy(
|
||||||
|
TemporaryTable.createIdTable(
|
||||||
|
entityDescriptor,
|
||||||
|
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
|
||||||
|
this,
|
||||||
|
runtimeModelCreationContext
|
||||||
|
),
|
||||||
|
runtimeModelCreationContext.getSessionFactory()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
|
||||||
|
EntityMappingType entityDescriptor,
|
||||||
|
RuntimeModelCreationContext runtimeModelCreationContext) {
|
||||||
|
return new LocalTemporaryTableInsertStrategy(
|
||||||
|
TemporaryTable.createEntityTable(
|
||||||
|
entityDescriptor,
|
||||||
|
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
|
||||||
|
this,
|
||||||
|
runtimeModelCreationContext
|
||||||
|
),
|
||||||
|
runtimeModelCreationContext.getSessionFactory()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TemporaryTableKind getSupportedTemporaryTableKind() {
|
||||||
|
return TemporaryTableKind.LOCAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeforeUseAction getTemporaryTableBeforeUseAction() {
|
||||||
|
return BeforeUseAction.CREATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
|
||||||
|
return EXTRACTOR;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final ViolatedConstraintNameExtractor EXTRACTOR =
|
||||||
|
new TemplatedViolatedConstraintNameExtractor( sqle -> {
|
||||||
|
// 23000: Check constraint violation: {0}
|
||||||
|
// 23001: Unique index or primary key violation: {0}
|
||||||
|
if ( sqle.getSQLState().startsWith( "23" ) ) {
|
||||||
|
final String message = sqle.getMessage();
|
||||||
|
final int idx = message.indexOf( "violation: " );
|
||||||
|
if ( idx > 0 ) {
|
||||||
|
String constraintName = message.substring( idx + "violation: ".length() );
|
||||||
|
if ( sqle.getSQLState().equals( "23506" ) ) {
|
||||||
|
constraintName = constraintName.substring( 1, constraintName.indexOf( ":" ) );
|
||||||
|
}
|
||||||
|
return constraintName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
} );
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||||
|
return (sqlException, message, sql) -> {
|
||||||
|
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||||
|
|
||||||
|
switch (errorCode) {
|
||||||
|
case 40001:
|
||||||
|
// DEADLOCK DETECTED
|
||||||
|
return new LockAcquisitionException(message, sqlException, sql);
|
||||||
|
case 50200:
|
||||||
|
// LOCK NOT AVAILABLE
|
||||||
|
return new PessimisticLockException(message, sqlException, sql);
|
||||||
|
case 90006:
|
||||||
|
// NULL not allowed for column [90006-145]
|
||||||
|
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
|
||||||
|
return new ConstraintViolationException(message, sqlException, sql, constraintName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCurrentTimestampSelection() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCurrentTimestampSelectStringCallable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCurrentTimestampSelectString() {
|
||||||
|
return "call current_timestamp()";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLobValueChangePropagation() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTupleCounts() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresParensForTupleDistinctCounts() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean doesReadCommittedCauseWritersToBlockReaders() {
|
||||||
|
// see http://groups.google.com/group/h2-database/browse_thread/thread/562d8a49e2dabe99?hl=en
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
|
||||||
|
return SelectItemReferenceStrategy.ALIAS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsOffsetInSubquery() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsWindowFunctions() {
|
||||||
|
return getVersion().isSameOrAfter( 1, 4, 200 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsFetchClause(FetchClauseType type) {
|
||||||
|
return getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
|
return new H2IdentityColumnSupport();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQueryHintString(String query, String hints) {
|
||||||
|
return IndexQueryHintHandler.INSTANCE.addQueryHints( query, hints );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||||
|
if ( getVersion().isSame( 1, 4, 200 ) ) {
|
||||||
|
// See https://github.com/h2database/h2database/issues/2518
|
||||||
|
appender.appendSql( OracleDialect.datetimeFormat( format, true, true ).result() );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
appender.appendSql(
|
||||||
|
new Replacer( format, "'", "''" )
|
||||||
|
.replace("e", "u")
|
||||||
|
.replace( "xxx", "XXX" )
|
||||||
|
.replace( "xx", "XX" )
|
||||||
|
.replace( "x", "X" )
|
||||||
|
.result()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String translateExtractField(TemporalUnit unit) {
|
||||||
|
switch ( unit ) {
|
||||||
|
case DAY_OF_MONTH: return "day";
|
||||||
|
case WEEK: return "iso_week";
|
||||||
|
default: return unit.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String generatedAs(String generatedAs) {
|
||||||
|
return " generated always as (" + generatedAs + ")";
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
/*
|
||||||
|
* 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 org.hibernate.LockMode;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||||
|
import org.hibernate.query.sqm.ComparisonOperator;
|
||||||
|
import org.hibernate.sql.ast.Clause;
|
||||||
|
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.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.BinaryArithmeticExpression;
|
||||||
|
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.SqlTupleContainer;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
import org.hibernate.sql.ast.tree.from.QueryPartTableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||||
|
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.predicate.InSubQueryPredicate;
|
||||||
|
import org.hibernate.sql.ast.tree.select.QueryPart;
|
||||||
|
import org.hibernate.sql.ast.tree.select.SelectClause;
|
||||||
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A legacy SQL AST translator for H2.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||||
|
|
||||||
|
private boolean renderAsArray;
|
||||||
|
|
||||||
|
public H2LegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
super( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
|
expression.accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( "not(" );
|
||||||
|
}
|
||||||
|
booleanExpressionPredicate.getExpression().accept( this );
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
|
if ( isRowsOnlyFetchClauseType( queryPart ) ) {
|
||||||
|
if ( supportsOffsetFetchClause() ) {
|
||||||
|
renderOffsetFetchClause( queryPart, true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
renderLimitOffsetClause( queryPart );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ( supportsOffsetFetchClausePercentWithTies() ) {
|
||||||
|
renderOffsetFetchClause( queryPart, true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// FETCH PERCENT and WITH TIES were introduced along with window functions
|
||||||
|
throw new IllegalArgumentException( "Can't emulate fetch clause type: " + queryPart.getFetchClauseType() );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderSearchClause(CteStatement cte) {
|
||||||
|
// H2 does not support this, but it's just a hint anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderCycleClause(CteStatement cte) {
|
||||||
|
// H2 does not support this, but it can be emulated
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderSelectTupleComparison(
|
||||||
|
List<SqlSelection> lhsExpressions,
|
||||||
|
SqlTuple tuple,
|
||||||
|
ComparisonOperator operator) {
|
||||||
|
emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitInSubQueryPredicate(InSubQueryPredicate inSubQueryPredicate) {
|
||||||
|
final SqlTuple lhsTuple;
|
||||||
|
// As of 1.4.200 this is supported
|
||||||
|
if ( getDialect().getVersion().isBefore( 1, 4, 200 )
|
||||||
|
&& ( lhsTuple = SqlTupleContainer.getSqlTuple( inSubQueryPredicate.getTestExpression() ) ) != null
|
||||||
|
&& lhsTuple.getExpressions().size() != 1 ) {
|
||||||
|
inSubQueryPredicate.getTestExpression().accept( this );
|
||||||
|
if ( inSubQueryPredicate.isNegated() ) {
|
||||||
|
appendSql( " not" );
|
||||||
|
}
|
||||||
|
appendSql( " in" );
|
||||||
|
final boolean renderAsArray = this.renderAsArray;
|
||||||
|
this.renderAsArray = true;
|
||||||
|
inSubQueryPredicate.getSubQuery().accept( this );
|
||||||
|
this.renderAsArray = renderAsArray;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitInSubQueryPredicate( inSubQueryPredicate );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void visitSqlSelections(SelectClause selectClause) {
|
||||||
|
final boolean renderAsArray = this.renderAsArray;
|
||||||
|
this.renderAsArray = false;
|
||||||
|
if ( renderAsArray ) {
|
||||||
|
append( OPEN_PARENTHESIS );
|
||||||
|
}
|
||||||
|
super.visitSqlSelections( selectClause );
|
||||||
|
if ( renderAsArray ) {
|
||||||
|
append( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderPartitionItem(Expression expression) {
|
||||||
|
if ( expression instanceof Literal ) {
|
||||||
|
appendSql( "'0' || '0'" );
|
||||||
|
}
|
||||||
|
else if ( expression instanceof Summarization ) {
|
||||||
|
// This could theoretically be emulated by rendering all grouping variations of the query and
|
||||||
|
// connect them via union all but that's probably pretty inefficient and would have to happen
|
||||||
|
// on the query spec level
|
||||||
|
throw new UnsupportedOperationException( "Summarization is not supported by DBMS!" );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expression.accept( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBinaryArithmeticExpression(BinaryArithmeticExpression arithmeticExpression) {
|
||||||
|
appendSql( OPEN_PARENTHESIS );
|
||||||
|
render( arithmeticExpression.getLeftHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||||
|
appendSql( arithmeticExpression.getOperator().getOperatorSqlTextString() );
|
||||||
|
render( arithmeticExpression.getRightHandOperand(), SqlAstNodeRenderingMode.NO_PLAIN_PARAMETER );
|
||||||
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||||
|
final TableReference tableRef = tableGroup.getPrimaryTableReference();
|
||||||
|
// The H2 parser can't handle a sub-query as first element in a nested join
|
||||||
|
// i.e. `join ( (select ...) alias join ... )`, so we have to introduce a dummy table reference
|
||||||
|
if ( tableRef instanceof QueryPartTableReference || tableRef.getTableId().startsWith( "(select" ) ) {
|
||||||
|
final boolean realTableGroup = tableGroup.isRealTableGroup()
|
||||||
|
&& ( CollectionHelper.isNotEmpty( tableGroup.getTableReferenceJoins() )
|
||||||
|
|| hasNestedTableGroupsToRender( tableGroup.getNestedTableGroupJoins() ) );
|
||||||
|
if ( realTableGroup ) {
|
||||||
|
appendSql( "dual cross join " );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.renderPrimaryTableReference( tableGroup, lockMode );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntax() {
|
||||||
|
// Just a guess
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||||
|
// Just a guess
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||||
|
// Just a guess
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean supportsNullPrecedence() {
|
||||||
|
// Support for nulls clause in listagg was added in 2.0
|
||||||
|
return getClauseStack().getCurrent() != Clause.WITHIN_GROUP || getDialect().getVersion().isSameOrAfter( 2 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getFromDual() {
|
||||||
|
return " from dual";
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean supportsOffsetFetchClause() {
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 1, 4, 195 );
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean supportsOffsetFetchClausePercentWithTies() {
|
||||||
|
// Introduction of TIES clause https://github.com/h2database/h2database/commit/876e9fbe7baf11d01675bfe871aac2cf1b6104ce
|
||||||
|
// Introduction of PERCENT support https://github.com/h2database/h2database/commit/f45913302e5f6ad149155a73763c0c59d8205849
|
||||||
|
return getDialect().getVersion().isSameOrAfter( 1, 4, 198 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -96,8 +96,7 @@ import static org.hibernate.type.SqlTypes.TIMESTAMP_UTC;
|
||||||
*/
|
*/
|
||||||
public class H2Dialect extends Dialect {
|
public class H2Dialect extends Dialect {
|
||||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( H2Dialect.class );
|
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( H2Dialect.class );
|
||||||
|
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 1, 4, 197 );
|
||||||
private final LimitHandler limitHandler;
|
|
||||||
|
|
||||||
private final boolean ansiSequence;
|
private final boolean ansiSequence;
|
||||||
private final boolean cascadeConstraints;
|
private final boolean cascadeConstraints;
|
||||||
|
@ -112,21 +111,12 @@ public class H2Dialect extends Dialect {
|
||||||
}
|
}
|
||||||
|
|
||||||
public H2Dialect() {
|
public H2Dialect() {
|
||||||
this( SimpleDatabaseVersion.ZERO_VERSION );
|
this( MINIMUM_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
public H2Dialect(DatabaseVersion version) {
|
public H2Dialect(DatabaseVersion version) {
|
||||||
super(version);
|
super(version);
|
||||||
|
|
||||||
// https://github.com/h2database/h2database/commit/b2cdf84e0b84eb8a482fa7dccdccc1ab95241440
|
|
||||||
limitHandler = version.isSameOrAfter( 1, 4, 195 )
|
|
||||||
? OffsetFetchLimitHandler.INSTANCE
|
|
||||||
: LimitOffsetLimitHandler.INSTANCE;
|
|
||||||
|
|
||||||
if ( version.isBefore( 1, 2, 139 ) ) {
|
|
||||||
LOG.unsupportedMultiTableBulkHqlJpaql( version.getMajor(), version.getMinor(), version.getMicro() );
|
|
||||||
}
|
|
||||||
|
|
||||||
// supportsTuplesInSubqueries = version.isSameOrAfter( 1, 4, 198 );
|
// supportsTuplesInSubqueries = version.isSameOrAfter( 1, 4, 198 );
|
||||||
|
|
||||||
// Prior to 1.4.200 there was no support for 'current value for sequence_name'
|
// Prior to 1.4.200 there was no support for 'current value for sequence_name'
|
||||||
|
@ -138,17 +128,11 @@ public class H2Dialect extends Dialect {
|
||||||
// 1.4.200 introduced changes in current_time and current_timestamp
|
// 1.4.200 introduced changes in current_time and current_timestamp
|
||||||
useLocalTime = version.isSameOrAfter( 1, 4, 200 );
|
useLocalTime = version.isSameOrAfter( 1, 4, 200 );
|
||||||
|
|
||||||
if ( version.isSameOrAfter( 1, 4, 32 ) ) {
|
|
||||||
this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 )
|
this.sequenceInformationExtractor = version.isSameOrAfter( 1, 4, 201 )
|
||||||
? SequenceInformationExtractorLegacyImpl.INSTANCE
|
? SequenceInformationExtractorLegacyImpl.INSTANCE
|
||||||
: SequenceInformationExtractorH2DatabaseImpl.INSTANCE;
|
: SequenceInformationExtractorH2DatabaseImpl.INSTANCE;
|
||||||
this.querySequenceString = "select * from INFORMATION_SCHEMA.SEQUENCES";
|
this.querySequenceString = "select * from INFORMATION_SCHEMA.SEQUENCES";
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
this.sequenceInformationExtractor = SequenceInformationExtractorNoOpImpl.INSTANCE;
|
|
||||||
this.querySequenceString = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
|
private static DatabaseVersion parseVersion(DialectResolutionInfo info) {
|
||||||
return DatabaseVersion.make( info.getMajor(), info.getMinor(), parseBuildId( info ) );
|
return DatabaseVersion.make( info.getMajor(), info.getMinor(), parseBuildId( info ) );
|
||||||
|
@ -217,14 +201,12 @@ public class H2Dialect extends Dialect {
|
||||||
if ( getVersion().isBefore( 2 ) ) {
|
if ( getVersion().isBefore( 2 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( ARRAY, "array", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( ARRAY, "array", this ) );
|
||||||
}
|
}
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INTERVAL_SECOND, "interval second($p,$s)", this ) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
@ -234,9 +216,7 @@ public class H2Dialect extends Dialect {
|
||||||
.getJdbcTypeRegistry();
|
.getJdbcTypeRegistry();
|
||||||
|
|
||||||
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantJdbcType.INSTANCE );
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
}
|
|
||||||
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
if ( getVersion().isSameOrAfter( 1, 4, 198 ) ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( H2DurationIntervalSecondJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
|
@ -447,7 +427,7 @@ public class H2Dialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LimitHandler getLimitHandler() {
|
public LimitHandler getLimitHandler() {
|
||||||
return limitHandler;
|
return OffsetFetchLimitHandler.INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -182,19 +182,19 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
|
||||||
@Override
|
@Override
|
||||||
protected boolean supportsRowValueConstructorSyntax() {
|
protected boolean supportsRowValueConstructorSyntax() {
|
||||||
// Just a guess
|
// Just a guess
|
||||||
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||||
// Just a guess
|
// Just a guess
|
||||||
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||||
// Just a guess
|
// Just a guess
|
||||||
return getDialect().getVersion().isSameOrAfter( 1, 4, 197 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -209,7 +209,7 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstT
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean supportsOffsetFetchClause() {
|
private boolean supportsOffsetFetchClause() {
|
||||||
return getDialect().getVersion().isSameOrAfter( 1, 4, 195 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean supportsOffsetFetchClausePercentWithTies() {
|
private boolean supportsOffsetFetchClausePercentWithTies() {
|
||||||
|
|
Loading…
Reference in New Issue