HHH-15192 - Remove support for Sybase ASE versions older than 16
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
b57f4a6b12
commit
11fb9440ef
|
@ -0,0 +1,724 @@
|
|||
/*
|
||||
* 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.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.RowLockStrategy;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.TopLimitHandler;
|
||||
import org.hibernate.engine.jdbc.Size;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
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.query.sqm.IntervalType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.service.ServiceRegistry;
|
||||
import org.hibernate.sql.ForUpdateFragment;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
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.descriptor.java.JavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TimestampJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.TinyIntJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
import org.hibernate.type.descriptor.sql.internal.CapacityDependentDdlType;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtractor.extractUsingTemplate;
|
||||
import static org.hibernate.type.SqlTypes.BIGINT;
|
||||
import static org.hibernate.type.SqlTypes.BOOLEAN;
|
||||
import static org.hibernate.type.SqlTypes.DATE;
|
||||
import static org.hibernate.type.SqlTypes.TIME;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP;
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 11.9 and above.
|
||||
*/
|
||||
public class SybaseASELegacyDialect extends SybaseLegacyDialect {
|
||||
|
||||
private final SizeStrategy sizeStrategy = new SizeStrategyImpl() {
|
||||
@Override
|
||||
public Size resolveSize(
|
||||
JdbcType jdbcType,
|
||||
JavaType<?> javaType,
|
||||
Integer precision,
|
||||
Integer scale,
|
||||
Long length) {
|
||||
switch ( jdbcType.getDefaultSqlTypeCode() ) {
|
||||
case Types.FLOAT:
|
||||
// Sybase ASE allows FLOAT with a precision up to 48
|
||||
if ( precision != null ) {
|
||||
return Size.precision( Math.min( Math.max( precision, 1 ), 48 ) );
|
||||
}
|
||||
}
|
||||
return super.resolveSize( jdbcType, javaType, precision, scale, length );
|
||||
}
|
||||
};
|
||||
|
||||
private final boolean ansiNull;
|
||||
|
||||
public SybaseASELegacyDialect() {
|
||||
this( DatabaseVersion.make( 11 ) );
|
||||
}
|
||||
|
||||
public SybaseASELegacyDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
ansiNull = false;
|
||||
}
|
||||
|
||||
public SybaseASELegacyDialect(DialectResolutionInfo info) {
|
||||
super(info);
|
||||
ansiNull = isAnsiNull( info.getDatabaseMetadata() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String columnType(int sqlTypeCode) {
|
||||
switch ( sqlTypeCode ) {
|
||||
case BOOLEAN:
|
||||
// On Sybase ASE, the 'bit' type cannot be null,
|
||||
// and cannot have indexes (while we don't use
|
||||
// tinyint to store signed bytes, we can use it
|
||||
// to store boolean values)
|
||||
return "tinyint";
|
||||
case BIGINT:
|
||||
// Sybase ASE didn't introduce 'bigint' until version 15.0
|
||||
return getVersion().isBefore( 15 ) ? "numeric(19,0)" : super.columnType( sqlTypeCode );
|
||||
case DATE:
|
||||
return getVersion().isSameOrAfter( 12 ) ? "date" : super.columnType( sqlTypeCode );
|
||||
case TIME:
|
||||
return getVersion().isSameOrAfter( 12 ) ? "time" : super.columnType( sqlTypeCode );
|
||||
}
|
||||
return super.columnType( sqlTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
|
||||
// According to Wikipedia bigdatetime and bigtime were added in 15.5
|
||||
// But with jTDS we can't use them as the driver can't handle the types
|
||||
if ( getVersion().isSameOrAfter( 15, 5 ) && !jtdsDriver ) {
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( DATE, "bigdatetime", "bigdatetime", this )
|
||||
.withTypeCapacity( 3, "datetime" )
|
||||
.build()
|
||||
);
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( TIME, "bigdatetime", "bigdatetime", this )
|
||||
.withTypeCapacity( 3, "datetime" )
|
||||
.build()
|
||||
);
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( TIMESTAMP, "bigdatetime", "bigdatetime", this )
|
||||
.withTypeCapacity( 3, "datetime" )
|
||||
.build()
|
||||
);
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( TIMESTAMP_WITH_TIMEZONE, "bigdatetime", "bigdatetime", this )
|
||||
.withTypeCapacity( 3, "datetime" )
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxVarcharLength() {
|
||||
// the maximum length of a VARCHAR or VARBINARY
|
||||
// column depends on the page size and ASE version
|
||||
// and is actually a limit on the whole row length,
|
||||
// not the individual column length -- anyway, the
|
||||
// largest possible page size is 16k, so that's a
|
||||
// hard upper limit
|
||||
return 16_384;
|
||||
}
|
||||
|
||||
private static boolean isAnsiNull(DatabaseMetaData databaseMetaData) {
|
||||
if ( databaseMetaData != null ) {
|
||||
try (java.sql.Statement s = databaseMetaData.getConnection().createStatement() ) {
|
||||
final ResultSet rs = s.executeQuery( "SELECT @@options" );
|
||||
if ( rs.next() ) {
|
||||
final byte[] optionBytes = rs.getBytes( 1 );
|
||||
// By trial and error, enabling and disabling ansinull revealed that this bit is the indicator
|
||||
return ( optionBytes[4] & 2 ) == 2;
|
||||
}
|
||||
}
|
||||
catch (SQLException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAnsiNullOn() {
|
||||
return ansiNull;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFloatPrecision() {
|
||||
return 15;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDoublePrecision() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SizeStrategy getSizeStrategy() {
|
||||
return sizeStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||
return new StandardSqlAstTranslatorFactory() {
|
||||
@Override
|
||||
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
return new SybaseASELegacySqlAstTranslator<>( sessionFactory, statement );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The Sybase ASE {@code BIT} type does not allow
|
||||
* null values, so we don't use it.
|
||||
*
|
||||
* @return false
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsBitType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
return getVersion().isSameOrAfter( 16, 3 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||
.getJdbcTypeRegistry();
|
||||
jdbcTypeRegistry.addDescriptor( Types.BOOLEAN, TinyIntJdbcType.INSTANCE );
|
||||
// At least the jTDS driver does not support this type code
|
||||
if ( jtdsDriver ) {
|
||||
jdbcTypeRegistry.addDescriptor( Types.TIMESTAMP_WITH_TIMEZONE, TimestampJdbcType.INSTANCE );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int resolveSqlTypeLength(
|
||||
String columnTypeName,
|
||||
int jdbcTypeCode,
|
||||
int precision,
|
||||
int scale,
|
||||
int displaySize) {
|
||||
// Sybase ASE reports the "actual" precision in the display size
|
||||
switch ( jdbcTypeCode ) {
|
||||
case Types.REAL:
|
||||
case Types.DOUBLE:
|
||||
return displaySize;
|
||||
}
|
||||
return super.resolveSqlTypeLength( columnTypeName, jdbcTypeCode, precision, scale, displaySize );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentDate() {
|
||||
return "current_date()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTime() {
|
||||
return "current_time()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTimestamp() {
|
||||
return "current_bigdatetime()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||
//TODO!!
|
||||
switch ( unit ) {
|
||||
case NANOSECOND:
|
||||
case NATIVE:
|
||||
// If the driver or database do not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) || jtdsDriver ) {
|
||||
return "dateadd(millisecond,?2/1000000,?3)";
|
||||
}
|
||||
else {
|
||||
return "dateadd(mcs,?2/1000,?3)";
|
||||
}
|
||||
default:
|
||||
return "dateadd(?1,?2,?3)";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFractionalSecondPrecisionInNanos() {
|
||||
// If the database does not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) ) {
|
||||
return 1_000_000;
|
||||
}
|
||||
else {
|
||||
return 1_000;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||
//TODO!!
|
||||
switch ( unit ) {
|
||||
case NANOSECOND:
|
||||
case NATIVE:
|
||||
// If the database does not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) ) {
|
||||
return "cast(datediff(ms,?2,?3) as numeric(21))";
|
||||
}
|
||||
else {
|
||||
return "cast(datediff(mcs,cast(?2 as bigdatetime),cast(?3 as bigdatetime)) as numeric(21))";
|
||||
}
|
||||
default:
|
||||
return "datediff(?1,?2,?3)";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerDefaultKeywords() {
|
||||
super.registerDefaultKeywords();
|
||||
registerKeyword( "add" );
|
||||
registerKeyword( "all" );
|
||||
registerKeyword( "alter" );
|
||||
registerKeyword( "and" );
|
||||
registerKeyword( "any" );
|
||||
registerKeyword( "arith_overflow" );
|
||||
registerKeyword( "as" );
|
||||
registerKeyword( "asc" );
|
||||
registerKeyword( "at" );
|
||||
registerKeyword( "authorization" );
|
||||
registerKeyword( "avg" );
|
||||
registerKeyword( "begin" );
|
||||
registerKeyword( "between" );
|
||||
registerKeyword( "break" );
|
||||
registerKeyword( "browse" );
|
||||
registerKeyword( "bulk" );
|
||||
registerKeyword( "by" );
|
||||
registerKeyword( "cascade" );
|
||||
registerKeyword( "case" );
|
||||
registerKeyword( "char_convert" );
|
||||
registerKeyword( "check" );
|
||||
registerKeyword( "checkpoint" );
|
||||
registerKeyword( "close" );
|
||||
registerKeyword( "clustered" );
|
||||
registerKeyword( "coalesce" );
|
||||
registerKeyword( "commit" );
|
||||
registerKeyword( "compute" );
|
||||
registerKeyword( "confirm" );
|
||||
registerKeyword( "connect" );
|
||||
registerKeyword( "constraint" );
|
||||
registerKeyword( "continue" );
|
||||
registerKeyword( "controlrow" );
|
||||
registerKeyword( "convert" );
|
||||
registerKeyword( "count" );
|
||||
registerKeyword( "count_big" );
|
||||
registerKeyword( "create" );
|
||||
registerKeyword( "current" );
|
||||
registerKeyword( "cursor" );
|
||||
registerKeyword( "database" );
|
||||
registerKeyword( "dbcc" );
|
||||
registerKeyword( "deallocate" );
|
||||
registerKeyword( "declare" );
|
||||
registerKeyword( "decrypt" );
|
||||
registerKeyword( "default" );
|
||||
registerKeyword( "delete" );
|
||||
registerKeyword( "desc" );
|
||||
registerKeyword( "determnistic" );
|
||||
registerKeyword( "disk" );
|
||||
registerKeyword( "distinct" );
|
||||
registerKeyword( "drop" );
|
||||
registerKeyword( "dummy" );
|
||||
registerKeyword( "dump" );
|
||||
registerKeyword( "else" );
|
||||
registerKeyword( "encrypt" );
|
||||
registerKeyword( "end" );
|
||||
registerKeyword( "endtran" );
|
||||
registerKeyword( "errlvl" );
|
||||
registerKeyword( "errordata" );
|
||||
registerKeyword( "errorexit" );
|
||||
registerKeyword( "escape" );
|
||||
registerKeyword( "except" );
|
||||
registerKeyword( "exclusive" );
|
||||
registerKeyword( "exec" );
|
||||
registerKeyword( "execute" );
|
||||
registerKeyword( "exist" );
|
||||
registerKeyword( "exit" );
|
||||
registerKeyword( "exp_row_size" );
|
||||
registerKeyword( "external" );
|
||||
registerKeyword( "fetch" );
|
||||
registerKeyword( "fillfactor" );
|
||||
registerKeyword( "for" );
|
||||
registerKeyword( "foreign" );
|
||||
registerKeyword( "from" );
|
||||
registerKeyword( "goto" );
|
||||
registerKeyword( "grant" );
|
||||
registerKeyword( "group" );
|
||||
registerKeyword( "having" );
|
||||
registerKeyword( "holdlock" );
|
||||
registerKeyword( "identity" );
|
||||
registerKeyword( "identity_gap" );
|
||||
registerKeyword( "identity_start" );
|
||||
registerKeyword( "if" );
|
||||
registerKeyword( "in" );
|
||||
registerKeyword( "index" );
|
||||
registerKeyword( "inout" );
|
||||
registerKeyword( "insensitive" );
|
||||
registerKeyword( "insert" );
|
||||
registerKeyword( "install" );
|
||||
registerKeyword( "intersect" );
|
||||
registerKeyword( "into" );
|
||||
registerKeyword( "is" );
|
||||
registerKeyword( "isolation" );
|
||||
registerKeyword( "jar" );
|
||||
registerKeyword( "join" );
|
||||
registerKeyword( "key" );
|
||||
registerKeyword( "kill" );
|
||||
registerKeyword( "level" );
|
||||
registerKeyword( "like" );
|
||||
registerKeyword( "lineno" );
|
||||
registerKeyword( "load" );
|
||||
registerKeyword( "lock" );
|
||||
registerKeyword( "materialized" );
|
||||
registerKeyword( "max" );
|
||||
registerKeyword( "max_rows_per_page" );
|
||||
registerKeyword( "min" );
|
||||
registerKeyword( "mirror" );
|
||||
registerKeyword( "mirrorexit" );
|
||||
registerKeyword( "modify" );
|
||||
registerKeyword( "national" );
|
||||
registerKeyword( "new" );
|
||||
registerKeyword( "noholdlock" );
|
||||
registerKeyword( "nonclustered" );
|
||||
registerKeyword( "nonscrollable" );
|
||||
registerKeyword( "non_sensitive" );
|
||||
registerKeyword( "not" );
|
||||
registerKeyword( "null" );
|
||||
registerKeyword( "nullif" );
|
||||
registerKeyword( "numeric_truncation" );
|
||||
registerKeyword( "of" );
|
||||
registerKeyword( "off" );
|
||||
registerKeyword( "offsets" );
|
||||
registerKeyword( "on" );
|
||||
registerKeyword( "once" );
|
||||
registerKeyword( "online" );
|
||||
registerKeyword( "only" );
|
||||
registerKeyword( "open" );
|
||||
registerKeyword( "option" );
|
||||
registerKeyword( "or" );
|
||||
registerKeyword( "order" );
|
||||
registerKeyword( "out" );
|
||||
registerKeyword( "output" );
|
||||
registerKeyword( "over" );
|
||||
registerKeyword( "artition" );
|
||||
registerKeyword( "perm" );
|
||||
registerKeyword( "permanent" );
|
||||
registerKeyword( "plan" );
|
||||
registerKeyword( "prepare" );
|
||||
registerKeyword( "primary" );
|
||||
registerKeyword( "print" );
|
||||
registerKeyword( "privileges" );
|
||||
registerKeyword( "proc" );
|
||||
registerKeyword( "procedure" );
|
||||
registerKeyword( "processexit" );
|
||||
registerKeyword( "proxy_table" );
|
||||
registerKeyword( "public" );
|
||||
registerKeyword( "quiesce" );
|
||||
registerKeyword( "raiserror" );
|
||||
registerKeyword( "read" );
|
||||
registerKeyword( "readpast" );
|
||||
registerKeyword( "readtext" );
|
||||
registerKeyword( "reconfigure" );
|
||||
registerKeyword( "references" );
|
||||
registerKeyword( "remove" );
|
||||
registerKeyword( "reorg" );
|
||||
registerKeyword( "replace" );
|
||||
registerKeyword( "replication" );
|
||||
registerKeyword( "reservepagegap" );
|
||||
registerKeyword( "return" );
|
||||
registerKeyword( "returns" );
|
||||
registerKeyword( "revoke" );
|
||||
registerKeyword( "role" );
|
||||
registerKeyword( "rollback" );
|
||||
registerKeyword( "rowcount" );
|
||||
registerKeyword( "rows" );
|
||||
registerKeyword( "rule" );
|
||||
registerKeyword( "save" );
|
||||
registerKeyword( "schema" );
|
||||
registerKeyword( "scroll" );
|
||||
registerKeyword( "scrollable" );
|
||||
registerKeyword( "select" );
|
||||
registerKeyword( "semi_sensitive" );
|
||||
registerKeyword( "set" );
|
||||
registerKeyword( "setuser" );
|
||||
registerKeyword( "shared" );
|
||||
registerKeyword( "shutdown" );
|
||||
registerKeyword( "some" );
|
||||
registerKeyword( "statistics" );
|
||||
registerKeyword( "stringsize" );
|
||||
registerKeyword( "stripe" );
|
||||
registerKeyword( "sum" );
|
||||
registerKeyword( "syb_identity" );
|
||||
registerKeyword( "syb_restree" );
|
||||
registerKeyword( "syb_terminate" );
|
||||
registerKeyword( "top" );
|
||||
registerKeyword( "table" );
|
||||
registerKeyword( "temp" );
|
||||
registerKeyword( "temporary" );
|
||||
registerKeyword( "textsize" );
|
||||
registerKeyword( "to" );
|
||||
registerKeyword( "tracefile" );
|
||||
registerKeyword( "tran" );
|
||||
registerKeyword( "transaction" );
|
||||
registerKeyword( "trigger" );
|
||||
registerKeyword( "truncate" );
|
||||
registerKeyword( "tsequal" );
|
||||
registerKeyword( "union" );
|
||||
registerKeyword( "unique" );
|
||||
registerKeyword( "unpartition" );
|
||||
registerKeyword( "update" );
|
||||
registerKeyword( "use" );
|
||||
registerKeyword( "user" );
|
||||
registerKeyword( "user_option" );
|
||||
registerKeyword( "using" );
|
||||
registerKeyword( "values" );
|
||||
registerKeyword( "varying" );
|
||||
registerKeyword( "view" );
|
||||
registerKeyword( "waitfor" );
|
||||
registerKeyword( "when" );
|
||||
registerKeyword( "where" );
|
||||
registerKeyword( "while" );
|
||||
registerKeyword( "with" );
|
||||
registerKeyword( "work" );
|
||||
registerKeyword( "writetext" );
|
||||
registerKeyword( "xmlextract" );
|
||||
registerKeyword( "xmlparse" );
|
||||
registerKeyword( "xmltest" );
|
||||
registerKeyword( "xmlvalidate" );
|
||||
}
|
||||
|
||||
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
||||
@Override
|
||||
public boolean supportsCascadeDelete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxAliasLength() {
|
||||
return 30;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxIdentifierLength() {
|
||||
return 255;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesListForInsert() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLockTimeouts() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOrderByInSubquery() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUnionInSubquery() {
|
||||
// At least not according to HHH-3637
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsPartitionBy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableTypeString() {
|
||||
//HHH-7298 I don't know if this would break something or cause some side affects
|
||||
//but it is required to use 'select for update'
|
||||
return getVersion().isBefore( 15, 7 ) ? super.getTableTypeString() : " lock datarows";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsExpectedLobUsagePattern() {
|
||||
// Earlier Sybase did not support LOB locators at all
|
||||
return getVersion().isSameOrAfter( 15, 7 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLobValueChangePropagation() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return getVersion().isSameOrAfter( 15, 7 ) ? RowLockStrategy.COLUMN : RowLockStrategy.TABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForUpdateString() {
|
||||
return getVersion().isBefore( 15, 7 ) ? "" : " for update";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForUpdateString(String aliases) {
|
||||
return getVersion().isBefore( 15, 7 )
|
||||
? ""
|
||||
: getForUpdateString() + " of " + aliases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String appendLockHint(LockOptions mode, String tableName) {
|
||||
//TODO: is this really necessary??!
|
||||
return getVersion().isBefore( 15, 7 ) ? super.appendLockHint( mode, tableName ) : tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
|
||||
//TODO: is this really correct?
|
||||
return getVersion().isBefore( 15, 7 )
|
||||
? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames )
|
||||
: sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViolatedConstraintNameExtractor getViolatedConstraintNameExtractor() {
|
||||
return EXTRACTOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constraint-name extractor for Sybase ASE constraint violation exceptions.
|
||||
* Orginally contributed by Denny Bartelt.
|
||||
*/
|
||||
private static final ViolatedConstraintNameExtractor EXTRACTOR =
|
||||
new TemplatedViolatedConstraintNameExtractor( sqle -> {
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqle );
|
||||
switch ( JdbcExceptionHelper.extractSqlState( sqle ) ) {
|
||||
// UNIQUE VIOLATION
|
||||
case "S1000":
|
||||
if (2601 == errorCode) {
|
||||
return extractUsingTemplate( "with unique index '", "'", sqle.getMessage() );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if (546 == errorCode) {
|
||||
// Foreign key violation
|
||||
return extractUsingTemplate( "constraint name = '", "'", sqle.getMessage() );
|
||||
}
|
||||
break;
|
||||
// // FOREIGN KEY VIOLATION
|
||||
// case 23503:
|
||||
// return extractUsingTemplate( "violates foreign key constraint \"","\"", sqle.getMessage() );
|
||||
// // NOT NULL VIOLATION
|
||||
// case 23502:
|
||||
// return extractUsingTemplate( "null value in column \"","\" violates not-null constraint", sqle.getMessage() );
|
||||
// // TODO: RESTRICT VIOLATION
|
||||
// case 23001:
|
||||
// return null;
|
||||
// ALL OTHER
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
} );
|
||||
|
||||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
if ( getVersion().isBefore( 15, 7 ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
switch ( sqlState ) {
|
||||
case "JZ0TO":
|
||||
case "JZ006":
|
||||
throw new LockTimeoutException( message, sqlException, sql );
|
||||
case "S1000":
|
||||
switch ( errorCode ) {
|
||||
case 515:
|
||||
// Attempt to insert NULL value into column; column does not allow nulls.
|
||||
case 2601:
|
||||
// Unique constraint violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
}
|
||||
break;
|
||||
case "ZZZZZ":
|
||||
if (515 == errorCode) {
|
||||
// Attempt to insert NULL value into column; column does not allow nulls.
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
}
|
||||
break;
|
||||
case "23000":
|
||||
if (546 == errorCode) {
|
||||
// Foreign key violation
|
||||
final String constraintName = getViolatedConstraintNameExtractor().extractConstraintName( sqlException );
|
||||
return new ConstraintViolationException( message, sqlException, sql, constraintName );
|
||||
}
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( getVersion().isBefore( 12, 5 ) ) {
|
||||
//support for SELECT TOP was introduced in Sybase ASE 12.5.3
|
||||
return super.getLimitHandler();
|
||||
}
|
||||
return new TopLimitHandler(false);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,403 @@
|
|||
/*
|
||||
* 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.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.QueryLiteral;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
import org.hibernate.sql.ast.tree.predicate.BooleanExpressionPredicate;
|
||||
import org.hibernate.sql.ast.tree.predicate.Predicate;
|
||||
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.ast.tree.select.SelectClause;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for Sybase ASE.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class SybaseASELegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
|
||||
public SybaseASELegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
}
|
||||
|
||||
// Sybase ASE does not allow CASE expressions where all result arms contain plain parameters.
|
||||
// At least one result arm must provide some type context for inference,
|
||||
// so we cast the first result arm if we encounter this condition
|
||||
|
||||
@Override
|
||||
protected void visitAnsiCaseSearchedExpression(
|
||||
CaseSearchedExpression caseSearchedExpression,
|
||||
Consumer<Expression> resultRenderer) {
|
||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSearchedExpression ) ) {
|
||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||
super.visitAnsiCaseSearchedExpression(
|
||||
caseSearchedExpression,
|
||||
e -> {
|
||||
if ( e == firstResult ) {
|
||||
renderCasted( e );
|
||||
}
|
||||
else {
|
||||
resultRenderer.accept( e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
super.visitAnsiCaseSearchedExpression( caseSearchedExpression, resultRenderer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitAnsiCaseSimpleExpression(
|
||||
CaseSimpleExpression caseSimpleExpression,
|
||||
Consumer<Expression> resultRenderer) {
|
||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSimpleExpression ) ) {
|
||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||
super.visitAnsiCaseSimpleExpression(
|
||||
caseSimpleExpression,
|
||||
e -> {
|
||||
if ( e == firstResult ) {
|
||||
renderCasted( e );
|
||||
}
|
||||
else {
|
||||
resultRenderer.accept( e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
super.visitAnsiCaseSimpleExpression( caseSimpleExpression, resultRenderer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
if ( getDialect().getVersion().isBefore( 15, 7 ) ) {
|
||||
if ( LockMode.READ.lessThan( lockMode ) ) {
|
||||
appendSql( " holdlock" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
else {
|
||||
appendSql( WHITESPACE );
|
||||
appendSql( tableGroupJoin.getJoinType().getText() );
|
||||
appendSql( "join " );
|
||||
}
|
||||
|
||||
final Predicate predicate;
|
||||
if ( tableGroupJoin.getPredicate() == null ) {
|
||||
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.CROSS ) {
|
||||
predicate = null;
|
||||
}
|
||||
else {
|
||||
predicate = new BooleanExpressionPredicate( new QueryLiteral<>( true, getBooleanType() ) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
predicate = tableGroupJoin.getPredicate();
|
||||
}
|
||||
if ( predicate != null && !predicate.isEmpty() ) {
|
||||
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
|
||||
}
|
||||
else {
|
||||
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||
if ( getDialect().getVersion().isBefore( 15, 7 ) ) {
|
||||
return;
|
||||
}
|
||||
super.renderForUpdateClause( querySpec, forUpdateClause );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSearchClause(CteStatement cte) {
|
||||
// Sybase ASE does not support this, but it's just a hint anyway
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderCycleClause(CteStatement cte) {
|
||||
// Sybase ASE does not support this, but it can be emulated
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitSqlSelections(SelectClause selectClause) {
|
||||
if ( supportsTopClause() ) {
|
||||
renderTopClause( (QuerySpec) getQueryPartStack().getCurrent(), true, false );
|
||||
}
|
||||
super.visitSqlSelections( selectClause );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderFetchPlusOffsetExpression(
|
||||
Expression fetchClauseExpression,
|
||||
Expression offsetClauseExpression,
|
||||
int offset) {
|
||||
renderFetchPlusOffsetExpressionAsLiteral( fetchClauseExpression, offsetClauseExpression, offset );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||
if ( queryGroup.hasSortSpecifications() || queryGroup.hasOffsetOrFetchClause() ) {
|
||||
appendSql( "select " );
|
||||
renderTopClause(
|
||||
queryGroup.getOffsetClauseExpression(),
|
||||
queryGroup.getFetchClauseExpression(),
|
||||
queryGroup.getFetchClauseType(),
|
||||
true,
|
||||
false
|
||||
);
|
||||
appendSql( "* from (" );
|
||||
renderQueryGroup( queryGroup, false );
|
||||
appendSql( ") grp_(c0" );
|
||||
// Sybase doesn't have implicit names for non-column select expressions, so we need to assign names
|
||||
final int itemCount = queryGroup.getFirstQuerySpec().getSelectClause().getSqlSelections().size();
|
||||
for (int i = 1; i < itemCount; i++) {
|
||||
appendSql( ",c" );
|
||||
appendSql( i );
|
||||
}
|
||||
appendSql( ')' );
|
||||
visitOrderBy( queryGroup.getSortSpecifications() );
|
||||
}
|
||||
else {
|
||||
super.visitQueryGroup( queryGroup );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
if ( !queryPart.isRoot() && queryPart.hasOffsetOrFetchClause() ) {
|
||||
if ( queryPart.getFetchClauseExpression() != null && !supportsTopClause() || queryPart.getOffsetClauseExpression() != null ) {
|
||||
throw new IllegalArgumentException( "Can't emulate offset fetch clause in subquery" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderFetchExpression(Expression fetchExpression) {
|
||||
if ( supportsParameterOffsetFetchExpression() ) {
|
||||
super.renderFetchExpression( fetchExpression );
|
||||
}
|
||||
else {
|
||||
renderExpressionAsLiteral( fetchExpression, getJdbcParameterBindings() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderOffsetExpression(Expression offsetExpression) {
|
||||
if ( supportsParameterOffsetFetchExpression() ) {
|
||||
super.renderOffsetExpression( offsetExpression );
|
||||
}
|
||||
else {
|
||||
renderExpressionAsLiteral( offsetExpression, getJdbcParameterBindings() );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
// I think intersect is only supported in 16.0 SP3
|
||||
if ( getDialect().isAnsiNullOn() ) {
|
||||
if ( supportsDistinctFromPredicate() ) {
|
||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||
}
|
||||
else {
|
||||
renderComparisonEmulateCase( lhs, operator, rhs );
|
||||
}
|
||||
}
|
||||
else {
|
||||
// The ansinull setting only matters if using a parameter or literal and the eq operator according to the docs
|
||||
// http://infocenter.sybase.com/help/index.jsp?topic=/com.sybase.infocenter.dc32300.1570/html/sqlug/sqlug89.htm
|
||||
boolean rhsNotNullPredicate =
|
||||
lhs instanceof Literal
|
||||
|| isParameter( lhs );
|
||||
boolean lhsNotNullPredicate =
|
||||
rhs instanceof Literal
|
||||
|| isParameter( rhs );
|
||||
if ( rhsNotNullPredicate || lhsNotNullPredicate ) {
|
||||
lhs.accept( this );
|
||||
switch ( operator ) {
|
||||
case DISTINCT_FROM:
|
||||
appendSql( "<>" );
|
||||
break;
|
||||
case NOT_DISTINCT_FROM:
|
||||
appendSql( '=' );
|
||||
break;
|
||||
case LESS_THAN:
|
||||
case GREATER_THAN:
|
||||
case LESS_THAN_OR_EQUAL:
|
||||
case GREATER_THAN_OR_EQUAL:
|
||||
// These operators are not affected by ansinull=off
|
||||
lhsNotNullPredicate = false;
|
||||
rhsNotNullPredicate = false;
|
||||
default:
|
||||
appendSql( operator.sqlText() );
|
||||
break;
|
||||
}
|
||||
rhs.accept( this );
|
||||
if ( lhsNotNullPredicate ) {
|
||||
appendSql( " and " );
|
||||
lhs.accept( this );
|
||||
appendSql( " is not null" );
|
||||
}
|
||||
if ( rhsNotNullPredicate ) {
|
||||
appendSql( " and " );
|
||||
rhs.accept( this );
|
||||
appendSql( " is not null" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ( supportsDistinctFromPredicate() ) {
|
||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||
}
|
||||
else {
|
||||
renderComparisonEmulateCase( lhs, operator, rhs );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsIntersect() {
|
||||
// At least the version that
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSelectTupleComparison(
|
||||
List<SqlSelection> lhsExpressions,
|
||||
SqlTuple tuple,
|
||||
ComparisonOperator operator) {
|
||||
emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderPartitionItem(Expression expression) {
|
||||
if ( expression instanceof Literal ) {
|
||||
// Note that this depends on the SqmToSqlAstConverter to add a dummy table group
|
||||
appendSql( "dummy_.x" );
|
||||
}
|
||||
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
|
||||
public void visitColumnReference(ColumnReference columnReference) {
|
||||
final String dmlTargetTableAlias = getDmlTargetTableAlias();
|
||||
if ( dmlTargetTableAlias != null && dmlTargetTableAlias.equals( columnReference.getQualifier() ) ) {
|
||||
// Sybase needs a table name prefix
|
||||
// but not if this is a restricted union table reference subquery
|
||||
final QuerySpec currentQuerySpec = (QuerySpec) getQueryPartStack().getCurrent();
|
||||
final List<TableGroup> roots;
|
||||
if ( currentQuerySpec != null && !currentQuerySpec.isRoot()
|
||||
&& (roots = currentQuerySpec.getFromClause().getRoots()).size() == 1
|
||||
&& roots.get( 0 ).getPrimaryTableReference() instanceof UnionTableReference ) {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
// for now, use the unqualified form
|
||||
else if ( columnReference.isColumnExpressionFormula() ) {
|
||||
// For formulas, we have to replace the qualifier as the alias was already rendered into the formula
|
||||
// This is fine for now as this is only temporary anyway until we render aliases for table references
|
||||
appendSql(
|
||||
columnReference.getColumnExpression()
|
||||
.replaceAll( "(\\b)(" + dmlTargetTableAlias + "\\.)(\\b)", "$1$3" )
|
||||
);
|
||||
}
|
||||
else {
|
||||
appendSql( getCurrentDmlStatement().getTargetTable().getTableExpression() );
|
||||
appendSql( '.' );
|
||||
appendSql( columnReference.getColumnExpression() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
columnReference.appendReadExpression( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsRowsToSkip() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsMaxRows() {
|
||||
return !supportsTopClause();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntax() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFromDual() {
|
||||
return " from (select 1) dual(c1)";
|
||||
}
|
||||
|
||||
private boolean supportsTopClause() {
|
||||
return getDialect().getVersion().isSameOrAfter( 12, 5 );
|
||||
}
|
||||
|
||||
private boolean supportsParameterOffsetFetchExpression() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
* 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 org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.AbstractTransactSQLDialect;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.NationalizationSupport;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.CountFunction;
|
||||
import org.hibernate.dialect.function.IntegralTimestampaddFunction;
|
||||
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.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.query.sqm.TrimSpec;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
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.service.ServiceRegistry;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
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.type.JavaObjectType;
|
||||
import org.hibernate.type.descriptor.jdbc.BlobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ClobJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.JdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.ObjectNullAsNullTypeJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
|
||||
/**
|
||||
* Superclass for all Sybase dialects.
|
||||
*
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
|
||||
|
||||
protected final boolean jtdsDriver;
|
||||
|
||||
//All Sybase dialects share an IN list size limit.
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 250000;
|
||||
|
||||
public SybaseLegacyDialect() {
|
||||
this( DatabaseVersion.make( 11, 0 ) );
|
||||
}
|
||||
|
||||
public SybaseLegacyDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
jtdsDriver = true;
|
||||
}
|
||||
|
||||
public SybaseLegacyDialect(DialectResolutionInfo info) {
|
||||
super(info);
|
||||
jtdsDriver = info.getDriverName() != null
|
||||
&& info.getDriverName().contains( "jTDS" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType resolveSqlTypeDescriptor(
|
||||
String columnTypeName,
|
||||
int jdbcTypeCode,
|
||||
int precision,
|
||||
int scale,
|
||||
JdbcTypeRegistry jdbcTypeRegistry) {
|
||||
switch ( jdbcTypeCode ) {
|
||||
case Types.NUMERIC:
|
||||
case Types.DECIMAL:
|
||||
if ( precision == 19 && scale == 0 ) {
|
||||
return jdbcTypeRegistry.getDescriptor( Types.BIGINT );
|
||||
}
|
||||
case Types.TINYINT:
|
||||
if ( jtdsDriver ) {
|
||||
return jdbcTypeRegistry.getDescriptor( Types.SMALLINT );
|
||||
}
|
||||
}
|
||||
return super.resolveSqlTypeDescriptor(
|
||||
columnTypeName,
|
||||
jdbcTypeCode,
|
||||
precision,
|
||||
scale,
|
||||
jdbcTypeRegistry
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqmTranslatorFactory getSqmTranslatorFactory() {
|
||||
return new StandardSqmTranslatorFactory() {
|
||||
@Override
|
||||
public SqmTranslator<SelectStatement> createSelectTranslator(
|
||||
SqmSelectStatement<?> sqmSelectStatement,
|
||||
QueryOptions queryOptions,
|
||||
DomainParameterXref domainParameterXref,
|
||||
QueryParameterBindings domainParameterBindings,
|
||||
LoadQueryInfluencers loadQueryInfluencers,
|
||||
SqlAstCreationContext creationContext,
|
||||
boolean deduplicateSelectionItems) {
|
||||
return new SybaseLegacySqmToSqlAstConverter<>(
|
||||
sqmSelectStatement,
|
||||
queryOptions,
|
||||
domainParameterXref,
|
||||
domainParameterBindings,
|
||||
loadQueryInfluencers,
|
||||
creationContext,
|
||||
deduplicateSelectionItems
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||
return new StandardSqlAstTranslatorFactory() {
|
||||
@Override
|
||||
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
return new SybaseLegacySqlAstTranslator<>( sessionFactory, statement );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNullPrecedence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInExpressionCountLimit() {
|
||||
return PARAM_LIST_SIZE_LIMIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes(typeContributions, serviceRegistry);
|
||||
final JdbcTypeRegistry jdbcTypeRegistry = typeContributions.getTypeConfiguration()
|
||||
.getJdbcTypeRegistry();
|
||||
if ( jtdsDriver ) {
|
||||
jdbcTypeRegistry.addDescriptor( Types.TINYINT, SmallIntJdbcType.INSTANCE );
|
||||
|
||||
// The jTDS driver doesn't support the JDBC4 signatures using 'long length' for stream bindings
|
||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.CLOB_BINDING );
|
||||
|
||||
// The jTDS driver doesn't support nationalized types
|
||||
jdbcTypeRegistry.addDescriptor( Types.NCLOB, ClobJdbcType.CLOB_BINDING );
|
||||
jdbcTypeRegistry.addDescriptor( Types.NVARCHAR, ClobJdbcType.CLOB_BINDING );
|
||||
}
|
||||
else {
|
||||
// Some Sybase drivers cannot support getClob. See HHH-7889
|
||||
jdbcTypeRegistry.addDescriptor( Types.CLOB, ClobJdbcType.STREAM_BINDING_EXTRACTING );
|
||||
}
|
||||
|
||||
jdbcTypeRegistry.addDescriptor( Types.BLOB, BlobJdbcType.PRIMITIVE_ARRAY_BINDING );
|
||||
|
||||
// Sybase requires a custom binder for binding untyped nulls with the NULL type
|
||||
typeContributions.contributeJdbcType( ObjectNullAsNullTypeJdbcType.INSTANCE );
|
||||
|
||||
// Until we remove StandardBasicTypes, we have to keep this
|
||||
typeContributions.contributeType(
|
||||
new JavaObjectType(
|
||||
ObjectNullAsNullTypeJdbcType.INSTANCE,
|
||||
typeContributions.getTypeConfiguration()
|
||||
.getJavaTypeRegistry()
|
||||
.getDescriptor( Object.class )
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NationalizationSupport getNationalizationSupport() {
|
||||
// At least the jTDS driver doesn't support this
|
||||
return jtdsDriver ? NationalizationSupport.IMPLICIT : super.getNationalizationSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||
super.initializeFunctionRegistry(queryEngine);
|
||||
|
||||
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||
|
||||
// For SQL-Server we need to cast certain arguments to varchar(16384) to be able to concat them
|
||||
queryEngine.getSqmFunctionRegistry().register(
|
||||
"count",
|
||||
new CountFunction(
|
||||
this,
|
||||
queryEngine.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.DEFAULT,
|
||||
"+",
|
||||
"varchar(16384)",
|
||||
false
|
||||
)
|
||||
);
|
||||
|
||||
// AVG by default uses the input type, so we possibly need to cast the argument type, hence a special function
|
||||
functionFactory.avg_castingNonDoubleArguments( this, SqlAstNodeRenderingMode.DEFAULT );
|
||||
|
||||
//this doesn't work 100% on earlier versions of Sybase
|
||||
//which were missing the third parameter in charindex()
|
||||
//TODO: we could emulate it with substring() like in Postgres
|
||||
functionFactory.locate_charindex();
|
||||
|
||||
functionFactory.replace_strReplace();
|
||||
functionFactory.everyAny_minMaxCase();
|
||||
functionFactory.octetLength_pattern( "datalength(?1)" );
|
||||
functionFactory.bitLength_pattern( "datalength(?1)*8" );
|
||||
|
||||
queryEngine.getSqmFunctionRegistry().register( "timestampadd",
|
||||
new IntegralTimestampaddFunction( this, queryEngine.getTypeConfiguration() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNullColumnString() {
|
||||
return " null";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCreateSchema() {
|
||||
// As far as I can tell, it does not
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentSchemaCommand() {
|
||||
return "select db_name()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxIdentifierLength() {
|
||||
return 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String castPattern(CastType from, CastType to) {
|
||||
if ( to == CastType.STRING ) {
|
||||
switch ( from ) {
|
||||
case DATE:
|
||||
return "str_replace(convert(varchar,?1,102),'.','-')";
|
||||
case TIME:
|
||||
return "convert(varchar,?1,108)";
|
||||
case TIMESTAMP:
|
||||
return "str_replace(convert(varchar,?1,23),'T',' ')";
|
||||
}
|
||||
}
|
||||
return super.castPattern( from, to );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translateExtractField(TemporalUnit unit) {
|
||||
switch ( unit ) {
|
||||
case WEEK: return "calweekofyear"; //the ISO week number I think
|
||||
default: return super.translateExtractField(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractPattern(TemporalUnit unit) {
|
||||
//TODO!!
|
||||
return "datepart(?1,?2)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFractionalTimestampArithmetic() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||
//TODO!!
|
||||
return "dateadd(?1,?2,?3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||
//TODO!!
|
||||
return "datediff(?1,?2,?3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String trimPattern(TrimSpec specification, char character) {
|
||||
return super.trimPattern(specification, character)
|
||||
.replace("replace", "str_replace");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||
throw new UnsupportedOperationException( "format() function not supported on Sybase");
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData)
|
||||
throws SQLException {
|
||||
if ( dbMetaData == null ) {
|
||||
builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.MIXED );
|
||||
builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED );
|
||||
}
|
||||
|
||||
return super.buildIdentifierHelper( builder, dbMetaData );
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
if ( getVersion().isSameOrAfter( 15 ) ) {
|
||||
return NameQualifierSupport.BOTH;
|
||||
}
|
||||
return NameQualifierSupport.CATALOG;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.AbstractSqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteStatement;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSearchedExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.CaseSimpleExpression;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.expression.SqlTuple;
|
||||
import org.hibernate.sql.ast.tree.expression.Summarization;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
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 Sybase.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class SybaseLegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
|
||||
public SybaseLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
}
|
||||
|
||||
// Sybase does not allow CASE expressions where all result arms contain plain parameters.
|
||||
// At least one result arm must provide some type context for inference,
|
||||
// so we cast the first result arm if we encounter this condition
|
||||
|
||||
@Override
|
||||
protected void visitAnsiCaseSearchedExpression(
|
||||
CaseSearchedExpression caseSearchedExpression,
|
||||
Consumer<Expression> resultRenderer) {
|
||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSearchedExpression ) ) {
|
||||
final List<CaseSearchedExpression.WhenFragment> whenFragments = caseSearchedExpression.getWhenFragments();
|
||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||
super.visitAnsiCaseSearchedExpression(
|
||||
caseSearchedExpression,
|
||||
e -> {
|
||||
if ( e == firstResult ) {
|
||||
renderCasted( e );
|
||||
}
|
||||
else {
|
||||
resultRenderer.accept( e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
super.visitAnsiCaseSearchedExpression( caseSearchedExpression, resultRenderer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitAnsiCaseSimpleExpression(
|
||||
CaseSimpleExpression caseSimpleExpression,
|
||||
Consumer<Expression> resultRenderer) {
|
||||
if ( getParameterRenderingMode() == SqlAstNodeRenderingMode.DEFAULT && areAllResultsParameters( caseSimpleExpression ) ) {
|
||||
final List<CaseSimpleExpression.WhenFragment> whenFragments = caseSimpleExpression.getWhenFragments();
|
||||
final Expression firstResult = whenFragments.get( 0 ).getResult();
|
||||
super.visitAnsiCaseSimpleExpression(
|
||||
caseSimpleExpression,
|
||||
e -> {
|
||||
if ( e == firstResult ) {
|
||||
renderCasted( e );
|
||||
}
|
||||
else {
|
||||
resultRenderer.accept( e );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
super.visitAnsiCaseSimpleExpression( caseSimpleExpression, resultRenderer );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
if ( LockMode.READ.lessThan( lockMode ) ) {
|
||||
appendSql( " holdlock" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||
// Sybase does not support the FOR UPDATE clause
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSearchClause(CteStatement cte) {
|
||||
// Sybase does not support this, but it's just a hint anyway
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderCycleClause(CteStatement cte) {
|
||||
// Sybase does not support this, but it can be emulated
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
assertRowsOnlyFetchClauseType( queryPart );
|
||||
if ( !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) {
|
||||
throw new IllegalArgumentException( "Can't emulate offset clause in subquery" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
renderComparisonEmulateIntersect( lhs, operator, rhs );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSelectTupleComparison(
|
||||
List<SqlSelection> lhsExpressions,
|
||||
SqlTuple tuple,
|
||||
ComparisonOperator operator) {
|
||||
emulateSelectTupleComparison( lhsExpressions, tuple.getExpressions(), operator, true );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderPartitionItem(Expression expression) {
|
||||
if ( expression instanceof Literal ) {
|
||||
// Note that this depends on the SqmToSqlAstConverter to add a dummy table group
|
||||
appendSql( "dummy_.x" );
|
||||
}
|
||||
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 supportsRowValueConstructorSyntax() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsRowsToSkip() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsMaxRows() {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* 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.LoadQueryInfluencers;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.query.sqm.internal.DomainParameterXref;
|
||||
import org.hibernate.query.sqm.sql.BaseSqmToSqlAstConverter;
|
||||
import org.hibernate.query.sqm.tree.SqmStatement;
|
||||
import org.hibernate.query.sqm.tree.expression.SqmExpression;
|
||||
import org.hibernate.query.sqm.tree.select.SqmQuerySpec;
|
||||
import org.hibernate.sql.ast.spi.SqlAstCreationContext;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Literal;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.StandardTableGroup;
|
||||
import org.hibernate.sql.ast.tree.select.QuerySpec;
|
||||
|
||||
/**
|
||||
* A SQM to SQL AST translator for Sybase ASE.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class SybaseLegacySqmToSqlAstConverter<T extends Statement> extends BaseSqmToSqlAstConverter<T> {
|
||||
|
||||
private boolean needsDummyTableGroup;
|
||||
|
||||
public SybaseLegacySqmToSqlAstConverter(
|
||||
SqmStatement<?> statement,
|
||||
QueryOptions queryOptions,
|
||||
DomainParameterXref domainParameterXref,
|
||||
QueryParameterBindings domainParameterBindings,
|
||||
LoadQueryInfluencers fetchInfluencers,
|
||||
SqlAstCreationContext creationContext,
|
||||
boolean deduplicateSelectionItems) {
|
||||
super(
|
||||
creationContext,
|
||||
statement,
|
||||
queryOptions,
|
||||
fetchInfluencers,
|
||||
domainParameterXref,
|
||||
domainParameterBindings,
|
||||
deduplicateSelectionItems
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QuerySpec visitQuerySpec(SqmQuerySpec<?> sqmQuerySpec) {
|
||||
final boolean needsDummy = this.needsDummyTableGroup;
|
||||
this.needsDummyTableGroup = false;
|
||||
try {
|
||||
final QuerySpec querySpec = super.visitQuerySpec( sqmQuerySpec );
|
||||
if ( this.needsDummyTableGroup ) {
|
||||
querySpec.getFromClause().addRoot(
|
||||
new StandardTableGroup(
|
||||
true,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
new NamedTableReference(
|
||||
"(select 1)",
|
||||
"dummy_(x)",
|
||||
false,
|
||||
getCreationContext().getSessionFactory()
|
||||
),
|
||||
null,
|
||||
getCreationContext().getSessionFactory()
|
||||
)
|
||||
);
|
||||
}
|
||||
return querySpec;
|
||||
}
|
||||
finally {
|
||||
this.needsDummyTableGroup = needsDummy;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Expression resolveGroupOrOrderByExpression(SqmExpression<?> groupByClauseExpression) {
|
||||
final Expression expression = super.resolveGroupOrOrderByExpression( groupByClauseExpression );
|
||||
if ( expression instanceof Literal ) {
|
||||
// Note that SqlAstTranslator.renderPartitionItem depends on this
|
||||
this.needsDummyTableGroup = true;
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
}
|
|
@ -4,16 +4,18 @@
|
|||
* 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.orm.test.dialect.unit.lockhint;
|
||||
package org.hibernate.community.dialect.unit.lockhint;
|
||||
|
||||
import org.hibernate.community.dialect.SybaseASELegacyDialect;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SybaseASE15Dialect;
|
||||
import org.hibernate.orm.test.dialect.unit.lockhint.AbstractLockHintTest;
|
||||
|
||||
/**
|
||||
* @author Gail Badner
|
||||
*/
|
||||
public class SybaseASE15LockHintsTest extends AbstractLockHintTest {
|
||||
public static final Dialect DIALECT = new SybaseASE15Dialect();
|
||||
public static final Dialect DIALECT = new SybaseASELegacyDialect();
|
||||
|
||||
protected String getLockHintUsed() {
|
||||
return "holdlock";
|
|
@ -48,10 +48,12 @@ import static org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtract
|
|||
import static org.hibernate.type.SqlTypes.*;
|
||||
|
||||
/**
|
||||
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 11.9 and above.
|
||||
* A {@linkplain Dialect SQL dialect} for Sybase Adaptive Server Enterprise 16 and above.
|
||||
*/
|
||||
public class SybaseASEDialect extends SybaseDialect {
|
||||
|
||||
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 16, 0 );
|
||||
|
||||
private final SizeStrategy sizeStrategy = new SizeStrategyImpl() {
|
||||
@Override
|
||||
public Size resolveSize(
|
||||
|
@ -74,7 +76,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
private final boolean ansiNull;
|
||||
|
||||
public SybaseASEDialect() {
|
||||
this( DatabaseVersion.make( 11 ) );
|
||||
this( MINIMUM_VERSION );
|
||||
}
|
||||
|
||||
public SybaseASEDialect(DatabaseVersion version) {
|
||||
|
@ -96,13 +98,10 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
// tinyint to store signed bytes, we can use it
|
||||
// to store boolean values)
|
||||
return "tinyint";
|
||||
case BIGINT:
|
||||
// Sybase ASE didn't introduce 'bigint' until version 15.0
|
||||
return getVersion().isBefore( 15 ) ? "numeric(19,0)" : super.columnType( sqlTypeCode );
|
||||
case DATE:
|
||||
return getVersion().isSameOrAfter( 12 ) ? "date" : super.columnType( sqlTypeCode );
|
||||
return "date";
|
||||
case TIME:
|
||||
return getVersion().isSameOrAfter( 12 ) ? "time" : super.columnType( sqlTypeCode );
|
||||
return "time";
|
||||
}
|
||||
return super.columnType( sqlTypeCode );
|
||||
}
|
||||
|
@ -114,7 +113,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
|
||||
// According to Wikipedia bigdatetime and bigtime were added in 15.5
|
||||
// But with jTDS we can't use them as the driver can't handle the types
|
||||
if ( getVersion().isSameOrAfter( 15, 5 ) && !jtdsDriver ) {
|
||||
if ( !jtdsDriver ) {
|
||||
ddlTypeRegistry.addDescriptor(
|
||||
CapacityDependentDdlType.builder( DATE, "bigdatetime", "bigdatetime", this )
|
||||
.withTypeCapacity( 3, "datetime" )
|
||||
|
@ -265,7 +264,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
case NATIVE:
|
||||
// If the driver or database do not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) || jtdsDriver ) {
|
||||
if ( jtdsDriver ) {
|
||||
return "dateadd(millisecond,?2/1000000,?3)";
|
||||
}
|
||||
else {
|
||||
|
@ -280,12 +279,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
public long getFractionalSecondPrecisionInNanos() {
|
||||
// If the database does not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) ) {
|
||||
return 1_000_000;
|
||||
}
|
||||
else {
|
||||
return 1_000;
|
||||
}
|
||||
return 1_000;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -294,14 +288,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
switch ( unit ) {
|
||||
case NANOSECOND:
|
||||
case NATIVE:
|
||||
// If the database does not support bigdatetime and bigtime types,
|
||||
// we try to operate on milliseconds instead
|
||||
if ( getVersion().isBefore( 15, 5 ) ) {
|
||||
return "cast(datediff(ms,?2,?3) as numeric(21))";
|
||||
}
|
||||
else {
|
||||
return "cast(datediff(mcs,cast(?2 as bigdatetime),cast(?3 as bigdatetime)) as numeric(21))";
|
||||
}
|
||||
return "cast(datediff(mcs,cast(?2 as bigdatetime),cast(?3 as bigdatetime)) as numeric(21))";
|
||||
default:
|
||||
return "datediff(?1,?2,?3)";
|
||||
}
|
||||
|
@ -577,13 +564,7 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
public String getTableTypeString() {
|
||||
//HHH-7298 I don't know if this would break something or cause some side affects
|
||||
//but it is required to use 'select for update'
|
||||
return getVersion().isBefore( 15, 7 ) ? super.getTableTypeString() : " lock datarows";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsExpectedLobUsagePattern() {
|
||||
// Earlier Sybase did not support LOB locators at all
|
||||
return getVersion().isSameOrAfter( 15, 7 );
|
||||
return " lock datarows";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -593,33 +574,29 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
|
||||
@Override
|
||||
public RowLockStrategy getWriteRowLockStrategy() {
|
||||
return getVersion().isSameOrAfter( 15, 7 ) ? RowLockStrategy.COLUMN : RowLockStrategy.TABLE;
|
||||
return RowLockStrategy.COLUMN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForUpdateString() {
|
||||
return getVersion().isBefore( 15, 7 ) ? "" : " for update";
|
||||
return " for update";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getForUpdateString(String aliases) {
|
||||
return getVersion().isBefore( 15, 7 )
|
||||
? ""
|
||||
: getForUpdateString() + " of " + aliases;
|
||||
return getForUpdateString() + " of " + aliases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String appendLockHint(LockOptions mode, String tableName) {
|
||||
//TODO: is this really necessary??!
|
||||
return getVersion().isBefore( 15, 7 ) ? super.appendLockHint( mode, tableName ) : tableName;
|
||||
return tableName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String applyLocksToSql(String sql, LockOptions aliasedLockOptions, Map<String, String[]> keyColumnNames) {
|
||||
//TODO: is this really correct?
|
||||
return getVersion().isBefore( 15, 7 )
|
||||
? super.applyLocksToSql( sql, aliasedLockOptions, keyColumnNames )
|
||||
: sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
|
||||
return sql + new ForUpdateFragment( this, aliasedLockOptions, keyColumnNames ).toFragmentString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -665,10 +642,6 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
|
||||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
if ( getVersion().isBefore( 15, 7 ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
|
@ -707,10 +680,6 @@ public class SybaseASEDialect extends SybaseDialect {
|
|||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( getVersion().isBefore( 12, 5 ) ) {
|
||||
//support for SELECT TOP was introduced in Sybase ASE 12.5.3
|
||||
return super.getLimitHandler();
|
||||
}
|
||||
return new TopLimitHandler(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,12 +104,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
@Override
|
||||
protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
if ( getDialect().getVersion().isBefore( 15, 7 ) ) {
|
||||
if ( LockMode.READ.lessThan( lockMode ) ) {
|
||||
appendSql( " holdlock" );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -144,14 +138,6 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||
if ( getDialect().getVersion().isBefore( 15, 7 ) ) {
|
||||
return;
|
||||
}
|
||||
super.renderForUpdateClause( querySpec, forUpdateClause );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSearchClause(CteStatement cte) {
|
||||
// Sybase ASE does not support this, but it's just a hint anyway
|
||||
|
@ -394,7 +380,7 @@ public class SybaseASESqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
|
||||
private boolean supportsTopClause() {
|
||||
return getDialect().getVersion().isSameOrAfter( 12, 5 );
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean supportsParameterOffsetFetchExpression() {
|
||||
|
|
|
@ -60,17 +60,20 @@ import jakarta.persistence.TemporalType;
|
|||
*/
|
||||
public class SybaseDialect extends AbstractTransactSQLDialect {
|
||||
|
||||
protected boolean jtdsDriver;
|
||||
protected final boolean jtdsDriver;
|
||||
|
||||
private static final DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 16, 0 );
|
||||
|
||||
//All Sybase dialects share an IN list size limit.
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 250000;
|
||||
|
||||
public SybaseDialect() {
|
||||
this( DatabaseVersion.make( 11, 0 ) );
|
||||
this( MINIMUM_VERSION );
|
||||
}
|
||||
|
||||
public SybaseDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
jtdsDriver = true;
|
||||
}
|
||||
|
||||
public SybaseDialect(DialectResolutionInfo info) {
|
||||
|
@ -79,6 +82,11 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
&& info.getDriverName().contains( "jTDS" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||
return MINIMUM_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcType resolveSqlTypeDescriptor(
|
||||
String columnTypeName,
|
||||
|
@ -321,10 +329,12 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
if ( getVersion().isSameOrAfter( 15 ) ) {
|
||||
if ( jtdsDriver ) {
|
||||
return NameQualifierSupport.CATALOG;
|
||||
}
|
||||
else {
|
||||
return NameQualifierSupport.BOTH;
|
||||
}
|
||||
return NameQualifierSupport.CATALOG;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ import org.junit.jupiter.api.Test;
|
|||
xmlMappings = "org/hibernate/orm/test/dialect/function/Product.hbm.xml"
|
||||
)
|
||||
@SessionFactory
|
||||
@RequiresDialect(value = SybaseASEDialect.class, majorVersion = 11)
|
||||
@RequiresDialect(value = SybaseASEDialect.class)
|
||||
@SuppressWarnings("rawtypes")
|
||||
public class SybaseASEFunctionTest {
|
||||
|
||||
|
|
Loading…
Reference in New Issue