org.hibernate.community.dialect.SingleStoreDialect Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hibernate-community-dialects Show documentation
Show all versions of hibernate-community-dialects Show documentation
Hibernate's community supported dialects
The newest version!
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.community.dialect;
import java.sql.CallableStatement;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.ZonedDateTime;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import org.hibernate.Length;
import org.hibernate.PessimisticLockException;
import org.hibernate.boot.Metadata;
import org.hibernate.boot.model.FunctionContributions;
import org.hibernate.boot.model.TypeContributions;
import org.hibernate.boot.model.relational.Exportable;
import org.hibernate.boot.model.relational.Sequence;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.DmlTargetColumnQualifierSupport;
import org.hibernate.dialect.FunctionalDependencyAnalysisSupport;
import org.hibernate.dialect.FunctionalDependencyAnalysisSupportImpl;
import org.hibernate.dialect.NullOrdering;
import org.hibernate.dialect.Replacer;
import org.hibernate.dialect.SelectItemReferenceStrategy;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.identity.MySQLIdentityColumnSupport;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.LimitLimitHandler;
import org.hibernate.dialect.temptable.TemporaryTable;
import org.hibernate.dialect.temptable.TemporaryTableKind;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.jdbc.env.spi.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.jdbc.env.spi.SchemaNameResolver;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.LockTimeoutException;
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor;
import org.hibernate.exception.spi.ViolatedConstraintNameExtractor;
import org.hibernate.internal.util.JdbcExceptionHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.ForeignKey;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.sqm.CastType;
import org.hibernate.query.sqm.IntervalType;
import org.hibernate.query.common.TemporalUnit;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
import org.hibernate.query.sqm.mutation.spi.AfterUseAction;
import org.hibernate.query.sqm.mutation.spi.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.query.sqm.produce.function.FunctionParameterType;
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.tool.schema.spi.Exporter;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.NullType;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.descriptor.jdbc.EnumJdbcType;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.NullJdbcType;
import org.hibernate.type.descriptor.jdbc.OrdinalEnumJdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.NativeEnumDdlTypeImpl;
import org.hibernate.type.descriptor.sql.internal.NativeOrdinalEnumDdlTypeImpl;
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.NUMERIC;
import static org.hibernate.type.SqlTypes.BIGINT;
import static org.hibernate.type.SqlTypes.BINARY;
import static org.hibernate.type.SqlTypes.BIT;
import static org.hibernate.type.SqlTypes.BLOB;
import static org.hibernate.type.SqlTypes.BOOLEAN;
import static org.hibernate.type.SqlTypes.CHAR;
import static org.hibernate.type.SqlTypes.CLOB;
import static org.hibernate.type.SqlTypes.DECIMAL;
import static org.hibernate.type.SqlTypes.DOUBLE;
import static org.hibernate.type.SqlTypes.FLOAT;
import static org.hibernate.type.SqlTypes.INTEGER;
import static org.hibernate.type.SqlTypes.JSON;
import static org.hibernate.type.SqlTypes.LONG32NVARCHAR;
import static org.hibernate.type.SqlTypes.LONG32VARBINARY;
import static org.hibernate.type.SqlTypes.LONG32VARCHAR;
import static org.hibernate.type.SqlTypes.NCHAR;
import static org.hibernate.type.SqlTypes.NCLOB;
import static org.hibernate.type.SqlTypes.NVARCHAR;
import static org.hibernate.type.SqlTypes.SMALLINT;
import static org.hibernate.type.SqlTypes.TIMESTAMP;
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TIME_WITH_TIMEZONE;
import static org.hibernate.type.SqlTypes.TINYINT;
import static org.hibernate.type.SqlTypes.VARBINARY;
import static org.hibernate.type.SqlTypes.VARCHAR;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsLocalTime;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMillis;
/**
* An SQL dialect for SingleStore.
*
* The following are some of the key aspects and limitations of SingleStore that may affect Hibernate functionality:
*
*
* - SingleStore supports two table types: COLUMNSTORE and ROWSTORE. Explicit table type can be configured by setting 'hibernate.dialect.singlestore.table_type' property. Refer to {@link SingleStoreTableType} for details.
* - SingleStore has a random order for SELECT queries, which may impact the predictability of query results.
* - SingleStore does not support foreign keys and referential integrity, which could affect the design of your database schema.
* - The SingleStore dialect ignores unique key constraints. See {@link DoNothingUniqueDelegate} for more information.
* - SingleStore does not support zoned timestamps, which might require adjustments to how you handle time-related data.
* - Updating primary keys in SingleStore is restricted because every primary key is also a unique key and shard key.
* - SingleStore does not support the ALL/ANY clause in SQL queries.
* - Sub-selects with references to outer table fields are not supported in SingleStore.
* - SingleStore does not support the 'FOR UPDATE' clause for table locking with distributed joins. It's disabled by default, can be enabled by setting 'hibernate.dialect.singlestore.for_update_lock_enabled' property {@link SingleStoreDialect#SINGLE_STORE_FOR_UPDATE_LOCK_ENABLED}.
* - The LIKE clause in SingleStore is case-insensitive, which might differ from other SQL implementations.
*
*
* @author Oleksandr Yeliseiev
*/
public class SingleStoreDialect extends Dialect {
private static final int PARAM_LIST_SIZE_LIMIT = 1_048_576;
private static final EmptyExporter NOOP_EXPORTER = new EmptyExporter();
private static final UniqueDelegate NOOP_UNIQUE_DELEGATE = new DoNothingUniqueDelegate();
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 8, 0 );
private final SingleStoreTableType explicitTableType;
private final boolean isForUpdateLockingEnabled;
public SingleStoreDialect() {
this( MINIMUM_VERSION, null, false );
}
public SingleStoreDialect(DialectResolutionInfo info) {
this( createVersion( info ), getTableType( info ), getUpdateForEnabled( info ) );
registerKeywords( info );
}
public SingleStoreDialect(
DatabaseVersion version, SingleStoreTableType explicitTableType, boolean isForUpdateLockingEnabled) {
super( version );
this.explicitTableType = explicitTableType;
this.isForUpdateLockingEnabled = isForUpdateLockingEnabled;
}
private static DatabaseVersion createVersion(DialectResolutionInfo info) {
final String versionString = info.getDatabaseVersion();
if ( versionString != null ) {
final String[] components = StringHelper.split( ".", versionString );
if ( components.length >= 3 ) {
try {
final int majorVersion = Integer.parseInt( components[0] );
final int minorVersion = Integer.parseInt( components[1] );
final int patchLevel = Integer.parseInt( components[2] );
return DatabaseVersion.make( majorVersion, minorVersion, patchLevel );
}
catch (NumberFormatException ex) {
// Ignore
}
}
}
return info.makeCopyOrDefault( MINIMUM_VERSION );
}
private static SingleStoreTableType getTableType(DialectResolutionInfo info) {
String value = ConfigurationHelper.getString( SINGLE_STORE_TABLE_TYPE, info.getConfigurationValues() );
return value == null ? null : SingleStoreTableType.fromValue( value );
}
private static boolean getUpdateForEnabled(DialectResolutionInfo info) {
return ConfigurationHelper.getBoolean(
SINGLE_STORE_FOR_UPDATE_LOCK_ENABLED,
info.getConfigurationValues(),
false
);
}
private final SizeStrategy sizeStrategy = new SizeStrategyImpl() {
@Override
public Size resolveSize(
JdbcType jdbcType, JavaType> javaType, Integer precision, Integer scale, Long length) {
switch ( jdbcType.getDdlTypeCode() ) {
case BIT:
if ( length != null ) {
return Size.length( Math.min( Math.max( length, 1 ), 64 ) );
}
case BLOB:
case NCLOB:
case CLOB:
return super.resolveSize(
jdbcType,
javaType,
precision,
scale,
length == null ? getDefaultLobLength() : length
);
default:
return super.resolveSize( jdbcType, javaType, precision, scale, length );
}
}
};
@Override
protected DatabaseVersion getMinimumSupportedVersion() {
return MINIMUM_VERSION;
}
@Override
public boolean useMaterializedLobWhenCapacityExceeded() {
return false;
}
@Override
public String extractPattern(TemporalUnit unit) {
switch ( unit ) {
case SECOND:
return "(second(?2)+microsecond(?2)/1e6)";
case WEEK:
return "weekofyear(?2)";
case DAY_OF_WEEK:
return "dayofweek(?2)";
case DAY_OF_MONTH:
return "dayofmonth(?2)";
case DAY_OF_YEAR:
return "dayofyear(?2)";
case EPOCH:
return "unix_timestamp(?2)";
default:
return "?1(?2)";
}
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
if ( temporalType == TemporalType.TIME ) {
switch ( unit ) {
case NANOSECOND:
return "time(timestampadd(microsecond,(?2)/1e3,to_timestamp(?3, 'HH24:MI:SS.FF6')))";
case NATIVE:
return "time(timestampadd(microsecond, ?2, to_timestamp(?3, 'HH24:MI:SS.FF6')))";
case SECOND:
return "time(timestampadd(microsecond, ?2 * 1000000, to_timestamp(?3, 'HH24:MI:SS.FF6')))"; // to handle seconds fraction part
default:
return "time(timestampadd(?1, ?2, to_timestamp(?3, 'HH24:MI:SS.FF6')))";
}
}
switch ( unit ) {
case NANOSECOND:
return "timestampadd(microsecond,(?2)/1e3,?3)";
case NATIVE:
return "timestampadd(microsecond,?2,?3)";
case SECOND:
return "timestampadd(microsecond,?2 * 1000000,?3)"; // to handle seconds fraction part
default:
return "timestampadd(?1,?2,?3)";
}
}
@Override
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
String fromType = fromTemporalType == TemporalType.TIME ? "to_timestamp(?2, 'HH24:MI:SS.FF6')" : "?2";
String toType = toTemporalType == TemporalType.TIME ? "to_timestamp(?3, 'HH24:MI:SS.FF6')" : "?3";
switch ( unit ) {
case NANOSECOND:
return String.format( "timestampdiff(microsecond,%s,%s)*1e3", fromType, toType );
case NATIVE:
return String.format( "timestampdiff(microsecond,%s,%s)", fromType, toType );
default:
return String.format( "timestampdiff(?1,%s,%s)", fromType, toType );
}
}
@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('" );
appendAsLocalTime( appender, temporalAccessor );
appender.appendSql( "')" );
break;
case TIMESTAMP:
if ( temporalAccessor instanceof ZonedDateTime ) {
temporalAccessor = ( (ZonedDateTime) temporalAccessor ).toOffsetDateTime();
}
appender.appendSql( "timestamp('" );
appendAsTimestampWithMicros(
appender,
temporalAccessor,
supportsTemporalLiteralOffset(),
jdbcTimeZone,
false
);
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('" );
appendAsLocalTime( appender, date );
appender.appendSql( "')" );
break;
case TIMESTAMP:
appender.appendSql( "timestamp('" );
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('" );
appendAsLocalTime( appender, calendar );
appender.appendSql( "')" );
break;
case TIMESTAMP:
appender.appendSql( "timestamp('" );
appendAsTimestampWithMillis( appender, calendar, jdbcTimeZone );
appender.appendSql( "')" );
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public SelectItemReferenceStrategy getGroupBySelectItemReferenceStrategy() {
return SelectItemReferenceStrategy.POSITION;
}
//Creating an index on an ENUM column on columnstore tables is not supported.
@Override
public String getEnumTypeDeclaration(String name, String[] values) {
StringBuilder type = new StringBuilder();
type.append( "enum (" );
String separator = "";
for ( String value : values ) {
type.append( separator ).append( '\'' ).append( value ).append( '\'' );
separator = ",";
}
return type.append( ')' ).toString();
}
@Override
public String getQueryHintString(String query, String hints) {
return addQueryHints( query, hints );
}
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR;
}
private static final ViolatedConstraintNameExtractor EXTRACTOR = new TemplatedViolatedConstraintNameExtractor( sqle -> {
final String sqlState = JdbcExceptionHelper.extractSqlState( sqle );
if ( sqlState != null ) {
if ( Integer.parseInt( sqlState ) == 23000 ) {
return extractUsingTemplate( " for key '", "'", sqle.getMessage() );
}
}
return null;
} );
@Override
public boolean qualifyIndexName() {
return false;
}
@Override
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.registerColumnTypes( typeContributions, serviceRegistry );
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( JSON, "json", this ) );
final int maxTinyLobLen = 225;
final int maxLobLen = 65_535;
final int maxMediumLobLen = 16_777_215;
final CapacityDependentDdlType.Builder varcharBuilder = CapacityDependentDdlType.builder(
VARCHAR,
CapacityDependentDdlType.LobKind.BIGGEST_LOB,
columnType( CLOB ),
columnType( CHAR ),
castType( CHAR ),
this
).withTypeCapacity( getMaxVarcharLength(), "varchar($l)" ).withTypeCapacity( maxMediumLobLen, "mediumtext" );
if ( getMaxVarcharLength() < maxLobLen ) {
varcharBuilder.withTypeCapacity( maxLobLen, "text" );
}
ddlTypeRegistry.addDescriptor( varcharBuilder.build() );
// SingleStore doesn't support nchar/nvarchar/ntext
final CapacityDependentDdlType.Builder nvarcharBuilder = CapacityDependentDdlType.builder(
NVARCHAR,
CapacityDependentDdlType.LobKind.BIGGEST_LOB,
columnType( NCLOB ),
columnType( NCHAR ),
castType( NCHAR ),
this
).withTypeCapacity( getMaxVarcharLength(), "varchar($l) character set utf8" ).withTypeCapacity(
maxMediumLobLen,
"mediumtext character set utf8"
);
if ( getMaxVarcharLength() < maxLobLen ) {
nvarcharBuilder.withTypeCapacity( maxLobLen, "text character set utf8" );
}
ddlTypeRegistry.addDescriptor( nvarcharBuilder.build() );
final CapacityDependentDdlType.Builder varbinaryBuilder = CapacityDependentDdlType.builder(
VARBINARY,
CapacityDependentDdlType.LobKind.BIGGEST_LOB,
columnType( BLOB ),
columnType( BINARY ),
castType( BINARY ),
this
).withTypeCapacity( getMaxVarbinaryLength(), "varbinary($l)" ).withTypeCapacity(
maxMediumLobLen,
"mediumblob"
);
if ( getMaxVarbinaryLength() < maxLobLen ) {
varbinaryBuilder.withTypeCapacity( maxLobLen, "blob" );
}
ddlTypeRegistry.addDescriptor( varbinaryBuilder.build() );
ddlTypeRegistry.addDescriptor( new DdlTypeImpl(
LONG32VARBINARY,
columnType( BLOB ),
castType( BINARY ),
this
) );
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32VARCHAR, columnType( CLOB ), castType( CHAR ), this ) );
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( LONG32NVARCHAR, columnType( CLOB ), castType( CHAR ), this ) );
ddlTypeRegistry.addDescriptor( CapacityDependentDdlType.builder(
BLOB,
columnType( BLOB ),
castType( BINARY ),
this
)
.withTypeCapacity( maxTinyLobLen, "tinyblob" )
.withTypeCapacity( maxMediumLobLen, "mediumblob" )
.withTypeCapacity( maxLobLen, "blob" )
.build() );
ddlTypeRegistry.addDescriptor( CapacityDependentDdlType.builder(
CLOB,
columnType( CLOB ),
castType( CHAR ),
this
)
.withTypeCapacity( maxTinyLobLen, "tinytext" )
.withTypeCapacity( maxMediumLobLen, "mediumtext" )
.withTypeCapacity( maxLobLen, "text" )
.build() );
ddlTypeRegistry.addDescriptor( CapacityDependentDdlType.builder(
NCLOB,
columnType( NCLOB ),
castType( NCHAR ),
this
).withTypeCapacity( maxTinyLobLen, "tinytext character set utf8" ).withTypeCapacity(
maxMediumLobLen,
"mediumtext character set utf8"
).withTypeCapacity( maxLobLen, "text character set utf8" ).build() );
ddlTypeRegistry.addDescriptor( new NativeEnumDdlTypeImpl( this ) );
ddlTypeRegistry.addDescriptor( new NativeOrdinalEnumDdlTypeImpl( this ) );
}
@Override
public void initializeFunctionRegistry(FunctionContributions functionContributions) {
super.initializeFunctionRegistry( functionContributions );
CommonFunctionFactory commonFunctionFactory = new CommonFunctionFactory( functionContributions );
commonFunctionFactory.windowFunctions();
commonFunctionFactory.radians();
commonFunctionFactory.degrees();
commonFunctionFactory.cot();
commonFunctionFactory.log();
commonFunctionFactory.log2();
commonFunctionFactory.log10();
commonFunctionFactory.trim2();
commonFunctionFactory.octetLength();
commonFunctionFactory.reverse();
commonFunctionFactory.pad_space();
commonFunctionFactory.md5();
commonFunctionFactory.yearMonthDay();
commonFunctionFactory.hourMinuteSecond();
commonFunctionFactory.dayofweekmonthyear();
commonFunctionFactory.weekQuarter();
commonFunctionFactory.daynameMonthname();
commonFunctionFactory.lastDay();
commonFunctionFactory.date();
commonFunctionFactory.timestamp();
commonFunctionFactory.utcDateTimeTimestamp();
commonFunctionFactory.rand();
commonFunctionFactory.crc32();
commonFunctionFactory.sha1();
commonFunctionFactory.sha2();
commonFunctionFactory.sha();
commonFunctionFactory.octetLength();
commonFunctionFactory.ascii();
commonFunctionFactory.instr();
commonFunctionFactory.substr();
commonFunctionFactory.position();
commonFunctionFactory.nowCurdateCurtime();
commonFunctionFactory.trunc_truncate();
commonFunctionFactory.bitandorxornot_operator();
commonFunctionFactory.bitAndOr();
commonFunctionFactory.stddev();
commonFunctionFactory.stddevPopSamp();
commonFunctionFactory.variance();
commonFunctionFactory.varPopSamp();
commonFunctionFactory.datediff();
commonFunctionFactory.adddateSubdateAddtimeSubtime();
commonFunctionFactory.format_dateFormat();
commonFunctionFactory.makedateMaketime();
commonFunctionFactory.localtimeLocaltimestamp();
commonFunctionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
commonFunctionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
commonFunctionFactory.listagg_groupConcat();
functionContributions.getFunctionRegistry()
.namedDescriptorBuilder( "time" )
.setExactArgumentCount( 1 )
.setInvariantType( functionContributions.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( StandardBasicTypes.STRING ) )
.register();
functionContributions.getFunctionRegistry()
.patternDescriptorBuilder( "median", "median(?1) over ()" )
.setInvariantType( functionContributions.getTypeConfiguration()
.getBasicTypeRegistry()
.resolve( StandardBasicTypes.DOUBLE ) )
.setExactArgumentCount( 1 )
.setParameterTypes( NUMERIC )
.register();
BasicTypeRegistry basicTypeRegistry = functionContributions.getTypeConfiguration().getBasicTypeRegistry();
SqmFunctionRegistry functionRegistry = functionContributions.getFunctionRegistry();
functionRegistry.noArgsBuilder( "localtime" )
.setInvariantType( basicTypeRegistry.resolve( StandardBasicTypes.TIMESTAMP ) )
.setUseParenthesesWhenNoArgs( false )
.register();
functionRegistry.patternDescriptorBuilder( "pi", "pi() :> double" ).setInvariantType( basicTypeRegistry.resolve(
StandardBasicTypes.DOUBLE ) ).setExactArgumentCount( 0 ).setArgumentListSignature( "" ).register();
functionRegistry.patternDescriptorBuilder( "chr", "char(?1 using utf8mb4)" )
.setInvariantType( basicTypeRegistry.resolve( StandardBasicTypes.CHARACTER ) )
.setExactArgumentCount( 1 )
.setParameterTypes( FunctionParameterType.INTEGER )
.register();
functionRegistry.registerAlternateKey( "char", "chr" );
}
@Override
public String getCreateTableString() {
return explicitTableType == null ? "create table" : String.format(
"create %s table",
explicitTableType.name().toLowerCase()
);
}
@Override
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
super.contributeTypes( typeContributions, serviceRegistry );
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration().getJdbcTypeRegistry();
typeContributions.contributeJdbcType( NullJdbcType.INSTANCE );
// Until we remove StandardBasicTypes, we have to keep this
typeContributions.contributeType( new NullType(
NullJdbcType.INSTANCE,
typeContributions.getTypeConfiguration()
.getJavaTypeRegistry()
.getDescriptor( Object.class )
) );
jdbcTypeRegistry.addDescriptor( EnumJdbcType.INSTANCE );
jdbcTypeRegistry.addDescriptor( OrdinalEnumJdbcType.INSTANCE );
}
@Override
public JdbcType resolveSqlTypeDescriptor(
String columnTypeName, int jdbcTypeCode, int precision, int scale, JdbcTypeRegistry jdbcTypeRegistry) {
switch ( jdbcTypeCode ) {
case Types.BIT:
return jdbcTypeRegistry.getDescriptor( Types.TINYINT );
case Types.OTHER:
if ( "GEOGRAPHY".equals( columnTypeName ) || "GEOGRAPHYPOINT".equals( columnTypeName ) ) {
jdbcTypeCode = VARCHAR;
}
break;
}
return super.resolveSqlTypeDescriptor( columnTypeName, jdbcTypeCode, precision, scale, jdbcTypeRegistry );
}
@Override
protected String columnType(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case BOOLEAN:
return "bit";
case TIMESTAMP:
return "datetime($p)";
case TIMESTAMP_WITH_TIMEZONE:
return "timestamp($p)";
case TIME_WITH_TIMEZONE:
return "time($p)";
case SqlTypes.NUMERIC:
return columnType( DECIMAL );
case FLOAT:
// Avoid using float type because
// SingleStore has potential inaccuracy when using the = or != comparison operators on FLOAT columns in WHERE clause
return columnType( DOUBLE );
case NCHAR:
return "char($l) character set utf8";
case NVARCHAR:
return "varchar($l) character set utf8";
case BLOB:
return "longblob";
case NCLOB:
return "longtext character set utf8";
case CLOB:
return "longtext";
default:
return super.columnType( sqlTypeCode );
}
}
@Override
public String castPattern(CastType from, CastType to) {
if ( CastType.FLOAT == to || CastType.DOUBLE == to || CastType.OTHER == to ) {
return "?1 :> ?2";
}
return super.castPattern( from, to );
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return Types.BIT;
}
@Override
protected String castType(int sqlTypeCode) {
switch ( sqlTypeCode ) {
case BOOLEAN:
case BIT:
//special case for casting to Boolean
return "unsigned";
case TINYINT:
case SMALLINT:
case INTEGER:
case BIGINT:
return "signed";
case CHAR:
case VARCHAR:
case LONG32VARCHAR:
return "char";
case NCHAR:
case NVARCHAR:
case LONG32NVARCHAR:
return "char character set utf8";
case BINARY:
case VARBINARY:
case LONG32VARBINARY:
return "binary";
}
return super.castType( sqlTypeCode );
}
@Override
public SizeStrategy getSizeStrategy() {
return sizeStrategy;
}
@Override
public int getFloatPrecision() {
//the maximum precision for 4 bytes
return 23;
}
@Override
public String currentTimestamp() {
return "current_timestamp(6)";
}
@Override
public long getFractionalSecondPrecisionInNanos() {
return 1_000; //microseconds
}
@Override
public long getDefaultLobLength() {
return Length.LONG32;
}
@Override
public int resolveSqlTypeLength(
String columnTypeName, int jdbcTypeCode, int precision, int scale, int displaySize) {
if ( jdbcTypeCode == Types.CHAR && precision <= 4 ) {
return displaySize;
}
else {
return precision;
}
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@Override
protected SqlAstTranslator buildTranslator(
SessionFactoryImplementor sessionFactory, Statement statement) {
return new SingleStoreSqlAstTranslator<>( sessionFactory, statement );
}
};
}
@Override
public SchemaNameResolver getSchemaNameResolver() {
return (connection, dialect) -> "";
}
@Override
public int getInExpressionCountLimit() {
return PARAM_LIST_SIZE_LIMIT;
}
/**
* The biggest size value that can be supplied as argument
*/
@Override
public int getMaxVarbinaryLength() {
return 65_533;
}
@Override
public int getMaxVarcharLength() {
return 21_844;
}
@Override
public String getNullColumnString(String columnType) {
if ( columnType.regionMatches( true, 0, "timestamp", 0, "timestamp".length() ) ) {
return " null";
}
return super.getNullColumnString( columnType );
}
/**
* Feature 'Check constraints' is not supported by SingleStore.
*/
@Override
public boolean supportsColumnCheck() {
return false;
}
/**
* Feature 'Check constraints' is not supported by SingleStore.
*/
public boolean supportsTableCheck() {
return false;
}
@Override
public int getDefaultDecimalPrecision() {
return 65;
}
@Override
public boolean doesRoundTemporalOnOverflow() {
return false;
}
@Override
public boolean supportsWindowFunctions() {
return true;
}
@Override
public boolean supportsRecursiveCTE() {
return true;
}
@Override
public boolean dropConstraints() {
return false;
}
@Override
public void appendLiteral(SqlAppender appender, String literal) {
appender.appendSql( '\'' );
for ( int i = 0; i < literal.length(); i++ ) {
final char c = literal.charAt( i );
if ( c == '\'' ) {
appender.appendSql( '\'' );
}
else if ( c == '\\' ) {
appender.appendSql( '\\' );
}
appender.appendSql( c );
}
appender.appendSql( '\'' );
}
@Override
public void appendDatetimeFormat(SqlAppender appender, String format) {
appender.appendSql( datetimeFormat( format ).result() );
}
public static Replacer datetimeFormat(String format) {
return new Replacer( format, "'", "" ).replace( "%", "%%" )
//year
.replace( "yyyy", "%Y" )
.replace( "yyy", "%Y" )
.replace( "yy", "%y" )
.replace( "y", "%Y" )
//month of year
.replace( "MMMM", "%M" )
.replace( "MMM", "%b" )
.replace( "MM", "%m" )
.replace( "M", "%c" )
//week of year
.replace( "ww", "%v" )
.replace( "w", "%v" )
//year for week
.replace( "YYYY", "%x" )
.replace( "YYY", "%x" )
.replace( "YY", "%x" )
.replace( "Y", "%x" )
//week of month
//????
//day of week
.replace( "EEEE", "%W" )
.replace( "EEE", "%a" )
.replace( "ee", "%w" )
.replace( "e", "%w" )
//day of month
.replace( "dd", "%d" )
.replace( "d", "%e" )
//day of year
.replace( "DDD", "%j" )
.replace( "DD", "%j" )
.replace( "D", "%j" )
//am pm
.replace( "a", "%p" )
//hour
.replace( "hh", "%I" )
.replace( "HH", "%H" )
.replace( "h", "%l" )
.replace( "H", "%k" )
//minute
.replace( "mm", "%i" )
.replace( "m", "%i" )
//second
.replace( "ss", "%S" )
.replace( "s", "%S" )
//fractional seconds
.replace( "SSSSSS", "%f" )
.replace( "SSSSS", "%f" )
.replace( "SSSS", "%f" )
.replace( "SSS", "%f" )
.replace( "SS", "%f" )
.replace( "S", "%f" );
}
@Override
public String getDropForeignKeyString() {
throw new UnsupportedOperationException( "SingleStore does not support foreign keys and referential integrity" );
}
@Override
public String getDropUniqueKeyString() {
return "drop index";
}
@Override
public String getAlterColumnTypeString(String columnName, String columnType, String columnDefinition) {
// no way to change just the column type, leaving other attributes intact
return "modify column " + columnName + " " + columnDefinition.trim();
}
/**
* SingleStore doesn't support modifying column type on columnstore tables.
* It only supports modifying column type on rowstore table.
*/
@Override
public boolean supportsAlterColumnType() {
return false;
}
@Override
public LimitHandler getLimitHandler() {
//also supports LIMIT n OFFSET m
return LimitLimitHandler.INSTANCE;
}
@Override
public char closeQuote() {
return '`';
}
@Override
public char openQuote() {
return '`';
}
@Override
public boolean canCreateCatalog() {
return true;
}
@Override
public String[] getCreateCatalogCommand(String catalogName) {
return new String[] { "create database " + catalogName };
}
@Override
public String[] getDropCatalogCommand(String catalogName) {
return new String[] { "drop database " + catalogName };
}
@Override
public boolean canCreateSchema() {
return false;
}
@Override
public String[] getCreateSchemaCommand(String schemaName) {
throw new UnsupportedOperationException(
"SingleStore does not support dropping creating/dropping schemas in the JDBC sense" );
}
@Override
public String[] getDropSchemaCommand(String schemaName) {
throw new UnsupportedOperationException(
"SingleStore does not support dropping creating/dropping schemas in the JDBC sense" );
}
@Override
public boolean supportsIfExistsBeforeTableName() {
return true;
}
@Override
public String getSelectGUIDString() {
return "select uuid()";
}
@Override
public boolean supportsCommentOn() {
return true;
}
@Override
public String getTableComment(String comment) {
return " comment='" + comment + "'";
}
@Override
public String getColumnComment(String comment) {
return " comment '" + comment + "'";
}
@Override
public NullOrdering getNullOrdering() {
return NullOrdering.SMALLEST;
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableMutationStrategy( TemporaryTable.createIdTable(
rootEntityDescriptor,
basename -> TemporaryTable.ID_TABLE_PREFIX + basename,
this,
runtimeModelCreationContext
), runtimeModelCreationContext.getSessionFactory() );
}
@Override
public SqmMultiTableInsertStrategy getFallbackSqmInsertStrategy(
EntityMappingType rootEntityDescriptor, RuntimeModelCreationContext runtimeModelCreationContext) {
return new LocalTemporaryTableInsertStrategy( TemporaryTable.createEntityTable(
rootEntityDescriptor,
name -> TemporaryTable.ENTITY_TABLE_PREFIX + name,
this,
runtimeModelCreationContext
), runtimeModelCreationContext.getSessionFactory() );
}
@Override
public TemporaryTableKind getSupportedTemporaryTableKind() {
return TemporaryTableKind.LOCAL;
}
@Override
public String getTemporaryTableCreateCommand() {
return "create temporary table if not exists";
}
//SingleStore throws an error on drop temporary table if there are uncommited statements within transaction.
//Just 'drop table' statement causes implicit commit, so using 'delete from'.
@Override
public String getTemporaryTableDropCommand() {
return "delete from";
}
@Override
public AfterUseAction getTemporaryTableAfterUseAction() {
return AfterUseAction.DROP;
}
@Override
public BeforeUseAction getTemporaryTableBeforeUseAction() {
return BeforeUseAction.CREATE;
}
@Override
public int getMaxAliasLength() {
return 64;
}
@Override
public int getMaxIdentifierLength() {
return 64;
}
@Override
public boolean supportsIsTrue() {
return true;
}
@Override
public boolean supportsCurrentTimestampSelection() {
return true;
}
@Override
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
@Override
public String getCurrentTimestampSelectString() {
return "select now()";
}
@Override
public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
throw new UnsupportedOperationException( "SingleStore does not support resultsets via stored procedures." );
}
@Override
public ResultSet getResultSet(CallableStatement ps) throws SQLException {
boolean isResultSet = ps.execute();
while ( !isResultSet && ps.getUpdateCount() != -1 ) {
isResultSet = ps.getMoreResults();
}
return ps.getResultSet();
}
@Override
public boolean supportsNullPrecedence() {
return false;
}
@Override
public boolean supportsLobValueChangePropagation() {
return false;
}
@Override
public boolean supportsSubqueryOnMutatingTable() {
return false;
}
@Override
public boolean supportsLockTimeouts() {
return false;
}
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
switch ( sqlException.getErrorCode() ) {
case 1205:
case 3572:
return new PessimisticLockException( message, sqlException, sql );
case 1207:
case 1206:
return new LockAcquisitionException( message, sqlException, sql );
case 1062:
String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
return new ConstraintViolationException(
message,
sqlException,
sql,
ConstraintViolationException.ConstraintKind.UNIQUE,
constraintName
);
}
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
if ( sqlState != null ) {
switch ( sqlState ) {
case "41000":
return new LockTimeoutException( message, sqlException, sql );
case "40001":
return new LockAcquisitionException( message, sqlException, sql );
}
}
return null;
};
}
@Override
public NameQualifierSupport getNameQualifierSupport() {
return NameQualifierSupport.CATALOG;
}
@Override
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
throws SQLException {
builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.MIXED );
builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED );
return super.buildIdentifierHelper( builder, dbMetaData );
}
@Override
public String getAddForeignKeyConstraintString(
String constraintName,
String[] foreignKey,
String referencedTable,
String[] primaryKey,
boolean referencesPrimaryKey) {
throw new UnsupportedOperationException( "SingleStore does not support foreign keys and referential integrity." );
}
@Override
public String getAddForeignKeyConstraintString(
String constraintName, String foreignKeyDefinition) {
throw new UnsupportedOperationException( "SingleStore does not support foreign keys and referential integrity." );
}
@Override
public String getAddPrimaryKeyConstraintString(String constraintName) {
throw new UnsupportedOperationException( "SingleStore does not support altering primary key." );
}
@Override
public String getWriteLockString(String aliases, int timeout) {
return getForUpdateString( aliases );
}
@Override
public String getForUpdateSkipLockedString(String aliases) {
return getForUpdateString();
}
@Override
public String getForUpdateNowaitString(String aliases) {
return getForUpdateString();
}
@Override
public String getForUpdateString() {
return isForUpdateLockingEnabled ? super.getForUpdateString() : "";
}
@Override
public boolean supportsOffsetInSubquery() {
return true;
}
@Override
public boolean supportsPartitionBy() {
return true;
}
@Override
public boolean supportsWait() {
return false;
}
@Override
protected void registerDefaultKeywords() {
super.registerDefaultKeywords();
registerKeyword( "key" );
}
@Override
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP;
}
@Override
public DmlTargetColumnQualifierSupport getDmlTargetColumnQualifierSupport() {
return DmlTargetColumnQualifierSupport.TABLE_ALIAS;
}
@Override
public boolean supportsFromClauseInUpdate() {
return true;
}
@Override
public boolean supportsCircularCascadeDeleteConstraints() {
return false;
}
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
return MySQLIdentityColumnSupport.INSTANCE;
}
@Override
public boolean isJdbcLogWarningsEnabledByDefault() {
return false;
}
@Override
public boolean supportsCascadeDelete() {
return false;
}
@Override
public boolean supportsNonQueryWithCTE() {
return true;
}
@Override
public Exporter getSequenceExporter() {
return NOOP_EXPORTER;
}
/**
* SingleStore does not support foreign keys and referential integrity
*/
@Override
public Exporter getForeignKeyExporter() {
return NOOP_EXPORTER;
}
@Override
public UniqueDelegate getUniqueDelegate() {
return NOOP_UNIQUE_DELEGATE;
}
/**
* A no-op {@link Exporter} which is responsible for returning empty Create and Drop SQL strings.
*/
static class EmptyExporter implements Exporter {
@Override
public String[] getSqlCreateStrings(T exportable, Metadata metadata, SqlStringGenerationContext context) {
return ArrayHelper.EMPTY_STRING_ARRAY;
}
@Override
public String[] getSqlDropStrings(T exportable, Metadata metadata, SqlStringGenerationContext context) {
return ArrayHelper.EMPTY_STRING_ARRAY;
}
}
/**
* Because of hibernate requires that entity tables have primary key separate unique keys are restricted.
* SingleStore restrictions:
* - Primary key in SingleStore table is unique key and shard key
* - SingleStore table allows only single shard key
* - SingleStore unique keys must contain all columns of the shard key: Unique Key restrictions.
* - Shard key fields cannot be updated (or altered) so they must be fields that never change
*/
static class DoNothingUniqueDelegate implements UniqueDelegate {
@Override
public String getColumnDefinitionUniquenessFragment(Column column, SqlStringGenerationContext context) {
return "";
}
@Override
public String getTableCreationUniqueConstraintsFragment(Table table, SqlStringGenerationContext context) {
return "";
}
@Override
public String getAlterTableToAddUniqueKeyCommand(
UniqueKey uniqueKey, Metadata metadata, SqlStringGenerationContext context) {
return "";
}
@Override
public String getAlterTableToDropUniqueKeyCommand(
UniqueKey uniqueKey, Metadata metadata, SqlStringGenerationContext context) {
return "";
}
}
/**
* The default table type in SingleStore is 'columnstore'. The default can be changed to 'rowstore' by updating the
* 'default_table_type' engine variable to 'rowstore' or specify explicitly by property : 'hibernate.dialect.singlestore.table_type'.
* Choosing a Table Storage Type
*/
public enum SingleStoreTableType {
COLUMNSTORE, ROWSTORE;
public static SingleStoreTableType fromValue(String value) {
return Arrays.stream( values() )
.filter( v -> v.name().equalsIgnoreCase( value.trim() ) )
.findAny()
.orElseThrow( () -> new IllegalArgumentException( "Wrong table type" ) );
}
}
public SingleStoreTableType getExplicitTableType() {
return explicitTableType;
}
public boolean isForUpdateLockingEnabled() {
return isForUpdateLockingEnabled;
}
/**
* Specifies SingleStore explicit table type.
*
* @settingDefault {@code null}
*/
public static final String SINGLE_STORE_TABLE_TYPE = "hibernate.dialect.singlestore.table_type";
/**
* Specifies SingleStore FOR UPDATE clause lock enable.
*
* @settingDefault {@code false}
*/
public static final String SINGLE_STORE_FOR_UPDATE_LOCK_ENABLED = "hibernate.dialect.singlestore.for_update_lock_enabled";
@Override
public String getDual() {
return "dual";
}
}