HHH-15195 - Moving support for CockroachDB versions older than 21.1 into community dialects
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
f33d3ed308
commit
75f4c95274
|
@ -0,0 +1,815 @@
|
||||||
|
/*
|
||||||
|
* 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.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.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import jakarta.persistence.TemporalType;
|
||||||
|
|
||||||
|
import org.hibernate.LockMode;
|
||||||
|
import org.hibernate.LockOptions;
|
||||||
|
import org.hibernate.boot.model.TypeContributions;
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
|
import org.hibernate.dialect.Dialect;
|
||||||
|
import org.hibernate.dialect.NationalizationSupport;
|
||||||
|
import org.hibernate.dialect.PostgreSQLDriverKind;
|
||||||
|
import org.hibernate.dialect.PostgreSQLInetJdbcType;
|
||||||
|
import org.hibernate.dialect.PostgreSQLIntervalSecondJdbcType;
|
||||||
|
import org.hibernate.dialect.PostgreSQLJsonJdbcType;
|
||||||
|
import org.hibernate.dialect.PostgreSQLJsonbJdbcType;
|
||||||
|
import org.hibernate.dialect.PostgreSQLPGObjectJdbcType;
|
||||||
|
import org.hibernate.dialect.RowLockStrategy;
|
||||||
|
import org.hibernate.dialect.SpannerDialect;
|
||||||
|
import org.hibernate.dialect.TimeZoneSupport;
|
||||||
|
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||||
|
import org.hibernate.dialect.function.FormatFunction;
|
||||||
|
import org.hibernate.dialect.identity.CockroachDBIdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||||
|
import org.hibernate.dialect.pagination.LimitHandler;
|
||||||
|
import org.hibernate.dialect.pagination.OffsetFetchLimitHandler;
|
||||||
|
import org.hibernate.dialect.sequence.PostgreSQLSequenceSupport;
|
||||||
|
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||||
|
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
|
||||||
|
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
|
||||||
|
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.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.NullOrdering;
|
||||||
|
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.service.ServiceRegistry;
|
||||||
|
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.type.JavaObjectType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.UUIDJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.VarbinaryJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.VarcharJdbcType;
|
||||||
|
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||||
|
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||||
|
import org.hibernate.type.descriptor.sql.internal.Scale6IntervalSecondDdlType;
|
||||||
|
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||||
|
|
||||||
|
import static org.hibernate.query.sqm.TemporalUnit.DAY;
|
||||||
|
import static org.hibernate.query.sqm.TemporalUnit.NATIVE;
|
||||||
|
import static org.hibernate.type.SqlTypes.*;
|
||||||
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||||
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||||
|
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@linkplain Dialect SQL dialect} for CockroachDB.
|
||||||
|
*
|
||||||
|
* @author Gavin King
|
||||||
|
*/
|
||||||
|
public class CockroachLegacyDialect extends Dialect {
|
||||||
|
|
||||||
|
private static final CockroachDBIdentityColumnSupport IDENTITY_COLUMN_SUPPORT = new CockroachDBIdentityColumnSupport();
|
||||||
|
// KNOWN LIMITATIONS:
|
||||||
|
// * no support for java.sql.Clob
|
||||||
|
|
||||||
|
private final PostgreSQLDriverKind driverKind;
|
||||||
|
|
||||||
|
public CockroachLegacyDialect() {
|
||||||
|
this( DatabaseVersion.make( 19, 2 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
public CockroachLegacyDialect(DialectResolutionInfo info) {
|
||||||
|
super(info);
|
||||||
|
driverKind = PostgreSQLDriverKind.determineKind( info );
|
||||||
|
}
|
||||||
|
|
||||||
|
public CockroachLegacyDialect(DatabaseVersion version) {
|
||||||
|
super(version);
|
||||||
|
driverKind = PostgreSQLDriverKind.PG_JDBC;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CockroachLegacyDialect(DatabaseVersion version, PostgreSQLDriverKind driverKind) {
|
||||||
|
super(version);
|
||||||
|
this.driverKind = driverKind;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String columnType(int sqlTypeCode) {
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
case TINYINT:
|
||||||
|
return "smallint"; //no tinyint
|
||||||
|
|
||||||
|
case CHAR:
|
||||||
|
case NCHAR:
|
||||||
|
case VARCHAR:
|
||||||
|
case NVARCHAR:
|
||||||
|
return "string($l)";
|
||||||
|
|
||||||
|
case NCLOB:
|
||||||
|
case CLOB:
|
||||||
|
return "string";
|
||||||
|
|
||||||
|
case BINARY:
|
||||||
|
case VARBINARY:
|
||||||
|
case BLOB:
|
||||||
|
return "bytes";
|
||||||
|
|
||||||
|
case TIMESTAMP_UTC:
|
||||||
|
return columnType( TIMESTAMP_WITH_TIMEZONE );
|
||||||
|
}
|
||||||
|
return super.columnType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String castType(int sqlTypeCode) {
|
||||||
|
switch ( sqlTypeCode ) {
|
||||||
|
case CHAR:
|
||||||
|
case NCHAR:
|
||||||
|
case VARCHAR:
|
||||||
|
case NVARCHAR:
|
||||||
|
case LONG32VARCHAR:
|
||||||
|
case LONG32NVARCHAR:
|
||||||
|
return "string";
|
||||||
|
case BINARY:
|
||||||
|
case VARBINARY:
|
||||||
|
case LONG32VARBINARY:
|
||||||
|
return "bytes";
|
||||||
|
}
|
||||||
|
return super.castType( sqlTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||||
|
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||||
|
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( UUID, "uuid", this ) );
|
||||||
|
if ( PostgreSQLPGObjectJdbcType.isUsable() ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||||
|
ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) );
|
||||||
|
|
||||||
|
// Prefer jsonb if possible
|
||||||
|
if ( getVersion().isSameOrAfter( 20 ) ) {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public JdbcType resolveSqlTypeDescriptor(
|
||||||
|
String columnTypeName,
|
||||||
|
int jdbcTypeCode,
|
||||||
|
int precision,
|
||||||
|
int scale,
|
||||||
|
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||||
|
if ( jdbcTypeCode == OTHER ) {
|
||||||
|
switch ( columnTypeName ) {
|
||||||
|
case "uuid":
|
||||||
|
jdbcTypeCode = UUID;
|
||||||
|
break;
|
||||||
|
case "json":
|
||||||
|
case "jsonb":
|
||||||
|
jdbcTypeCode = JSON;
|
||||||
|
break;
|
||||||
|
case "inet":
|
||||||
|
jdbcTypeCode = INET;
|
||||||
|
break;
|
||||||
|
case "geometry":
|
||||||
|
jdbcTypeCode = GEOMETRY;
|
||||||
|
break;
|
||||||
|
case "geography":
|
||||||
|
jdbcTypeCode = GEOGRAPHY;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return jdbcTypeRegistry.getDescriptor( jdbcTypeCode );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||||
|
super.contributeTypes( typeContributions, serviceRegistry );
|
||||||
|
|
||||||
|
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||||
|
.getJdbcTypeRegistry();
|
||||||
|
jdbcTypeRegistry.addDescriptor( TIMESTAMP_UTC, InstantAsTimestampWithTimeZoneJdbcType.INSTANCE );
|
||||||
|
if ( driverKind == PostgreSQLDriverKind.PG_JDBC ) {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
|
if ( PostgreSQLPGObjectJdbcType.isUsable() ) {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLIntervalSecondJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLInetJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonbJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonJdbcType.INSTANCE );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force Blob binding to byte[] for CockroachDB
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.BLOB, VarbinaryJdbcType.INSTANCE );
|
||||||
|
jdbcTypeRegistry.addDescriptor( Types.CLOB, VarcharJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
// The next two contributions are the same as for Postgresql
|
||||||
|
typeContributions.contributeJdbcType( ObjectNullAsBinaryTypeJdbcType.INSTANCE );
|
||||||
|
|
||||||
|
// Until we remove StandardBasicTypes, we have to keep this
|
||||||
|
typeContributions.contributeType(
|
||||||
|
new JavaObjectType(
|
||||||
|
ObjectNullAsBinaryTypeJdbcType.INSTANCE,
|
||||||
|
typeContributions.getTypeConfiguration()
|
||||||
|
.getJavaTypeRegistry()
|
||||||
|
.getDescriptor( Object.class )
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||||
|
super.initializeFunctionRegistry(queryEngine);
|
||||||
|
|
||||||
|
final CommonFunctionFactory functionFactory = new CommonFunctionFactory( queryEngine );
|
||||||
|
functionFactory.ascii();
|
||||||
|
functionFactory.char_chr();
|
||||||
|
functionFactory.overlay();
|
||||||
|
functionFactory.position();
|
||||||
|
functionFactory.substringFromFor();
|
||||||
|
functionFactory.locate_positionSubstring();
|
||||||
|
functionFactory.trim2();
|
||||||
|
functionFactory.substr();
|
||||||
|
functionFactory.reverse();
|
||||||
|
functionFactory.repeat();
|
||||||
|
functionFactory.md5();
|
||||||
|
functionFactory.sha1();
|
||||||
|
functionFactory.octetLength();
|
||||||
|
functionFactory.bitLength();
|
||||||
|
functionFactory.cbrt();
|
||||||
|
functionFactory.cot();
|
||||||
|
functionFactory.degrees();
|
||||||
|
functionFactory.radians();
|
||||||
|
functionFactory.pi();
|
||||||
|
functionFactory.trunc(); //TODO: emulate second arg
|
||||||
|
|
||||||
|
queryEngine.getSqmFunctionRegistry().register(
|
||||||
|
"format",
|
||||||
|
new FormatFunction( "experimental_strftime", queryEngine.getTypeConfiguration() )
|
||||||
|
);
|
||||||
|
functionFactory.windowFunctions();
|
||||||
|
functionFactory.listagg_stringAgg( "string" );
|
||||||
|
functionFactory.inverseDistributionOrderedSetAggregates();
|
||||||
|
functionFactory.hypotheticalOrderedSetAggregates();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimeZoneSupport getTimeZoneSupport() {
|
||||||
|
return TimeZoneSupport.NORMALIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
|
||||||
|
appender.appendSql( bool );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCascadeConstraintsString() {
|
||||||
|
return " cascade";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsDistinctFromPredicate() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsBeforeTableName() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsBeforeConstraintName() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsIfExistsAfterAlterTable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean qualifyIndexName() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||||
|
return IDENTITY_COLUMN_SUPPORT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsValuesList() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsPartitionBy() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNonQueryWithCTE() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNoColumnsInsertString() {
|
||||||
|
return "default values";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getCaseInsensitiveLike(){
|
||||||
|
return "ilike";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsCaseInsensitiveLike() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNullPrecedence() {
|
||||||
|
// Not yet implemented: https://www.cockroachlabs.com/docs/v20.2/null-handling.html#nulls-and-sorting
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NullOrdering getNullOrdering() {
|
||||||
|
return NullOrdering.SMALLEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsTupleCounts() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean requiresParensForTupleDistinctCounts() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getNativeIdentifierGeneratorStrategy() {
|
||||||
|
return "sequence";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SequenceSupport getSequenceSupport() {
|
||||||
|
return PostgreSQLSequenceSupport.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getQuerySequencesString() {
|
||||||
|
return "select sequence_name,sequence_schema,sequence_catalog,start_value,minimum_value,maximum_value,increment from information_schema.sequences";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLobValueChangePropagation() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||||
|
return new StandardSqlAstTranslatorFactory() {
|
||||||
|
@Override
|
||||||
|
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||||
|
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
return new CockroachLegacySqlAstTranslator<>( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NationalizationSupport getNationalizationSupport() {
|
||||||
|
// TEXT / STRING inherently support nationalized data
|
||||||
|
return NationalizationSupport.IMPLICIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaxIdentifierLength() {
|
||||||
|
return 63;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsStandardArrays() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDateTimeLiteral(
|
||||||
|
SqlAppender appender,
|
||||||
|
TemporalAccessor temporalAccessor,
|
||||||
|
TemporalType precision,
|
||||||
|
TimeZone jdbcTimeZone) {
|
||||||
|
switch ( precision ) {
|
||||||
|
case DATE:
|
||||||
|
appender.appendSql( "date '" );
|
||||||
|
appendAsDate( appender, temporalAccessor );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
appender.appendSql( "time '" );
|
||||||
|
appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIMESTAMP:
|
||||||
|
appender.appendSql( "timestamp with time zone '" );
|
||||||
|
appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
|
||||||
|
switch ( precision ) {
|
||||||
|
case DATE:
|
||||||
|
appender.appendSql( "date '" );
|
||||||
|
appendAsDate( appender, date );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
appender.appendSql( "time '" );
|
||||||
|
appendAsTime( appender, date );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIMESTAMP:
|
||||||
|
appender.appendSql( "timestamp with time zone '" );
|
||||||
|
appendAsTimestampWithMicros( 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 '" );
|
||||||
|
appendAsDate( appender, calendar );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIME:
|
||||||
|
appender.appendSql( "time '" );
|
||||||
|
appendAsTime( appender, calendar );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
case TIMESTAMP:
|
||||||
|
appender.appendSql( "timestamp with time zone '" );
|
||||||
|
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||||
|
appender.appendSql( '\'' );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The {@code extract()} function returns {@link TemporalUnit#DAY_OF_WEEK}
|
||||||
|
* numbered from 0 to 6. This isn't consistent with what most other
|
||||||
|
* databases do, so here we adjust the result by generating
|
||||||
|
* {@code (extract(dayofweek,arg)+1))}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public String extractPattern(TemporalUnit unit) {
|
||||||
|
switch ( unit ) {
|
||||||
|
case DAY_OF_WEEK:
|
||||||
|
return "(" + super.extractPattern(unit) + "+1)";
|
||||||
|
case SECOND:
|
||||||
|
return "(extract(second from ?2)+extract(microsecond from ?2)/1e6)";
|
||||||
|
default:
|
||||||
|
return super.extractPattern(unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translateExtractField(TemporalUnit unit) {
|
||||||
|
switch ( unit ) {
|
||||||
|
case DAY_OF_MONTH: return "day";
|
||||||
|
case DAY_OF_YEAR: return "dayofyear";
|
||||||
|
case DAY_OF_WEEK: return "dayofweek";
|
||||||
|
default: return super.translateExtractField( unit );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@code microsecond} is the smallest unit for an {@code interval},
|
||||||
|
* and the highest precision for a {@code timestamp}.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public long getFractionalSecondPrecisionInNanos() {
|
||||||
|
return 1_000; //microseconds
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||||
|
if ( intervalType != null ) {
|
||||||
|
return "(?2+?3)";
|
||||||
|
}
|
||||||
|
switch ( unit ) {
|
||||||
|
case NANOSECOND:
|
||||||
|
return "(?3+(?2)/1e3*interval '1 microsecond')";
|
||||||
|
case NATIVE:
|
||||||
|
return "(?3+(?2)*interval '1 microsecond')";
|
||||||
|
case QUARTER: //quarter is not supported in interval literals
|
||||||
|
return "(?3+(?2)*interval '3 month')";
|
||||||
|
case WEEK: //week is not supported in interval literals
|
||||||
|
return "(?3+(?2)*interval '7 day')";
|
||||||
|
default:
|
||||||
|
return "(?3+(?2)*interval '1 ?1')";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||||
|
if ( unit == null ) {
|
||||||
|
return "(?3-?2)";
|
||||||
|
}
|
||||||
|
switch (unit) {
|
||||||
|
case YEAR:
|
||||||
|
return "(extract(year from ?3)-extract(year from ?2))";
|
||||||
|
case QUARTER:
|
||||||
|
return "(extract(year from ?3)*4-extract(year from ?2)*4+extract(month from ?3)//3-extract(month from ?2)//3)";
|
||||||
|
case MONTH:
|
||||||
|
return "(extract(year from ?3)*12-extract(year from ?2)*12+extract(month from ?3)-extract(month from ?2))";
|
||||||
|
}
|
||||||
|
if ( toTemporalType != TemporalType.TIMESTAMP && fromTemporalType != TemporalType.TIMESTAMP ) {
|
||||||
|
// special case: subtraction of two dates
|
||||||
|
// results in an integer number of days
|
||||||
|
// instead of an INTERVAL
|
||||||
|
return "(?3-?2)" + DAY.conversionFactor( unit, this );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (unit) {
|
||||||
|
case WEEK:
|
||||||
|
return "extract_duration(hour from ?3-?2)/168";
|
||||||
|
case DAY:
|
||||||
|
return "extract_duration(hour from ?3-?2)/24";
|
||||||
|
case NANOSECOND:
|
||||||
|
return "extract_duration(microsecond from ?3-?2)*1e3";
|
||||||
|
default:
|
||||||
|
return "extract_duration(?1 from ?3-?2)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String translateDurationField(TemporalUnit unit) {
|
||||||
|
return unit==NATIVE
|
||||||
|
? "microsecond"
|
||||||
|
: super.translateDurationField(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||||
|
appender.appendSql( SpannerDialect.datetimeFormat( format ).result() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitHandler getLimitHandler() {
|
||||||
|
return OffsetFetchLimitHandler.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateString(String aliases) {
|
||||||
|
return getForUpdateString() + " of " + aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateString(LockOptions lockOptions) {
|
||||||
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
|
if ( getVersion().isBefore( 20, 1 ) ) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return super.getForUpdateString( lockOptions );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateString(String aliases, LockOptions lockOptions) {
|
||||||
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
|
if ( getVersion().isBefore( 20, 1 ) ) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Parent's implementation for (aliases, lockOptions) ignores aliases.
|
||||||
|
*/
|
||||||
|
if ( aliases.isEmpty() ) {
|
||||||
|
LockMode lockMode = lockOptions.getLockMode();
|
||||||
|
final Iterator<Map.Entry<String, LockMode>> itr = lockOptions.getAliasLockIterator();
|
||||||
|
while ( itr.hasNext() ) {
|
||||||
|
// seek the highest lock mode
|
||||||
|
final Map.Entry<String, LockMode> entry = itr.next();
|
||||||
|
final LockMode lm = entry.getValue();
|
||||||
|
if ( lm.greaterThan( lockMode ) ) {
|
||||||
|
aliases = entry.getKey();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LockMode lockMode = lockOptions.getAliasSpecificLockMode( aliases );
|
||||||
|
if (lockMode == null ) {
|
||||||
|
lockMode = lockOptions.getLockMode();
|
||||||
|
}
|
||||||
|
switch ( lockMode ) {
|
||||||
|
case PESSIMISTIC_READ: {
|
||||||
|
return getReadLockString( aliases, lockOptions.getTimeOut() );
|
||||||
|
}
|
||||||
|
case PESSIMISTIC_WRITE: {
|
||||||
|
return getWriteLockString( aliases, lockOptions.getTimeOut() );
|
||||||
|
}
|
||||||
|
case UPGRADE_NOWAIT:
|
||||||
|
case PESSIMISTIC_FORCE_INCREMENT: {
|
||||||
|
return getForUpdateNowaitString(aliases);
|
||||||
|
}
|
||||||
|
case UPGRADE_SKIPLOCKED: {
|
||||||
|
return getForUpdateSkipLockedString(aliases);
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String withTimeout(String lockString, int timeout) {
|
||||||
|
switch (timeout) {
|
||||||
|
case LockOptions.NO_WAIT: {
|
||||||
|
return supportsNoWait() ? lockString + " nowait" : lockString;
|
||||||
|
}
|
||||||
|
case LockOptions.SKIP_LOCKED: {
|
||||||
|
return supportsSkipLocked() ? lockString + " skip locked" : lockString;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
return lockString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteLockString(int timeout) {
|
||||||
|
return withTimeout( getForUpdateString(), timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWriteLockString(String aliases, int timeout) {
|
||||||
|
return withTimeout( getForUpdateString( aliases ), timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReadLockString(int timeout) {
|
||||||
|
return withTimeout(" for share", timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReadLockString(String aliases, int timeout) {
|
||||||
|
return withTimeout(" for share of " + aliases, timeout );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateNowaitString() {
|
||||||
|
return supportsNoWait()
|
||||||
|
? " for update nowait"
|
||||||
|
: getForUpdateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateNowaitString(String aliases) {
|
||||||
|
return supportsNoWait()
|
||||||
|
? " for update of " + aliases + " nowait"
|
||||||
|
: getForUpdateString(aliases);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateSkipLockedString() {
|
||||||
|
return supportsSkipLocked()
|
||||||
|
? " for update skip locked"
|
||||||
|
: getForUpdateString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getForUpdateSkipLockedString(String aliases) {
|
||||||
|
return supportsSkipLocked()
|
||||||
|
? " for update of " + aliases + " skip locked"
|
||||||
|
: getForUpdateString( aliases );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsOuterJoinForUpdate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean useInputStreamToInsertBlob() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsOffsetInSubquery() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsWindowFunctions() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsLateral() {
|
||||||
|
return getVersion().isSameOrAfter( 20, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsNoWait() {
|
||||||
|
return getVersion().isSameOrAfter( 20, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsWait() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean supportsSkipLocked() {
|
||||||
|
return getVersion().isSameOrAfter( 20, 1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
|
return getVersion().isSameOrAfter( 20, 1 ) ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NameQualifierSupport getNameQualifierSupport() {
|
||||||
|
// This method is overridden so the correct value will be returned when
|
||||||
|
// DatabaseMetaData is not available.
|
||||||
|
return NameQualifierSupport.SCHEMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||||
|
throws SQLException {
|
||||||
|
|
||||||
|
if ( dbMetaData == null ) {
|
||||||
|
builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.LOWER );
|
||||||
|
builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED );
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.buildIdentifierHelper( builder, dbMetaData );
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
/*
|
||||||
|
* 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.engine.spi.SessionFactoryImplementor;
|
||||||
|
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||||
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
|
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||||
|
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||||
|
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.exec.spi.JdbcOperation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A SQL AST translator for Cockroach.
|
||||||
|
*
|
||||||
|
* @author Christian Beikov
|
||||||
|
*/
|
||||||
|
public class CockroachLegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||||
|
|
||||||
|
public CockroachLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||||
|
super( sessionFactory, statement );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderExpressionAsClauseItem(Expression expression) {
|
||||||
|
expression.accept( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitBooleanExpressionPredicate(BooleanExpressionPredicate booleanExpressionPredicate) {
|
||||||
|
if ( booleanExpressionPredicate.isNegated() ) {
|
||||||
|
super.visitBooleanExpressionPredicate( booleanExpressionPredicate );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
final boolean isNegated = booleanExpressionPredicate.isNegated();
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( "not(" );
|
||||||
|
}
|
||||||
|
booleanExpressionPredicate.getExpression().accept( this );
|
||||||
|
if ( isNegated ) {
|
||||||
|
appendSql( CLOSE_PARENTHESIS );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getForShare(int timeoutMillis) {
|
||||||
|
return " for share";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LockStrategy determineLockingStrategy(
|
||||||
|
QuerySpec querySpec,
|
||||||
|
ForUpdateClause forUpdateClause,
|
||||||
|
Boolean followOnLocking) {
|
||||||
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
|
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
|
||||||
|
return LockStrategy.NONE;
|
||||||
|
}
|
||||||
|
return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||||
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
|
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.renderForUpdateClause( querySpec, forUpdateClause );
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||||
|
// Check if current query part is already row numbering to avoid infinite recursion
|
||||||
|
return useOffsetFetchClause( queryPart ) && getQueryPartForRowNumbering() != queryPart
|
||||||
|
&& !isRowsOnlyFetchClauseType( queryPart );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||||
|
if ( shouldEmulateFetchClause( queryGroup ) ) {
|
||||||
|
emulateFetchOffsetWithWindowFunctions( queryGroup, true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitQueryGroup( queryGroup );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitQuerySpec(QuerySpec querySpec) {
|
||||||
|
if ( shouldEmulateFetchClause( querySpec ) ) {
|
||||||
|
emulateFetchOffsetWithWindowFunctions( querySpec, true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
super.visitQuerySpec( querySpec );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||||
|
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||||
|
renderLimitOffsetClause( queryPart );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderSearchClause(CteStatement cte) {
|
||||||
|
// Cockroach does not support this, but it's just a hint anyway
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderCycleClause(CteStatement cte) {
|
||||||
|
// Cockroach does not support this, but it can be emulated
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -53,8 +53,6 @@ import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
|
||||||
import org.hibernate.sql.ast.tree.Statement;
|
import org.hibernate.sql.ast.tree.Statement;
|
||||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||||
import org.hibernate.type.JavaObjectType;
|
import org.hibernate.type.JavaObjectType;
|
||||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
|
||||||
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
import org.hibernate.type.descriptor.jdbc.InstantAsTimestampWithTimeZoneJdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
import org.hibernate.type.descriptor.jdbc.ObjectNullAsBinaryTypeJdbcType;
|
||||||
|
@ -104,10 +102,12 @@ public class CockroachDialect extends Dialect {
|
||||||
// KNOWN LIMITATIONS:
|
// KNOWN LIMITATIONS:
|
||||||
// * no support for java.sql.Clob
|
// * no support for java.sql.Clob
|
||||||
|
|
||||||
|
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 21, 1 );
|
||||||
|
|
||||||
private final PostgreSQLDriverKind driverKind;
|
private final PostgreSQLDriverKind driverKind;
|
||||||
|
|
||||||
public CockroachDialect() {
|
public CockroachDialect() {
|
||||||
this( DatabaseVersion.make( 19, 2 ) );
|
this( MINIMUM_VERSION );
|
||||||
}
|
}
|
||||||
|
|
||||||
public CockroachDialect(DialectResolutionInfo info) {
|
public CockroachDialect(DialectResolutionInfo info) {
|
||||||
|
@ -125,6 +125,11 @@ public class CockroachDialect extends Dialect {
|
||||||
this.driverKind = driverKind;
|
this.driverKind = driverKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||||
|
return MINIMUM_VERSION;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String columnType(int sqlTypeCode) {
|
protected String columnType(int sqlTypeCode) {
|
||||||
switch ( sqlTypeCode ) {
|
switch ( sqlTypeCode ) {
|
||||||
|
@ -183,14 +188,9 @@ public class CockroachDialect extends Dialect {
|
||||||
ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) );
|
ddlTypeRegistry.addDescriptor( new Scale6IntervalSecondDdlType( this ) );
|
||||||
|
|
||||||
// Prefer jsonb if possible
|
// Prefer jsonb if possible
|
||||||
if ( getVersion().isSameOrAfter( 20 ) ) {
|
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( INET, "inet", this ) );
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "jsonb", this ) );
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -234,15 +234,9 @@ public class CockroachDialect extends Dialect {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( UUIDJdbcType.INSTANCE );
|
||||||
if ( PostgreSQLPGObjectJdbcType.isUsable() ) {
|
if ( PostgreSQLPGObjectJdbcType.isUsable() ) {
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLIntervalSecondJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLIntervalSecondJdbcType.INSTANCE );
|
||||||
|
|
||||||
if ( getVersion().isSameOrAfter( 20, 0 ) ) {
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLInetJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLInetJdbcType.INSTANCE );
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonbJdbcType.INSTANCE );
|
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonbJdbcType.INSTANCE );
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
jdbcTypeRegistry.addDescriptorIfAbsent( PostgreSQLJsonJdbcType.INSTANCE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force Blob binding to byte[] for CockroachDB
|
// Force Blob binding to byte[] for CockroachDB
|
||||||
|
@ -632,19 +626,11 @@ public class CockroachDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getForUpdateString(LockOptions lockOptions) {
|
public String getForUpdateString(LockOptions lockOptions) {
|
||||||
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
|
||||||
if ( getVersion().isBefore( 20, 1 ) ) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
return super.getForUpdateString( lockOptions );
|
return super.getForUpdateString( lockOptions );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getForUpdateString(String aliases, LockOptions lockOptions) {
|
public String getForUpdateString(String aliases, LockOptions lockOptions) {
|
||||||
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
|
||||||
if ( getVersion().isBefore( 20, 1 ) ) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* Parent's implementation for (aliases, lockOptions) ignores aliases.
|
* Parent's implementation for (aliases, lockOptions) ignores aliases.
|
||||||
*/
|
*/
|
||||||
|
@ -768,12 +754,12 @@ public class CockroachDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsLateral() {
|
public boolean supportsLateral() {
|
||||||
return getVersion().isSameOrAfter( 20, 1 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsNoWait() {
|
public boolean supportsNoWait() {
|
||||||
return getVersion().isSameOrAfter( 20, 1 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -783,12 +769,12 @@ public class CockroachDialect extends Dialect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean supportsSkipLocked() {
|
public boolean supportsSkipLocked() {
|
||||||
return getVersion().isSameOrAfter( 20, 1 );
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RowLockStrategy getWriteRowLockStrategy() {
|
public RowLockStrategy getWriteRowLockStrategy() {
|
||||||
return getVersion().isSameOrAfter( 20, 1 ) ? RowLockStrategy.TABLE : RowLockStrategy.NONE;
|
return RowLockStrategy.TABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -63,18 +63,12 @@ public class CockroachSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
||||||
ForUpdateClause forUpdateClause,
|
ForUpdateClause forUpdateClause,
|
||||||
Boolean followOnLocking) {
|
Boolean followOnLocking) {
|
||||||
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
|
|
||||||
return LockStrategy.NONE;
|
|
||||||
}
|
|
||||||
return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking );
|
return super.determineLockingStrategy( querySpec, forUpdateClause, followOnLocking );
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||||
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
// Support was added in 20.1: https://www.cockroachlabs.com/docs/v20.1/select-for-update.html
|
||||||
if ( getDialect().getVersion().isBefore( 20, 1 ) ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.renderForUpdateClause( querySpec, forUpdateClause );
|
super.renderForUpdateClause( querySpec, forUpdateClause );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
package org.hibernate.spatial.dialect.cockroachdb;
|
package org.hibernate.spatial.dialect.cockroachdb;
|
||||||
|
|
||||||
import org.hibernate.dialect.CockroachDialect;
|
import org.hibernate.dialect.CockroachDialect;
|
||||||
|
import org.hibernate.dialect.DatabaseVersion;
|
||||||
import org.hibernate.spatial.SpatialDialect;
|
import org.hibernate.spatial.SpatialDialect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -17,4 +18,7 @@ import org.hibernate.spatial.SpatialDialect;
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public class CockroachDB202SpatialDialect extends CockroachDialect implements SpatialDialect {
|
public class CockroachDB202SpatialDialect extends CockroachDialect implements SpatialDialect {
|
||||||
|
public CockroachDB202SpatialDialect() {
|
||||||
|
super( DatabaseVersion.make( 19, 2 ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue