org.hibernate.community.dialect.CacheDialect 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.LockMode;
import org.hibernate.cfg.Environment;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.dialect.function.CommonFunctionFactory;
import org.hibernate.community.dialect.identity.CacheIdentityColumnSupport;
import org.hibernate.dialect.identity.IdentityColumnSupport;
import org.hibernate.dialect.lock.*;
import org.hibernate.dialect.pagination.LimitHandler;
import org.hibernate.dialect.pagination.TopLimitHandler;
import org.hibernate.community.dialect.sequence.CacheSequenceSupport;
import org.hibernate.dialect.sequence.SequenceSupport;
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
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.persister.entity.Lockable;
import org.hibernate.query.IntervalType;
import org.hibernate.query.TemporalUnit;
import org.hibernate.query.spi.QueryEngine;
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.StandardBasicTypes;
import org.hibernate.type.descriptor.jdbc.JdbcType;
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
import java.sql.CallableStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import jakarta.persistence.TemporalType;
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
/**
* Dialect for Intersystems Caché SQL 2007.1 and above.
*
* @author Jonathan Levinson
*/
public class CacheDialect extends Dialect {
public CacheDialect() {
super();
// Note: For object <-> SQL datatype mappings see:
// Configuration Manager > Advanced > SQL > System DDL Datatype Mappings
registerColumnType( Types.BOOLEAN, "bit" );
//no explicit precision
registerColumnType( Types.TIMESTAMP, "timestamp" );
registerColumnType( Types.TIMESTAMP_WITH_TIMEZONE, "timestamp" );
registerColumnType( Types.BLOB, "image" );
registerColumnType( Types.CLOB, "text" );
getDefaultProperties().setProperty( Environment.USE_STREAMS_FOR_BINARY, "false" );
getDefaultProperties().setProperty( Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE );
getDefaultProperties().setProperty( Environment.USE_SQL_COMMENTS, "false" );
}
public CacheDialect(DialectResolutionInfo info) {
this();
registerKeywords( info );
}
private static void useJdbcEscape(QueryEngine queryEngine, String name) {
//Yep, this seems to be truly necessary for certain functions
queryEngine.getSqmFunctionRegistry().wrapInJdbcEscape(
name,
queryEngine.getSqmFunctionRegistry().findFunctionDescriptor(name)
);
}
@Override
public int getVersion() {
return 0;
}
@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 Types.BIT;
}
@Override
public int getDefaultDecimalPrecision() {
//the largest *meaningful* value
return 19;
}
@Override
public void initializeFunctionRegistry(QueryEngine queryEngine) {
super.initializeFunctionRegistry( queryEngine );
CommonFunctionFactory.repeat( queryEngine );
CommonFunctionFactory.trim2( queryEngine );
CommonFunctionFactory.substr( queryEngine );
//also natively supports ANSI-style substring()
CommonFunctionFactory.concat_pipeOperator( queryEngine );
CommonFunctionFactory.cot( queryEngine );
CommonFunctionFactory.log10( queryEngine );
CommonFunctionFactory.log( queryEngine );
CommonFunctionFactory.pi( queryEngine );
CommonFunctionFactory.space( queryEngine );
CommonFunctionFactory.hourMinuteSecond( queryEngine );
CommonFunctionFactory.yearMonthDay( queryEngine );
CommonFunctionFactory.weekQuarter( queryEngine );
CommonFunctionFactory.daynameMonthname( queryEngine );
CommonFunctionFactory.toCharNumberDateTimestamp( queryEngine );
CommonFunctionFactory.truncate( queryEngine );
CommonFunctionFactory.dayofweekmonthyear( queryEngine );
CommonFunctionFactory.repeat_replicate( queryEngine );
CommonFunctionFactory.datepartDatename( queryEngine );
CommonFunctionFactory.ascii( queryEngine );
CommonFunctionFactory.chr_char( queryEngine );
CommonFunctionFactory.nowCurdateCurtime( queryEngine );
CommonFunctionFactory.sysdate( queryEngine );
CommonFunctionFactory.stddev( queryEngine );
CommonFunctionFactory.stddevPopSamp( queryEngine );
CommonFunctionFactory.variance( queryEngine );
CommonFunctionFactory.varPopSamp( queryEngine );
CommonFunctionFactory.lastDay( queryEngine );
queryEngine.getSqmFunctionRegistry().registerBinaryTernaryPattern(
"locate",
queryEngine.getTypeConfiguration().getBasicTypeRegistry().resolve( StandardBasicTypes.INTEGER ),
"$find(?2,?1)",
"$find(?2,?1,?3)"
).setArgumentListSignature("(pattern, string[, start])");
CommonFunctionFactory.bitLength_pattern( queryEngine, "($length(?1)*8)" );
useJdbcEscape(queryEngine, "sin");
useJdbcEscape(queryEngine, "cos");
useJdbcEscape(queryEngine, "tan");
useJdbcEscape(queryEngine, "asin");
useJdbcEscape(queryEngine, "acos");
useJdbcEscape(queryEngine, "atan");
useJdbcEscape(queryEngine, "atan2");
useJdbcEscape(queryEngine, "exp");
useJdbcEscape(queryEngine, "log");
useJdbcEscape(queryEngine, "log10");
useJdbcEscape(queryEngine, "pi");
useJdbcEscape(queryEngine, "truncate");
useJdbcEscape(queryEngine, "left");
useJdbcEscape(queryEngine, "right");
useJdbcEscape(queryEngine, "hour");
useJdbcEscape(queryEngine, "minute");
useJdbcEscape(queryEngine, "second");
useJdbcEscape(queryEngine, "week");
useJdbcEscape(queryEngine, "quarter");
useJdbcEscape(queryEngine, "dayname");
useJdbcEscape(queryEngine, "monthname");
useJdbcEscape(queryEngine, "dayofweek");
useJdbcEscape(queryEngine, "dayofmonth");
useJdbcEscape(queryEngine, "dayofyear");
}
@Override
public String extractPattern(TemporalUnit unit) {
return "datepart(?1,?2)";
}
@Override
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
return "dateadd(millisecond,(?2)/1e6,?3)";
default:
return "dateadd(?1,?2,?3)";
}
}
@Override
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
switch (unit) {
case NANOSECOND:
case NATIVE:
return "datediff(millisecond,?2,?3)*1e6";
default:
return "datediff(?1,?2,?3)";
}
}
// DDL support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean qualifyIndexName() {
// Do we need to qualify index names with the schema name?
return false;
}
@Override
@SuppressWarnings("StringBufferReplaceableByString")
public String getAddForeignKeyConstraintString(
String constraintName,
String[] foreignKey,
String referencedTable,
String[] primaryKey,
boolean referencesPrimaryKey) {
// The syntax used to add a foreign key constraint to a table.
return new StringBuilder( 300 )
.append( " ADD CONSTRAINT " )
.append( constraintName )
.append( " FOREIGN KEY " )
.append( constraintName )
.append( " (" )
.append( String.join( ", ", foreignKey ) )
.append( ") REFERENCES " )
.append( referencedTable )
.append( " (" )
.append( String.join( ", ", primaryKey ) )
.append( ") " )
.toString();
}
@Override
public boolean hasSelfReferentialForeignKeyBug() {
return true;
}
@Override
public String getNativeIdentifierGeneratorStrategy() {
return "identity";
}
// IDENTITY support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public IdentityColumnSupport getIdentityColumnSupport() {
return new CacheIdentityColumnSupport();
}
// SEQUENCE support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public SequenceSupport getSequenceSupport() {
return CacheSequenceSupport.INSTANCE;
}
public String getQuerySequencesString() {
return "select name from InterSystems.Sequences";
}
// lock acquisition support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsOuterJoinForUpdate() {
return false;
}
@Override
public LockingStrategy getLockingStrategy(Lockable lockable, LockMode lockMode) {
// InterSystems Cache' does not current support "SELECT ... FOR UPDATE" syntax...
// Set your transaction mode to READ_COMMITTED before using
switch (lockMode) {
case PESSIMISTIC_FORCE_INCREMENT:
return new PessimisticForceIncrementLockingStrategy(lockable, lockMode);
case PESSIMISTIC_WRITE:
return new PessimisticWriteUpdateLockingStrategy(lockable, lockMode);
case PESSIMISTIC_READ:
return new PessimisticReadUpdateLockingStrategy(lockable, lockMode);
case OPTIMISTIC:
return new OptimisticLockingStrategy(lockable, lockMode);
case OPTIMISTIC_FORCE_INCREMENT:
return new OptimisticForceIncrementLockingStrategy(lockable, lockMode);
}
if ( lockMode.greaterThan( LockMode.READ ) ) {
return new UpdateLockingStrategy( lockable, lockMode );
}
else {
return new SelectLockingStrategy( lockable, lockMode );
}
}
@Override
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
return new StandardSqlAstTranslatorFactory() {
@Override
protected SqlAstTranslator buildTranslator(
SessionFactoryImplementor sessionFactory, Statement statement) {
return new CacheSqlAstTranslator<>( sessionFactory, statement );
}
};
}
// LIMIT support (ala TOP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public LimitHandler getLimitHandler() {
return TopLimitHandler.INSTANCE;
}
// callable statement support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public int registerResultSetOutParameter(CallableStatement statement, int col) throws SQLException {
return col;
}
@Override
public ResultSet getResultSet(CallableStatement ps) throws SQLException {
ps.execute();
return (ResultSet) ps.getObject( 1 );
}
// miscellaneous support ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public String getLowercaseFunction() {
// The name of the SQL function that transforms a string to lowercase
return "lower";
}
@Override
public String getNullColumnString() {
// The keyword used to specify a nullable column.
return " null";
}
@Override
public String getNoColumnsInsertString() {
// The keyword used to insert a row without specifying
// any column values
return " default values";
}
@Override
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
return (sqlException, message, sql) -> {
String sqlStateClassCode = JdbcExceptionHelper.extractSqlStateClassCode( sqlException );
if ( sqlStateClassCode != null ) {
int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
if ( errorCode >= 119 && errorCode <= 127 && errorCode != 126 ) {
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName(sqlException);
return new ConstraintViolationException( message, sqlException, sql, constraintName );
}
if ( sqlStateClassCode.equals("22")
|| sqlStateClassCode.equals("21")
|| sqlStateClassCode.equals("02") ) {
return new DataException( message, sqlException, sql );
}
}
return null; // allow other delegates the chance to look
};
}
@Override
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
return EXTRACTOR;
}
private static final ViolatedConstraintNameExtractor EXTRACTOR =
new TemplatedViolatedConstraintNameExtractor( sqle ->
extractUsingTemplate( "constraint (", ") violated", sqle.getMessage() )
);
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public boolean supportsOrderByInSubquery() {
// This is just a guess
return false;
}
@Override
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
return false;
}
@Override
public void appendDatetimeFormat(SqlAppender appender, String format) {
//I don't think Cache needs FM
appender.appendSql( OracleDialect.datetimeFormat( format, false, false ).result() );
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy