Skip to content

HHH-18589 Properly handle temporals in BC era #10705

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.spi.ColumnTypeInformation;
import org.hibernate.type.JavaObjectType;
import org.hibernate.type.descriptor.DateTimeUtils;
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
Expand Down Expand Up @@ -129,11 +130,10 @@
import static org.hibernate.type.SqlTypes.UUID;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDateWithEraSuffix;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicrosAndEraSuffix;

/**
* A {@linkplain Dialect SQL dialect} for CockroachDB.
Expand Down Expand Up @@ -742,7 +742,7 @@ public void appendDateTimeLiteral(
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDate( appender, temporalAccessor );
appendAsDateWithEraSuffix( appender, temporalAccessor );
appender.appendSql( '\'' );
break;
case TIME:
Expand All @@ -759,12 +759,12 @@ public void appendDateTimeLiteral(
case TIMESTAMP:
if ( supportsTemporalLiteralOffset() && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
appender.appendSql( "timestamp with time zone '" );
appendAsTimestampWithMicros( appender, temporalAccessor, true, jdbcTimeZone );
appendAsTimestampWithMicrosAndEraSuffix( appender, temporalAccessor, true, jdbcTimeZone );
appender.appendSql( '\'' );
}
else {
appender.appendSql( "timestamp '" );
appendAsTimestampWithMicros( appender, temporalAccessor, false, jdbcTimeZone );
appendAsTimestampWithMicrosAndEraSuffix( appender, temporalAccessor, false, jdbcTimeZone );
appender.appendSql( '\'' );
}
break;
Expand All @@ -778,7 +778,7 @@ public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDate( appender, date );
appendAsDateWithEraSuffix( appender, date );
appender.appendSql( '\'' );
break;
case TIME:
Expand All @@ -788,7 +788,7 @@ public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType
break;
case TIMESTAMP:
appender.appendSql( "timestamp with time zone '" );
appendAsTimestampWithMicros( appender,date, jdbcTimeZone );
appendAsTimestampWithMicrosAndEraSuffix( appender,date, jdbcTimeZone );
appender.appendSql( '\'' );
break;
default:
Expand All @@ -805,7 +805,7 @@ public void appendDateTimeLiteral(
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDate( appender, calendar );
appendAsDateWithEraSuffix( appender, calendar );
appender.appendSql( '\'' );
break;
case TIME:
Expand All @@ -815,7 +815,7 @@ public void appendDateTimeLiteral(
break;
case TIMESTAMP:
appender.appendSql( "timestamp with time zone '" );
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
DateTimeUtils.appendAsTimestampWithMillisAndEraSuffix( appender, calendar, jdbcTimeZone );
appender.appendSql( '\'' );
break;
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorNoOpImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.descriptor.jdbc.EnumJdbcType;
import org.hibernate.type.descriptor.jdbc.GregorianEpochBasedDateJdbcType;
import org.hibernate.type.descriptor.jdbc.GregorianEpochBasedTimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.OrdinalEnumJdbcType;
import org.hibernate.type.descriptor.jdbc.TimeAsTimestampWithTimeZoneJdbcType;
Expand Down Expand Up @@ -294,6 +296,8 @@ public void contributeTypes(TypeContributions typeContributions, ServiceRegistry
jdbcTypeRegistry.addDescriptor( TimeUtcAsOffsetTimeJdbcType.INSTANCE );
}
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, TimestampUtcAsInstantJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( GregorianEpochBasedDateJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( GregorianEpochBasedTimestampJdbcType.INSTANCE );
if ( getVersion().isSameOrAfter( 1, 4, 197 ) ) {
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import org.hibernate.Locking;
import org.hibernate.StaleObjectStateException;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.community.dialect.pagination.LegacyHSQLLimitHandler;
import org.hibernate.dialect.BooleanDecoder;
import org.hibernate.dialect.DatabaseVersion;
Expand Down Expand Up @@ -67,6 +68,7 @@
import org.hibernate.query.sqm.mutation.spi.BeforeUseAction;
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;
Expand All @@ -76,20 +78,31 @@
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.internal.SequenceInformationExtractorHSQLDBDatabaseImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.descriptor.DateTimeUtils;
import org.hibernate.type.descriptor.jdbc.GregorianEpochBasedDateJdbcType;
import org.hibernate.type.descriptor.jdbc.GregorianEpochBasedTimestampJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import org.jboss.logging.Logger;

import java.lang.invoke.MethodHandles;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.sql.Types;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;

import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.CLOB;
import static org.hibernate.type.SqlTypes.DOUBLE;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NUMERIC;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDateWithEraPrefix;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillisAndEraPrefix;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanosAndEraPrefix;

/**
* A {@linkplain Dialect SQL dialect} for HSQLDB (HyperSQL) 1.8 up to (but not including) 2.6.1.
Expand Down Expand Up @@ -294,6 +307,85 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio
) );
}

@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
final JdbcTypeRegistry jdbcTypeRegistry =
typeContributions.getTypeConfiguration().getJdbcTypeRegistry();

jdbcTypeRegistry.addDescriptor( GregorianEpochBasedDateJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( GregorianEpochBasedTimestampJdbcType.INSTANCE );
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, TemporalAccessor temporalAccessor, TemporalType precision, TimeZone jdbcTimeZone) {
if ( precision != TemporalType.TIME && DateTimeUtils.isBcEra( temporalAccessor ) ) {
switch ( precision ) {
case DATE:
appender.appendSql( "to_date('" );
appendAsDateWithEraPrefix( appender, temporalAccessor );
appender.appendSql( "','BC YYYY-MM-DD')" );
break;
case TIMESTAMP:
appender.appendSql( "to_timestamp('" );
appendAsTimestampWithNanosAndEraPrefix( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
appender.appendSql( "','BC YYYY-MM-DD HH:MI:SS.FF')" );
break;
default:
throw new IllegalArgumentException();
}
}
else {
super.appendDateTimeLiteral( appender, temporalAccessor, precision, jdbcTimeZone );
}
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
if ( precision != TemporalType.TIME && DateTimeUtils.isBcEra( date ) ) {
switch ( precision ) {
case DATE:
appender.appendSql( "to_date('" );
appendAsDateWithEraPrefix( appender, date );
appender.appendSql( "','BC YYYY-MM-DD')" );
break;
case TIMESTAMP:
appender.appendSql( "to_timestamp('" );
appendAsTimestampWithNanosAndEraPrefix( appender, date, jdbcTimeZone );
appender.appendSql( "','BC YYYY-MM-DD HH:MI:SS.FF')" );
break;
default:
throw new IllegalArgumentException();
}
}
else {
super.appendDateTimeLiteral( appender, date, precision, jdbcTimeZone );
}
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, Calendar calendar, TemporalType precision, TimeZone jdbcTimeZone) {
if ( precision != TemporalType.TIME && DateTimeUtils.isBcEra( calendar ) ) {
switch ( precision ) {
case DATE:
appender.appendSql( "to_date('" );
appendAsDateWithEraPrefix( appender, calendar );
appender.appendSql( "','BC YYYY-MM-DD')" );
break;
case TIMESTAMP:
appender.appendSql( "to_timestamp('" );
appendAsTimestampWithMillisAndEraPrefix( appender, calendar, jdbcTimeZone );
appender.appendSql( "','BC YYYY-MM-DD HH:MI:SS.FF')" );
break;
default:
throw new IllegalArgumentException();
}
}
else {
super.appendDateTimeLiteral( appender, calendar, precision, jdbcTimeZone );
}
}

/**
* HSQLDB doesn't support the {@code generate_series} function or {@code lateral} recursive CTEs,
* so it has to be emulated with a top level recursive CTE which requires an upper bound on the amount
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.temporal.ChronoField;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
Expand Down Expand Up @@ -158,7 +159,11 @@
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanos;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDateWithoutYear0;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillisWithoutYear0;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithNanosWithoutYear0;

/**
* A {@linkplain Dialect SQL dialect} for Oracle 8i and above.
Expand Down Expand Up @@ -1469,27 +1474,80 @@ public String getReadLockString(String aliases, int timeout) {

@Override
public boolean supportsTemporalLiteralOffset() {
// Oracle *does* support offsets, but only
// in the ANSI syntax, not in the JDBC
// escape-based syntax, which we use in
// almost all circumstances (see below)
return false;
return true;
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, TemporalAccessor temporalAccessor, TemporalType precision, TimeZone jdbcTimeZone) {
// we usually use the JDBC escape-based syntax
// because we want to let the JDBC driver handle
// TIME (a concept which does not exist in Oracle)
// but for the special case of timestamps with an
// offset we need to use the ANSI syntax
if ( precision == TemporalType.TIMESTAMP && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
appender.appendSql( "timestamp '" );
appendAsTimestampWithNanos( appender, temporalAccessor, true, jdbcTimeZone, false );
appender.appendSql( '\'' );
public void appendDateTimeLiteral(
SqlAppender appender,
TemporalAccessor temporalAccessor,
@SuppressWarnings("deprecation")
TemporalType precision,
TimeZone jdbcTimeZone) {
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDateWithoutYear0( appender, temporalAccessor );
appender.appendSql( '\'' );
break;
case TIME:
appender.appendSql( "time '" );
appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
appender.appendSql( "timestamp '" );
appendAsTimestampWithNanosWithoutYear0( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone, false );
appender.appendSql( '\'' );
break;
default:
throw new IllegalArgumentException();
}
else {
super.appendDateTimeLiteral( appender, temporalAccessor, precision, jdbcTimeZone );
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDateWithoutYear0( appender, date );
appender.appendSql( '\'' );
break;
case TIME:
appender.appendSql( "time '" );
appendAsLocalTime( appender, date );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
appender.appendSql( "timestamp '" );
appendAsTimestampWithNanosWithoutYear0( appender, date, jdbcTimeZone );
appender.appendSql( '\'' );
break;
default:
throw new IllegalArgumentException();
}
}

@Override
public void appendDateTimeLiteral(SqlAppender appender, Calendar calendar, TemporalType precision, TimeZone jdbcTimeZone) {
switch ( precision ) {
case DATE:
appender.appendSql( "date '" );
appendAsDateWithoutYear0( appender, calendar );
appender.appendSql( '\'' );
break;
case TIME:
appender.appendSql( "time '" );
appendAsLocalTime( appender, calendar );
appender.appendSql( '\'' );
break;
case TIMESTAMP:
appender.appendSql( "timestamp '" );
appendAsTimestampWithMillisWithoutYear0( appender, calendar, jdbcTimeZone );
appender.appendSql( '\'' );
break;
default:
throw new IllegalArgumentException();
}
}

Expand Down
Loading
Loading