org.hibernate.community.dialect.IngresDialect Maven / Gradle / Ivy
/*
* 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 .
*/
package org.hibernate.community.dialect;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.MySQLDialect;
import org.hibernate.dialect.TimeZoneSupport;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.community.dialect.identity.Ingres10IdentityColumnSupport;
import org.hibernate.community.dialect.identity.Ingres9IdentityColumnSupport;
import org.hibernate.community.dialect.pagination.FirstLimitHandler;
import org.hibernate.community.dialect.pagination.IngresLimitHandler;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.sequence.ANSISequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.spi.LoadQueryInfluencers;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
import org.hibernate.query.FetchClauseType;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
import org.hibernate.query.spi.QueryOptions;
import org.hibernate.query.spi.QueryParameterBindings;
import org.hibernate.query.sqm.internal.DomainParameterXref;
import org.hibernate.query.sqm.mutation.internal.idtable.AfterUseAction;
import org.hibernate.query.sqm.mutation.internal.idtable.GlobalTemporaryTableStrategy;
import org.hibernate.query.sqm.mutation.internal.idtable.IdTable;
import org.hibernate.query.sqm.mutation.internal.idtable.TempIdTableExporter;
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
import org.hibernate.query.sqm.sql.SqmTranslator;
import org.hibernate.query.sqm.sql.SqmTranslatorFactory;
import org.hibernate.query.sqm.sql.StandardSqmTranslatorFactory;
import org.hibernate.query.sqm.tree.select.SqmSelectStatement;
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.SqlAstCreationContext;
import org.hibernate.sql.ast.spi.StandardSqlAstTranslatorFactory;
import org.hibernate.sql.ast.tree.Statement;
import org.hibernate.sql.ast.tree.select.SelectStatement;
import org.hibernate.sql.exec.spi.JdbcOperation;
import org.hibernate.tool.schema.extract.internal.SequenceNameExtractorImpl;
import org.hibernate.tool.schema.extract.spi.SequenceInformationExtractor;
import org.hibernate.type.BasicType;
import org.hibernate.type.BasicTypeRegistry;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import java.sql.Types;
import jakarta.persistence.TemporalType;
/**
* An SQL dialect for Ingres 9.2.
*
* Known limitations:
* -
* Only supports simple constants or columns on the left side of an IN,
* making {@code (1,2,3) in (...)} or {@code (subselect) in (...)} non-supported.
*
* -
* Supports only 39 digits in decimal.
*
* -
* Explicitly set USE_GET_GENERATED_KEYS property to false.
*
* -
* Perform string casts to varchar; removes space padding.
*
*
*
* @author Ian Booth
* @author Bruce Lunsford
* @author Max Rydahl Andersen
* @author Raymond Fan
*/
public class IngresDialect extends Dialect {
private final LimitHandler limitHandler;
private final int version;
private final SequenceSupport sequenceSupport;
public IngresDialect(DialectResolutionInfo info) {
this( info.getDatabaseMajorVersion() * 100 + info.getDatabaseMinorVersion() * 10 );
registerKeywords( info );
}
public IngresDialect() {
this(920);
}
/**
* Constructs a IngresDialect
*/
public IngresDialect(int version) {
super();
this.version = version;
if ( getVersion() < 1000 ) {
registerColumnType( Types.BOOLEAN, "tinyint" );
}
else {
registerColumnType( Types.BOOLEAN, "boolean" );
}
registerColumnType( Types.NUMERIC, "decimal($p, $s)" ); //Ingres has no 'numeric' type
final int maxStringLength = 32_000;
registerColumnType( Types.BINARY, maxStringLength, "byte($l)" );
registerColumnType( Types.VARBINARY, maxStringLength, "varbyte($l)" );
//note: 'long byte' is a synonym for 'blob'
registerColumnType( Types.VARBINARY, "long byte($l)" );
//TODO: should we be using nchar/nvarchar/long nvarchar
// here? I think Ingres char/varchar types don't
// support Unicode. Copy what AbstractHANADialect
// does with a Hibernate property to config this.
registerColumnType( Types.CHAR, maxStringLength, "char($l)" );
registerColumnType( Types.VARCHAR, maxStringLength, "varchar($l)" );
//note: 'long varchar' is a synonym for 'clob'
registerColumnType( Types.VARCHAR, "long varchar($l)" );
registerColumnType( Types.NCHAR, maxStringLength, "nchar($l)" );
registerColumnType( Types.NVARCHAR, maxStringLength, "nvarchar($l)" );
//note: 'long nvarchar' is a synonym for 'nclob'
registerColumnType( Types.NVARCHAR, "long nvarchar($l)" );
if ( getVersion() >= 930 ) {
// Not completely necessary, given that Ingres
// can be configured to set DATE = ANSIDATE
registerColumnType( Types.DATE, "ansidate" );
}
// Ingres driver supports getGeneratedKeys but only in the following
// form:
// The Ingres DBMS returns only a single table key or a single object
// key per insert statement. Ingres does not return table and object
// keys for INSERT AS SELECT statements. Depending on the keys that are
// produced by the statement executed, auto-generated key parameters in
// execute(), executeUpdate(), and prepareStatement() methods are
// ignored and getGeneratedKeys() returns a result-set containing no
// rows, a single row with one column, or a single row with two columns.
// Ingres JDBC Driver returns table and object keys as BINARY values.
getDefaultProperties().setProperty( Environment.USE_GET_GENERATED_KEYS, "false" );
if ( getVersion() < 1000 ) {
// There is no support for a native boolean type that accepts values
// of true, false or unknown. Using the tinyint type requires
// substitutions of true and false.
getDefaultProperties().setProperty( Environment.QUERY_SUBSTITUTIONS, "true=1,false=0" );
}
limitHandler = getVersion() < 930 ? FirstLimitHandler.INSTANCE : IngresLimitHandler.INSTANCE;
sequenceSupport = new ANSISequenceSupport() {
@Override
public boolean supportsPooledSequences() {
return getVersion() >= 930;
}
};
}
@Override
public int getVersion() {
return version;
}
@Override
public JdbcType resolveSqlTypeDescriptor(
String columnTypeName,
int jdbcTypeCode,
int precision,
int scale,
JdbcTypeRegistry jdbcTypeRegistry) {
if ( jdbcTypeCode == Types.BIT ) {
return jdbcTypeRegistry.getDescriptor( Types.BOOLEAN );
}
return super.resolveSqlTypeDescriptor(
columnTypeName,
jdbcTypeCode,
precision,
scale,
jdbcTypeRegistry
);
}
@Override
public int getPreferredSqlTypeCodeForBoolean() {
return getVersion() < 1000 ? Types.BIT : Types.BOOLEAN;
}
@Override
public void appendBooleanValueString(SqlAppender appender, boolean bool) {
if ( getVersion() < 1000 ) {
appender.appendSql( bool ? '1' : '0' );
}
else {
appender.appendSql( bool );
}
}
@Override
public int getDefaultDecimalPrecision() {
//the maximum
return 39;
}
@Override
public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry( queryEngine );
final BasicTypeRegistry basicTypeRegistry = queryEngine.getTypeConfiguration().getBasicTypeRegistry();
final BasicType stringType = basicTypeRegistry.resolve( StandardBasicTypes.STRING );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Common functions
CommonFunctionFactory.log( queryEngine );
CommonFunctionFactory.rand( queryEngine );
CommonFunctionFactory.soundex( queryEngine );
CommonFunctionFactory.octetLength( queryEngine );
CommonFunctionFactory.repeat( queryEngine );
CommonFunctionFactory.trim2( queryEngine );
CommonFunctionFactory.trunc( queryEngine );
CommonFunctionFactory.truncate( queryEngine );
CommonFunctionFactory.initcap( queryEngine );
CommonFunctionFactory.yearMonthDay( queryEngine );
CommonFunctionFactory.hourMinuteSecond( queryEngine );
CommonFunctionFactory.dayofweekmonthyear( queryEngine );
CommonFunctionFactory.weekQuarter( queryEngine );
CommonFunctionFactory.lastDay( queryEngine );
CommonFunctionFactory.concat_pipeOperator( queryEngine );
CommonFunctionFactory.substr( queryEngine );
CommonFunctionFactory.monthsBetween( queryEngine );
CommonFunctionFactory.substring_substr( queryEngine );
//also natively supports ANSI-style substring()
CommonFunctionFactory.ascii( queryEngine );
CommonFunctionFactory.char_chr( queryEngine );
CommonFunctionFactory.sysdate( queryEngine );
CommonFunctionFactory.position( queryEngine );
CommonFunctionFactory.format_dateFormat( queryEngine );
CommonFunctionFactory.dateTrunc( queryEngine );
CommonFunctionFactory.bitLength_pattern( queryEngine, "octet_length(hex(?1))*4" );
final BasicType integerType = queryEngine.getTypeConfiguration().getBasicTypeRegistry()
.resolve( StandardBasicTypes.INTEGER );
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate",
integerType,
"position(?1 in ?2)",
"(position(?1 in substring(?2 from ?3))+(?3)-1)"
).setArgumentListSignature("(pattern, string[, start])");
queryEngine.getSqmFunctionRegistry().registerPattern( "extract", "date_part('?1',?2)", integerType );
CommonFunctionFactory.bitandorxornot_bitAndOrXorNot(queryEngine);
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "squeeze" )
.setExactArgumentCount( 1 )
.setInvariantType( stringType )
.register();
}
@Override
public SqmTranslatorFactory getSqmTranslatorFactory() {
return new StandardSqmTranslatorFactory() {
@Override
public SqmTranslator createSelectTranslator(
SqmSelectStatement> sqmSelectStatement,
QueryOptions queryOptions,
DomainParameterXref domainParameterXref,
QueryParameterBindings domainParameterBindings,
LoadQueryInfluencers loadQueryInfluencers,
SqlAstCreationContext creationContext) {
return new IngresSqmToSqlAstConverter<>(
sqmSelectStatement,
queryOptions,
domainParameterXref,
domainParameterBindings,
loadQueryInfluencers,
creationContext
);
}
};
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@Override
protected SqlAstTranslator buildTranslator(
SessionFactoryImplementor sessionFactory, Statement statement) {
return new IngresSqlAstTranslator<>( sessionFactory, statement );
}
};
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
return "timestampadd(?1,?2,?3)";
}
@Override
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
return "timestampdiff(?1,?2,?3)";
}
@Override
public TimeZoneSupport getTimeZoneSupport() {
return TimeZoneSupport.NATIVE;
}
@Override
public String getSelectGUIDString() {
return "select uuid_to_char(uuid_create())";
}
@Override
public boolean dropConstraints() {
return false;
}
@Override
public String getNullColumnString() {
return " with null";
}
// SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public SequenceSupport getSequenceSupport() {
return sequenceSupport;
}
@Override
public String getQuerySequencesString() {
return getVersion() < 930
? "select seq_name from iisequence"
: "select seq_name from iisequences";
}
@Override
public SequenceInformationExtractor getSequenceInformationExtractor() {
return SequenceNameExtractorImpl.INSTANCE;
}
@Override
public String getLowercaseFunction() {
return "lowercase";
}
@Override
public LimitHandler getLimitHandler() {
return limitHandler;
}
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
if ( getVersion() >= 1000 ) {
return new Ingres10IdentityColumnSupport();
}
else if (getVersion() >= 930) {
return new Ingres9IdentityColumnSupport();
}
else {
return super.getIdentityColumnSupport();
}
}
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* FOR UPDATE only supported for cursors
*
* @return the empty string
*/
@Override
public String getForUpdateString() {
return "";
}
// current timestamp support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean isCurrentTimestampSelectStringCallable() {
return false;
}
@Override
public boolean supportsCurrentTimestampSelection() {
return getVersion() >= 930;
}
@Override
public SqmMultiTableMutationStrategy getFallbackSqmMutationStrategy(
EntityMappingType rootEntityDescriptor,
RuntimeModelCreationContext runtimeModelCreationContext) {
return new GlobalTemporaryTableStrategy(
new IdTable( rootEntityDescriptor, name -> "session." + name, this, runtimeModelCreationContext ),
() -> new TempIdTableExporter( false, this::getTypeName ) {
@Override
protected String getCreateOptions() {
return "on commit preserve rows with norecovery";
}
@Override
protected String getCreateCommand() {
return "declare global temporary table";
}
},
AfterUseAction.CLEAN,
runtimeModelCreationContext.getSessionFactory()
);
}
// union subclass support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsUnionAll() {
return getVersion() >= 930;
}
@Override
public boolean supportsUnionInSubquery() {
// At least not according to HHH-3637
return false;
}
@Override
public boolean supportsSubqueryInSelect() {
// At least according to HHH-4961
return getVersion() >= 1000;
}
@Override
public boolean supportsOrderByInSubquery() {
// This is just a guess
return false;
}
// Informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean doesReadCommittedCauseWritersToBlockReaders() {
return getVersion() >= 930;
}
@Override
public boolean doesRepeatableReadCauseReadersToBlockWriters() {
return getVersion() >= 930;
}
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsSubselectAsInPredicateLHS() {
return false;
}
@Override
public boolean supportsExpectedLobUsagePattern() {
return false;
}
@Override
public boolean supportsTupleDistinctCounts() {
return false;
}
@Override
public void appendDatetimeFormat(SqlAppender appender, String format) {
appender.appendSql( MySQLDialect.datetimeFormat( format ).result() );
}
@Override
public String translateExtractField(TemporalUnit unit) {
switch ( unit ) {
case DAY_OF_MONTH: return "day";
case DAY_OF_YEAR: return "doy";
case DAY_OF_WEEK: return "dow";
case WEEK: return "iso_week";
default: return super.translateExtractField( unit );
}
}
@Override
public boolean supportsFetchClause(FetchClauseType type) {
return getVersion() >= 930;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy