HHH-15180 - Remove support for SQL Server versions older than 2008
Signed-off-by: Jan Schatteman <jschatte@redhat.com>
This commit is contained in:
parent
75f4c95274
commit
429ab5b936
|
@ -0,0 +1,948 @@
|
|||
/*
|
||||
* 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.*;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.boot.model.relational.QualifiedSequenceName;
|
||||
import org.hibernate.boot.model.relational.Sequence;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.AbstractTransactSQLDialect;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.Replacer;
|
||||
import org.hibernate.dialect.TimeZoneSupport;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.CountFunction;
|
||||
import org.hibernate.dialect.function.SQLServerFormatEmulation;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.SQLServerIdentityColumnSupport;
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.pagination.SQLServer2005LimitHandler;
|
||||
import org.hibernate.dialect.pagination.SQLServer2012LimitHandler;
|
||||
import org.hibernate.dialect.pagination.TopLimitHandler;
|
||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SQLServer16SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SQLServerSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder;
|
||||
import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
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.StandardSqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.tool.schema.internal.StandardSequenceExporter;
|
||||
import org.hibernate.tool.schema.spi.Exporter;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.BasicTypeRegistry;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.descriptor.java.PrimitiveByteArrayJavaType;
|
||||
import org.hibernate.type.descriptor.jdbc.SmallIntJdbcType;
|
||||
import org.hibernate.type.descriptor.jdbc.XmlJdbcType;
|
||||
import org.hibernate.type.descriptor.sql.internal.DdlTypeImpl;
|
||||
import org.hibernate.type.descriptor.sql.spi.DdlTypeRegistry;
|
||||
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import static org.hibernate.query.sqm.TemporalUnit.NANOSECOND;
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.INTEGER;
|
||||
import static org.hibernate.type.SqlTypes.*;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsDate;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTime;
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
|
||||
/**
|
||||
* A dialect for Microsoft SQL Server 2000 and above
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 2100;
|
||||
|
||||
private final StandardSequenceExporter exporter;
|
||||
|
||||
public SQLServerLegacyDialect() {
|
||||
this( DatabaseVersion.make( 8, 0 ) );
|
||||
}
|
||||
|
||||
public SQLServerLegacyDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
exporter = createSequenceExporter(version);
|
||||
}
|
||||
|
||||
public SQLServerLegacyDialect(DialectResolutionInfo info) {
|
||||
super(info);
|
||||
exporter = createSequenceExporter(info);
|
||||
}
|
||||
|
||||
private StandardSequenceExporter createSequenceExporter(DatabaseVersion version) {
|
||||
return version.isSameOrAfter(11) ? new SqlServerSequenceExporter(this) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerDefaultKeywords() {
|
||||
super.registerDefaultKeywords();
|
||||
registerKeyword( "top" );
|
||||
registerKeyword( "key" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String columnType(int sqlTypeCode) {
|
||||
// there is no 'double' type in SQL server
|
||||
// but 'float' is double precision by default
|
||||
if ( sqlTypeCode == DOUBLE ) {
|
||||
return "float";
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
switch ( sqlTypeCode ) {
|
||||
// Prefer 'varchar(max)' and 'varbinary(max)' to
|
||||
// the deprecated TEXT and IMAGE types. Note that
|
||||
// the length of a VARCHAR or VARBINARY column must
|
||||
// be either between 1 and 8000 or exactly MAX, and
|
||||
// the length of an NVARCHAR column must be either
|
||||
// between 1 and 4000 or exactly MAX. (HHH-3965)
|
||||
case CLOB:
|
||||
return "varchar(max)";
|
||||
case NCLOB:
|
||||
return "nvarchar(max)";
|
||||
case BLOB:
|
||||
return "varbinary(max)";
|
||||
case DATE:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "date" : super.columnType( sqlTypeCode );
|
||||
case TIME:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "time" : super.columnType( sqlTypeCode );
|
||||
case TIMESTAMP:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "datetime2($p)" : super.columnType( sqlTypeCode );
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "datetimeoffset($p)" : super.columnType( sqlTypeCode );
|
||||
}
|
||||
}
|
||||
return super.columnType( sqlTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String castType(int sqlTypeCode) {
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
switch ( sqlTypeCode ) {
|
||||
case VARCHAR:
|
||||
case LONG32VARCHAR:
|
||||
case CLOB:
|
||||
return "varchar(max)";
|
||||
case NVARCHAR:
|
||||
case LONG32NVARCHAR:
|
||||
case NCLOB:
|
||||
return "nvarchar(max)";
|
||||
case VARBINARY:
|
||||
case LONG32VARBINARY:
|
||||
case BLOB:
|
||||
return "varbinary(max)";
|
||||
}
|
||||
}
|
||||
return super.castType( sqlTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||
}
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxVarcharLength() {
|
||||
return 8000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxNVarcharLength() {
|
||||
return 4000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
return getVersion().isSameOrAfter( 10 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getDefaultLobLength() {
|
||||
// this is essentially the only legal length for
|
||||
// a "lob" in SQL Server, i.e. the value of MAX
|
||||
// (caveat: for NVARCHAR it is half this value)
|
||||
return 2_147_483_647;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxIdentifierLength() {
|
||||
return 128;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contributeTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.contributeTypes( typeContributions, serviceRegistry );
|
||||
|
||||
typeContributions.getTypeConfiguration().getJdbcTypeRegistry().addDescriptor(
|
||||
Types.TINYINT,
|
||||
SmallIntJdbcType.INSTANCE
|
||||
);
|
||||
typeContributions.contributeJdbcType( XmlJdbcType.INSTANCE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initializeFunctionRegistry(QueryEngine queryEngine) {
|
||||
super.initializeFunctionRegistry(queryEngine);
|
||||
|
||||
final BasicTypeRegistry basicTypeRegistry = queryEngine.getTypeConfiguration().getBasicTypeRegistry();
|
||||
BasicType<Date> dateType = basicTypeRegistry.resolve( StandardBasicTypes.DATE );
|
||||
BasicType<Date> timeType = basicTypeRegistry.resolve( StandardBasicTypes.TIME );
|
||||
BasicType<Date> timestampType = basicTypeRegistry.resolve( StandardBasicTypes.TIMESTAMP );
|
||||
|
||||
CommonFunctionFactory functionFactory = new CommonFunctionFactory(queryEngine);
|
||||
|
||||
// For SQL-Server we need to cast certain arguments to varchar(max) to be able to concat them
|
||||
queryEngine.getSqmFunctionRegistry().register(
|
||||
"count",
|
||||
new CountFunction(
|
||||
this,
|
||||
queryEngine.getTypeConfiguration(),
|
||||
SqlAstNodeRenderingMode.DEFAULT,
|
||||
"+",
|
||||
"varchar(max)",
|
||||
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 );
|
||||
|
||||
functionFactory.truncate_round();
|
||||
functionFactory.everyAny_minMaxIif();
|
||||
functionFactory.octetLength_pattern( "datalength(?1)" );
|
||||
functionFactory.bitLength_pattern( "datalength(?1)*8" );
|
||||
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
functionFactory.locate_charindex();
|
||||
functionFactory.stddevPopSamp_stdevp();
|
||||
functionFactory.varPopSamp_varp();
|
||||
}
|
||||
|
||||
if ( getVersion().isSameOrAfter( 11 ) ) {
|
||||
queryEngine.getSqmFunctionRegistry().register(
|
||||
"format",
|
||||
new SQLServerFormatEmulation( queryEngine.getTypeConfiguration() )
|
||||
);
|
||||
|
||||
//actually translate() was added in 2017 but
|
||||
//it's not worth adding a new dialect for that!
|
||||
functionFactory.translate();
|
||||
|
||||
functionFactory.median_percentileCont( true );
|
||||
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "datefromparts" )
|
||||
.setInvariantType( dateType )
|
||||
.setExactArgumentCount( 3 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "timefromparts" )
|
||||
.setInvariantType( timeType )
|
||||
.setExactArgumentCount( 5 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "smalldatetimefromparts" )
|
||||
.setInvariantType( timestampType )
|
||||
.setExactArgumentCount( 5 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "datetimefromparts" )
|
||||
.setInvariantType( timestampType )
|
||||
.setExactArgumentCount( 7 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "datetime2fromparts" )
|
||||
.setInvariantType( timestampType )
|
||||
.setExactArgumentCount( 8 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
queryEngine.getSqmFunctionRegistry().namedDescriptorBuilder( "datetimeoffsetfromparts" )
|
||||
.setInvariantType( timestampType )
|
||||
.setExactArgumentCount( 10 )
|
||||
.setParameterTypes(INTEGER)
|
||||
.register();
|
||||
}
|
||||
functionFactory.windowFunctions();
|
||||
functionFactory.inverseDistributionOrderedSetAggregates_windowEmulation();
|
||||
functionFactory.hypotheticalOrderedSetAggregates_windowEmulation();
|
||||
if ( getVersion().isSameOrAfter( 14 ) ) {
|
||||
functionFactory.listagg_stringAggWithinGroup( "varchar(max)" );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SqlAstTranslatorFactory getSqlAstTranslatorFactory() {
|
||||
return new StandardSqlAstTranslatorFactory() {
|
||||
@Override
|
||||
protected <T extends JdbcOperation> SqlAstTranslator<T> buildTranslator(
|
||||
SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
return new SQLServerLegacySqlAstTranslator<>( sessionFactory, statement );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String castPattern(CastType from, CastType to) {
|
||||
if ( to == CastType.STRING ) {
|
||||
switch ( from ) {
|
||||
case TIMESTAMP:
|
||||
// SQL Server uses yyyy-MM-dd HH:mm:ss.nnnnnnn by default when doing a cast, but only need second precision
|
||||
return "format(?1,'yyyy-MM-dd HH:mm:ss')";
|
||||
case TIME:
|
||||
// SQL Server uses HH:mm:ss.nnnnnnn by default when doing a cast, but only need second precision
|
||||
// SQL Server requires quoting of ':' in time formats and the use of 'hh' instead of 'HH'
|
||||
return "format(?1,'hh\\:mm\\:ss')";
|
||||
}
|
||||
}
|
||||
return super.castPattern( from, to );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTimestamp() {
|
||||
return "sysdatetime()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentifierHelper buildIdentifierHelper(
|
||||
IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) throws SQLException {
|
||||
|
||||
if ( dbMetaData == null ) {
|
||||
// TODO: if DatabaseMetaData != null, unquoted case strategy is set to IdentifierCaseStrategy.UPPER
|
||||
// Check to see if this setting is correct.
|
||||
builder.setUnquotedCaseStrategy( IdentifierCaseStrategy.MIXED );
|
||||
builder.setQuotedCaseStrategy( IdentifierCaseStrategy.MIXED );
|
||||
}
|
||||
|
||||
return super.buildIdentifierHelper( builder, dbMetaData );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTime() {
|
||||
return "convert(time,getdate())";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentDate() {
|
||||
return "convert(date,getdate())";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String currentTimestampWithTimeZone() {
|
||||
return "sysdatetimeoffset()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNoColumnsInsertString() {
|
||||
return "default values";
|
||||
}
|
||||
|
||||
@Override
|
||||
public LimitHandler getLimitHandler() {
|
||||
if ( getVersion().isSameOrAfter( 11 ) ) {
|
||||
return SQLServer2012LimitHandler.INSTANCE;
|
||||
}
|
||||
else if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
//this is a stateful class, don't cache
|
||||
//it in the Dialect!
|
||||
return new SQLServer2005LimitHandler();
|
||||
}
|
||||
else {
|
||||
return new TopLimitHandler(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return getVersion().isSameOrAfter( 10 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public char closeQuote() {
|
||||
return ']';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentSchemaCommand() {
|
||||
return "select schema_name()";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsIfExistsBeforeTableName() {
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
return true;
|
||||
}
|
||||
return super.supportsIfExistsBeforeTableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsIfExistsBeforeConstraintName() {
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
return true;
|
||||
}
|
||||
return super.supportsIfExistsBeforeConstraintName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public char openQuote() {
|
||||
return '[';
|
||||
}
|
||||
|
||||
@Override
|
||||
public String appendLockHint(LockOptions lockOptions, String tableName) {
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
LockMode lockMode = lockOptions.getAliasSpecificLockMode( tableName );
|
||||
if (lockMode == null) {
|
||||
lockMode = lockOptions.getLockMode();
|
||||
}
|
||||
|
||||
final String writeLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? "updlock" : "updlock,holdlock";
|
||||
final String readLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? "updlock" : "holdlock";
|
||||
|
||||
final String noWaitStr = lockOptions.getTimeOut() == LockOptions.NO_WAIT ? ",nowait" : "";
|
||||
final String skipLockStr = lockOptions.getTimeOut() == LockOptions.SKIP_LOCKED ? ",readpast" : "";
|
||||
|
||||
switch ( lockMode ) {
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE:
|
||||
return tableName + " with (" + writeLockStr + ",rowlock" + noWaitStr + skipLockStr + ")";
|
||||
case PESSIMISTIC_READ:
|
||||
return tableName + " with (" + readLockStr + ",rowlock" + noWaitStr + skipLockStr + ")";
|
||||
case UPGRADE_SKIPLOCKED:
|
||||
return tableName + " with (updlock,rowlock,readpast" + noWaitStr + ")";
|
||||
case UPGRADE_NOWAIT:
|
||||
return tableName + " with (updlock,holdlock,rowlock,nowait)";
|
||||
default:
|
||||
return tableName;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( lockOptions.getLockMode() ) {
|
||||
case UPGRADE_NOWAIT:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE:
|
||||
return tableName + " with (updlock,rowlock)";
|
||||
case PESSIMISTIC_READ:
|
||||
return tableName + " with (holdlock,rowlock)";
|
||||
case UPGRADE_SKIPLOCKED:
|
||||
return tableName + " with (updlock,rowlock,readpast)";
|
||||
default:
|
||||
return tableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The current_timestamp is more accurate, but only known to be supported in SQL Server 7.0 and later and
|
||||
* Sybase not known to support it at all
|
||||
* <p/>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String getCurrentTimestampSelectString() {
|
||||
return "select current_timestamp";
|
||||
}
|
||||
|
||||
// Overridden informational metadata ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@Override
|
||||
public boolean supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsCircularCascadeDeleteConstraints() {
|
||||
// SQL Server (at least up through 2005) does not support defining
|
||||
// cascade delete constraints which can circle back to the mutating
|
||||
// table
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLobValueChangePropagation() {
|
||||
// note: at least my local SQL Server 2005 Express shows this not working...
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesReadCommittedCauseWritersToBlockReaders() {
|
||||
// here assume SQLServer2005 using snapshot isolation, which does not have this problem
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesRepeatableReadCauseReadersToBlockWriters() {
|
||||
// here assume SQLServer2005 using snapshot isolation, which does not have this problem
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getInExpressionCountLimit() {
|
||||
return PARAM_LIST_SIZE_LIMIT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return new SQLServerIdentityColumnSupport();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNoWait() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWait() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
if ( getVersion().isBefore( 11 ) ) {
|
||||
return NoSequenceSupport.INSTANCE;
|
||||
}
|
||||
else if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
return SQLServer16SequenceSupport.INSTANCE;
|
||||
}
|
||||
else {
|
||||
return SQLServerSequenceSupport.INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQuerySequencesString() {
|
||||
return getVersion().isBefore( 11 )
|
||||
? super.getQuerySequencesString() //null
|
||||
// The upper-case name should work on both case-sensitive
|
||||
// and case-insensitive collations.
|
||||
: "select * from INFORMATION_SCHEMA.SEQUENCES";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryHintString(String sql, String hints) {
|
||||
if ( getVersion().isBefore( 11 ) ) {
|
||||
return super.getQueryHintString( sql, hints );
|
||||
}
|
||||
|
||||
final StringBuilder buffer = new StringBuilder(
|
||||
sql.length() + hints.length() + 12
|
||||
);
|
||||
final int pos = sql.indexOf( ";" );
|
||||
if ( pos > -1 ) {
|
||||
buffer.append( sql, 0, pos );
|
||||
}
|
||||
else {
|
||||
buffer.append( sql );
|
||||
}
|
||||
buffer.append( " OPTION (" ).append( hints ).append( ")" );
|
||||
if ( pos > -1 ) {
|
||||
buffer.append( ";" );
|
||||
}
|
||||
sql = buffer.toString();
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNullPrecedence() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsOffsetInSubquery() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsWindowFunctions() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsLateral() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsFetchClause(FetchClauseType type) {
|
||||
return getVersion().isSameOrAfter( 11 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
if ( getVersion().isBefore( 9 ) ) {
|
||||
return super.buildSQLExceptionConversionDelegate(); //null
|
||||
}
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
if ( "HY008".equals( sqlState ) ) {
|
||||
throw new QueryTimeoutException( message, sqlException, sql );
|
||||
}
|
||||
if ( 1222 == errorCode ) {
|
||||
throw new LockTimeoutException( message, sqlException, sql );
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL server supports up to 7 decimal digits of
|
||||
* fractional second precision in a datetime2,
|
||||
* but since its duration arithmetic functions
|
||||
* try to fit durations into an int,
|
||||
* which is impossible with such high precision,
|
||||
* so default to generating {@code datetime2(3)}
|
||||
* columns.
|
||||
*/
|
||||
@Override
|
||||
public int getDefaultTimestampPrecision() {
|
||||
return 6; //microseconds!
|
||||
}
|
||||
|
||||
/**
|
||||
* SQL server supports up to 7 decimal digits of
|
||||
* fractional second precision in a datetime2,
|
||||
* but unfortunately its duration arithmetic
|
||||
* functions have a nasty habit of overflowing.
|
||||
* So to give ourselves a little extra headroom,
|
||||
* we will use {@code microsecond} as the native
|
||||
* unit of precision (but even then we have to
|
||||
* use tricks when calling {@code dateadd()}).
|
||||
*/
|
||||
@Override
|
||||
public long getFractionalSecondPrecisionInNanos() {
|
||||
return 1_000; //microseconds!
|
||||
}
|
||||
|
||||
@Override
|
||||
public String extractPattern(TemporalUnit unit) {
|
||||
switch (unit) {
|
||||
case TIMEZONE_HOUR:
|
||||
return "(datepart(tz,?2)/60)";
|
||||
case TIMEZONE_MINUTE:
|
||||
return "(datepart(tz,?2)%60)";
|
||||
//currently Dialect.extract() doesn't need
|
||||
//to handle NANOSECOND (might change that?)
|
||||
// case NANOSECOND:
|
||||
// //this should evaluate to a bigint type
|
||||
// return "(datepart(second,?2)*1000000000+datepart(nanosecond,?2))";
|
||||
case SECOND:
|
||||
//this should evaluate to a floating point type
|
||||
return "(datepart(second,?2)+datepart(nanosecond,?2)/1e9)";
|
||||
case WEEK:
|
||||
// Thanks https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
|
||||
if ( getVersion().isBefore( 10 ) ) {
|
||||
return "(DATEPART(dy,DATEADD(dd,DATEDIFF(dd,'17530101',?2)/7*7,'17530104'))+6)/7)";
|
||||
}
|
||||
default:
|
||||
return "datepart(?1,?2)";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampaddPattern(TemporalUnit unit, TemporalType temporalType, IntervalType intervalType) {
|
||||
// dateadd() supports only especially small magnitudes
|
||||
// since it casts its argument to int (and unfortunately
|
||||
// there's no dateadd_big()) so here we need to use two
|
||||
// calls to dateadd() to add a whole duration
|
||||
switch (unit) {
|
||||
case NANOSECOND:
|
||||
//Java Durations are usually the only thing
|
||||
//we find expressed in nanosecond precision,
|
||||
//and they can easily be very large
|
||||
return "dateadd(nanosecond,?2%1000000000,dateadd(second,?2/1000000000,?3))";
|
||||
case NATIVE:
|
||||
//microsecond is the "native" precision
|
||||
return "dateadd(microsecond,?2%1000000,dateadd(second,?2/1000000,?3))";
|
||||
default:
|
||||
return "dateadd(?1,?2,?3)";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String timestampdiffPattern(TemporalUnit unit, TemporalType fromTemporalType, TemporalType toTemporalType) {
|
||||
if ( unit == TemporalUnit.NATIVE ) {//use microsecond as the "native" precision
|
||||
return "datediff_big(microsecond,?2,?3)";
|
||||
}
|
||||
|
||||
//datediff() returns an int, and can easily
|
||||
//overflow when dealing with "physical"
|
||||
//durations, so use datediff_big()
|
||||
return unit.normalized() == NANOSECOND
|
||||
? "datediff_big(?1,?2,?3)"
|
||||
: "datediff(?1,?2,?3)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translateDurationField(TemporalUnit unit) {
|
||||
//use microsecond as the "native" precision
|
||||
if ( unit == TemporalUnit.NATIVE ) {
|
||||
return "microsecond";
|
||||
}
|
||||
|
||||
return super.translateDurationField( unit );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String translateExtractField(TemporalUnit unit) {
|
||||
switch ( unit ) {
|
||||
//the ISO week number (behavior of "week" depends on a system property)
|
||||
case WEEK: return "isowk";
|
||||
case OFFSET: return "tz";
|
||||
default: return super.translateExtractField(unit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDatetimeFormat(SqlAppender appender, String format) {
|
||||
appender.appendSql( datetimeFormat(format).result() );
|
||||
}
|
||||
|
||||
public static Replacer datetimeFormat(String format) {
|
||||
return new Replacer( format, "'", "\"" )
|
||||
//era
|
||||
.replace("G", "g")
|
||||
|
||||
//y nothing to do
|
||||
//M nothing to do
|
||||
|
||||
//w no equivalent
|
||||
//W no equivalent
|
||||
//Y no equivalent
|
||||
|
||||
//day of week
|
||||
.replace("EEEE", "dddd")
|
||||
.replace("EEE", "ddd")
|
||||
//e no equivalent
|
||||
|
||||
//d nothing to do
|
||||
//D no equivalent
|
||||
|
||||
//am pm
|
||||
.replace("a", "tt")
|
||||
|
||||
//h nothing to do
|
||||
//H nothing to do
|
||||
|
||||
//m nothing to do
|
||||
//s nothing to do
|
||||
|
||||
//fractional seconds
|
||||
.replace("S", "F")
|
||||
|
||||
//timezones
|
||||
.replace("XXX", "K") //UTC represented as "Z"
|
||||
.replace("xxx", "zzz")
|
||||
.replace("x", "zz");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) {
|
||||
appender.appendSql( "0x" );
|
||||
PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDateTimeLiteral(
|
||||
SqlAppender appender,
|
||||
TemporalAccessor temporalAccessor,
|
||||
TemporalType precision,
|
||||
TimeZone jdbcTimeZone) {
|
||||
switch ( precision ) {
|
||||
case DATE:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsDate( appender, temporalAccessor );
|
||||
appender.appendSql( "' as date)" );
|
||||
break;
|
||||
case TIME:
|
||||
//needed because the {t ... } JDBC is just buggy
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTime( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
|
||||
appender.appendSql( "' as time)" );
|
||||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTimestampWithMicros( appender, temporalAccessor, supportsTemporalLiteralOffset(), jdbcTimeZone );
|
||||
//needed because the {ts ... } JDBC escape chokes on microseconds
|
||||
if ( supportsTemporalLiteralOffset() && temporalAccessor.isSupported( ChronoField.OFFSET_SECONDS ) ) {
|
||||
appender.appendSql( "' as datetimeoffset)" );
|
||||
}
|
||||
else {
|
||||
appender.appendSql( "' as datetime2)" );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDateTimeLiteral(SqlAppender appender, Date date, TemporalType precision, TimeZone jdbcTimeZone) {
|
||||
switch ( precision ) {
|
||||
case DATE:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsDate( appender, date );
|
||||
appender.appendSql( "' as date)" );
|
||||
break;
|
||||
case TIME:
|
||||
//needed because the {t ... } JDBC is just buggy
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTime( appender, date );
|
||||
appender.appendSql( "' as time)" );
|
||||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTimestampWithMicros( appender, date, jdbcTimeZone );
|
||||
appender.appendSql( "' as datetimeoffset)" );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendDateTimeLiteral(
|
||||
SqlAppender appender,
|
||||
Calendar calendar,
|
||||
TemporalType precision,
|
||||
TimeZone jdbcTimeZone) {
|
||||
switch ( precision ) {
|
||||
case DATE:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsDate( appender, calendar );
|
||||
appender.appendSql( "' as date)" );
|
||||
break;
|
||||
case TIME:
|
||||
//needed because the {t ... } JDBC is just buggy
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTime( appender, calendar );
|
||||
appender.appendSql( "' as time)" );
|
||||
break;
|
||||
case TIMESTAMP:
|
||||
appender.appendSql( "cast('" );
|
||||
appendAsTimestampWithMicros( appender, calendar, jdbcTimeZone );
|
||||
appender.appendSql( "' as datetime2)" );
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateTemporaryTableColumnAnnotation(int sqlTypeCode) {
|
||||
switch (sqlTypeCode) {
|
||||
case Types.CHAR:
|
||||
case Types.NCHAR:
|
||||
case Types.VARCHAR:
|
||||
case Types.NVARCHAR:
|
||||
case Types.LONGVARCHAR:
|
||||
case Types.LONGNVARCHAR:
|
||||
return "collate database_default";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getDropSchemaCommand(String schemaName) {
|
||||
if ( getVersion().isSameOrAfter( 16 ) ) {
|
||||
return new String[] { "drop schema if exists " + schemaName };
|
||||
}
|
||||
return super.getDropSchemaCommand( schemaName );
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
return NameQualifierSupport.BOTH;
|
||||
}
|
||||
|
||||
public Exporter<Sequence> getSequenceExporter() {
|
||||
if ( exporter == null ) {
|
||||
return super.getSequenceExporter();
|
||||
}
|
||||
return exporter;
|
||||
}
|
||||
|
||||
private static class SqlServerSequenceExporter extends StandardSequenceExporter {
|
||||
|
||||
public SqlServerSequenceExporter(Dialect dialect) {
|
||||
super( dialect );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getFormattedSequenceName(QualifiedSequenceName name, Metadata metadata, SqlStringGenerationContext context) {
|
||||
// SQL Server does not allow the catalog in the sequence name.
|
||||
// See https://docs.microsoft.com/en-us/sql/t-sql/statements/create-sequence-transact-sql?view=sql-server-ver15&viewFallbackFrom=sql-server-ver12
|
||||
// Keeping the catalog in the name does not break on ORM, but it fails using Vert.X for Reactive.
|
||||
return context.formatWithoutCatalog( name );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) {
|
||||
// Not sure if it's a JDBC driver issue, but it doesn't work
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String generatedAs(String generatedAs) {
|
||||
return " as (" + generatedAs + ") persisted";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDataTypeBeforeGeneratedAs() {
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* 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 org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.query.sqm.ComparisonOperator;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstJoinType;
|
||||
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.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.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroupJoin;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.ast.tree.from.UnionTableReference;
|
||||
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.ast.tree.select.SortSpecification;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for SQL Server.
|
||||
*
|
||||
* @author Christian Beikov
|
||||
*/
|
||||
public class SQLServerLegacySqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAstTranslator<T> {
|
||||
|
||||
private static final String UNION_ALL = " union all ";
|
||||
|
||||
private Predicate lateralPredicate;
|
||||
|
||||
public SQLServerLegacySqlAstTranslator(SessionFactoryImplementor sessionFactory, Statement statement) {
|
||||
super( sessionFactory, statement );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderTableGroupJoin(TableGroupJoin tableGroupJoin, List<TableGroupJoin> tableGroupJoinCollector) {
|
||||
appendSql( WHITESPACE );
|
||||
if ( tableGroupJoin.getJoinedGroup().isLateral() ) {
|
||||
if ( tableGroupJoin.getJoinType() == SqlAstJoinType.LEFT ) {
|
||||
appendSql( "outer apply " );
|
||||
}
|
||||
else {
|
||||
appendSql( "cross apply " );
|
||||
}
|
||||
}
|
||||
else {
|
||||
appendSql( tableGroupJoin.getJoinType().getText() );
|
||||
appendSql( "join " );
|
||||
}
|
||||
|
||||
final Predicate predicate = tableGroupJoin.getPredicate();
|
||||
if ( predicate != null && !predicate.isEmpty() ) {
|
||||
if ( tableGroupJoin.getJoinedGroup().isLateral() ) {
|
||||
// We have to inject the lateral predicate into the sub-query
|
||||
final Predicate lateralPredicate = this.lateralPredicate;
|
||||
this.lateralPredicate = predicate;
|
||||
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
|
||||
this.lateralPredicate = lateralPredicate;
|
||||
}
|
||||
else {
|
||||
renderTableGroup( tableGroupJoin.getJoinedGroup(), predicate, tableGroupJoinCollector );
|
||||
}
|
||||
}
|
||||
else {
|
||||
renderTableGroup( tableGroupJoin.getJoinedGroup(), null, tableGroupJoinCollector );
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean renderPrimaryTableReference(TableGroup tableGroup, LockMode lockMode) {
|
||||
final TableReference tableReference = tableGroup.getPrimaryTableReference();
|
||||
if ( tableReference instanceof NamedTableReference ) {
|
||||
return renderNamedTableReference( (NamedTableReference) tableReference, lockMode );
|
||||
}
|
||||
tableReference.accept( this );
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean renderNamedTableReference(NamedTableReference tableReference, LockMode lockMode) {
|
||||
final String tableExpression = tableReference.getTableExpression();
|
||||
if ( tableReference instanceof UnionTableReference && lockMode != LockMode.NONE && tableExpression.charAt( 0 ) == '(' ) {
|
||||
// SQL Server requires to push down the lock hint to the actual table names
|
||||
int searchIndex = 0;
|
||||
int unionIndex;
|
||||
while ( ( unionIndex = tableExpression.indexOf( UNION_ALL, searchIndex ) ) != -1 ) {
|
||||
append( tableExpression, searchIndex, unionIndex );
|
||||
renderLockHint( lockMode );
|
||||
appendSql( UNION_ALL );
|
||||
searchIndex = unionIndex + UNION_ALL.length();
|
||||
}
|
||||
append( tableExpression, searchIndex, tableExpression.length() - 2 );
|
||||
renderLockHint( lockMode );
|
||||
appendSql( " )" );
|
||||
|
||||
registerAffectedTable( tableReference );
|
||||
final Clause currentClause = getClauseStack().getCurrent();
|
||||
if ( rendersTableReferenceAlias( currentClause ) ) {
|
||||
final String identificationVariable = tableReference.getIdentificationVariable();
|
||||
if ( identificationVariable != null ) {
|
||||
appendSql( ' ' );
|
||||
appendSql( identificationVariable );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.renderNamedTableReference( tableReference, lockMode );
|
||||
renderLockHint( lockMode );
|
||||
}
|
||||
// Just always return true because SQL Server doesn't support the FOR UPDATE clause
|
||||
return true;
|
||||
}
|
||||
|
||||
private void renderLockHint(LockMode lockMode) {
|
||||
if ( getDialect().getVersion().isSameOrAfter( 9 ) ) {
|
||||
final int effectiveLockTimeout = getEffectiveLockTimeout( lockMode );
|
||||
switch ( lockMode ) {
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE: {
|
||||
switch ( effectiveLockTimeout ) {
|
||||
case LockOptions.SKIP_LOCKED:
|
||||
appendSql( " with (updlock,rowlock,readpast)" );
|
||||
break;
|
||||
case LockOptions.NO_WAIT:
|
||||
appendSql( " with (updlock,holdlock,rowlock,nowait)" );
|
||||
break;
|
||||
default:
|
||||
appendSql( " with (updlock,holdlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PESSIMISTIC_READ: {
|
||||
switch ( effectiveLockTimeout ) {
|
||||
case LockOptions.SKIP_LOCKED:
|
||||
appendSql( " with (updlock,rowlock,readpast)" );
|
||||
break;
|
||||
case LockOptions.NO_WAIT:
|
||||
appendSql( " with (holdlock,rowlock,nowait)" );
|
||||
break;
|
||||
default:
|
||||
appendSql( " with (holdlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UPGRADE_SKIPLOCKED: {
|
||||
if ( effectiveLockTimeout == LockOptions.NO_WAIT ) {
|
||||
appendSql( " with (updlock,rowlock,readpast,nowait)" );
|
||||
}
|
||||
else {
|
||||
appendSql( " with (updlock,rowlock,readpast)" );
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UPGRADE_NOWAIT: {
|
||||
appendSql( " with (updlock,holdlock,rowlock,nowait)" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( lockMode ) {
|
||||
case UPGRADE_NOWAIT:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE: {
|
||||
appendSql( " with (updlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
case PESSIMISTIC_READ: {
|
||||
appendSql( " with (holdlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
case UPGRADE_SKIPLOCKED: {
|
||||
appendSql( " with (updlock,rowlock,readpast)" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||
// SQL Server does not support the FOR UPDATE clause
|
||||
}
|
||||
|
||||
protected OffsetFetchClauseMode getOffsetFetchClauseMode(QueryPart queryPart) {
|
||||
final DatabaseVersion version = getDialect().getVersion();
|
||||
final boolean hasLimit;
|
||||
final boolean hasOffset;
|
||||
if ( queryPart.isRoot() && hasLimit() ) {
|
||||
hasLimit = getLimit().getMaxRows() != null;
|
||||
hasOffset = getLimit().getFirstRow() != null;
|
||||
}
|
||||
else {
|
||||
hasLimit = queryPart.getFetchClauseExpression() != null;
|
||||
hasOffset = queryPart.getOffsetClauseExpression() != null;
|
||||
}
|
||||
if ( queryPart instanceof QueryGroup ) {
|
||||
// We can't use TOP for set operations
|
||||
if ( hasOffset || hasLimit ) {
|
||||
if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) {
|
||||
return OffsetFetchClauseMode.EMULATED;
|
||||
}
|
||||
else {
|
||||
return OffsetFetchClauseMode.STANDARD;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
if ( version.isBefore( 9 ) || !hasOffset ) {
|
||||
return hasLimit ? OffsetFetchClauseMode.TOP_ONLY : null;
|
||||
}
|
||||
else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) {
|
||||
return OffsetFetchClauseMode.EMULATED;
|
||||
}
|
||||
else {
|
||||
return OffsetFetchClauseMode.STANDARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsSimpleQueryGrouping() {
|
||||
// SQL Server is quite strict i.e. it requires `select .. union all select * from (select ...)`
|
||||
// rather than `select .. union all (select ...)` because parenthesis followed by select
|
||||
// is always treated as a subquery, which is not supported in a set operation
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean shouldEmulateFetchClause(QueryPart queryPart) {
|
||||
// Check if current query part is already row numbering to avoid infinite recursion
|
||||
return getQueryPartForRowNumbering() != queryPart && getOffsetFetchClauseMode( queryPart ) == OffsetFetchClauseMode.EMULATED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQueryGroup(QueryGroup queryGroup) {
|
||||
final Predicate lateralPredicate = this.lateralPredicate;
|
||||
if ( lateralPredicate != null ) {
|
||||
this.lateralPredicate = null;
|
||||
addAdditionalWherePredicate( lateralPredicate );
|
||||
}
|
||||
if ( shouldEmulateFetchClause( queryGroup ) ) {
|
||||
emulateFetchOffsetWithWindowFunctions( queryGroup, !isRowsOnlyFetchClauseType( queryGroup ) );
|
||||
}
|
||||
else {
|
||||
super.visitQueryGroup( queryGroup );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitQuerySpec(QuerySpec querySpec) {
|
||||
if ( shouldEmulateFetchClause( querySpec ) ) {
|
||||
emulateFetchOffsetWithWindowFunctions( querySpec, !isRowsOnlyFetchClauseType( querySpec ) );
|
||||
}
|
||||
else {
|
||||
super.visitQuerySpec( querySpec );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSelectClause(SelectClause selectClause) {
|
||||
if ( lateralPredicate != null ) {
|
||||
addAdditionalWherePredicate( lateralPredicate );
|
||||
lateralPredicate = null;
|
||||
}
|
||||
super.visitSelectClause( selectClause );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean needsRowsToSkip() {
|
||||
return getDialect().getVersion().isBefore( 9 );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderFetchPlusOffsetExpression(
|
||||
Expression fetchClauseExpression,
|
||||
Expression offsetClauseExpression,
|
||||
int offset) {
|
||||
renderFetchPlusOffsetExpressionAsSingleParameter( fetchClauseExpression, offsetClauseExpression, offset );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitSqlSelections(SelectClause selectClause) {
|
||||
final QuerySpec querySpec = (QuerySpec) getQueryPartStack().getCurrent();
|
||||
final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( querySpec );
|
||||
if ( offsetFetchClauseMode == OffsetFetchClauseMode.TOP_ONLY ) {
|
||||
renderTopClause( querySpec, true, true );
|
||||
}
|
||||
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.EMULATED ) {
|
||||
renderTopClause( querySpec, isRowsOnlyFetchClauseType( querySpec ), true );
|
||||
}
|
||||
super.visitSqlSelections( selectClause );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderOrderBy(boolean addWhitespace, List<SortSpecification> sortSpecifications) {
|
||||
if ( sortSpecifications != null && !sortSpecifications.isEmpty() ) {
|
||||
super.renderOrderBy( addWhitespace, sortSpecifications );
|
||||
}
|
||||
else if ( getClauseStack().getCurrent() == Clause.OVER ) {
|
||||
if ( addWhitespace ) {
|
||||
appendSql( ' ' );
|
||||
}
|
||||
renderEmptyOrderBy();
|
||||
}
|
||||
}
|
||||
|
||||
protected void renderEmptyOrderBy() {
|
||||
// Always need an order by clause: https://blog.jooq.org/2014/05/13/sql-server-trick-circumvent-missing-order-by-clause/
|
||||
appendSql( "order by @@version" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||
if ( getDialect().getVersion().isBefore( 9 ) && !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) {
|
||||
throw new IllegalArgumentException( "Can't emulate offset clause in subquery" );
|
||||
}
|
||||
// Note that SQL Server is very strict i.e. it requires an order by clause for TOP or OFFSET
|
||||
final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart );
|
||||
if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) {
|
||||
if ( !queryPart.hasSortSpecifications() ) {
|
||||
appendSql( ' ' );
|
||||
renderEmptyOrderBy();
|
||||
}
|
||||
final Expression offsetExpression;
|
||||
final Expression fetchExpression;
|
||||
final FetchClauseType fetchClauseType;
|
||||
if ( queryPart.isRoot() && hasLimit() ) {
|
||||
prepareLimitOffsetParameters();
|
||||
offsetExpression = getOffsetParameter();
|
||||
fetchExpression = getLimitParameter();
|
||||
fetchClauseType = FetchClauseType.ROWS_ONLY;
|
||||
}
|
||||
else {
|
||||
offsetExpression = queryPart.getOffsetClauseExpression();
|
||||
fetchExpression = queryPart.getFetchClauseExpression();
|
||||
fetchClauseType = queryPart.getFetchClauseType();
|
||||
}
|
||||
if ( offsetExpression == null ) {
|
||||
appendSql( " offset 0 rows" );
|
||||
}
|
||||
else {
|
||||
renderOffset( offsetExpression, true );
|
||||
}
|
||||
|
||||
if ( fetchExpression != null ) {
|
||||
renderFetch( fetchExpression, null, fetchClauseType );
|
||||
}
|
||||
}
|
||||
else if ( offsetFetchClauseMode == OffsetFetchClauseMode.TOP_ONLY && !queryPart.hasSortSpecifications() ) {
|
||||
appendSql( ' ' );
|
||||
renderEmptyOrderBy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderSearchClause(CteStatement cte) {
|
||||
// SQL Server does not support this, but it's just a hint anyway
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderCycleClause(CteStatement cte) {
|
||||
// SQL Server does not support this, but it can be emulated
|
||||
}
|
||||
|
||||
@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 ) {
|
||||
appendSql( "()" );
|
||||
}
|
||||
else if ( expression instanceof Summarization ) {
|
||||
Summarization summarization = (Summarization) expression;
|
||||
renderCommaSeparated( summarization.getGroupings() );
|
||||
appendSql( " with " );
|
||||
appendSql( summarization.getKind().sqlText() );
|
||||
}
|
||||
else {
|
||||
expression.accept( this );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntax() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInInList() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean supportsRowValueConstructorSyntaxInQuantifiedPredicates() {
|
||||
return false;
|
||||
}
|
||||
|
||||
enum OffsetFetchClauseMode {
|
||||
STANDARD,
|
||||
TOP_ONLY,
|
||||
EMULATED;
|
||||
}
|
||||
}
|
|
@ -4,12 +4,13 @@
|
|||
* 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;
|
||||
package org.hibernate.community.dialect;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.SQLServer2005Dialect;
|
||||
import org.hibernate.query.spi.Limit;
|
||||
|
||||
|
@ -29,11 +30,11 @@ import static org.junit.Assert.assertEquals;
|
|||
* @author Chris Cranford
|
||||
*/
|
||||
public class SQLServer2005DialectTestCase extends BaseUnitTestCase {
|
||||
private SQLServer2005Dialect dialect;
|
||||
private SQLServerLegacyDialect dialect;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
dialect = new SQLServer2005Dialect();
|
||||
dialect = new SQLServerLegacyDialect( DatabaseVersion.make( 9 ) );
|
||||
}
|
||||
|
||||
@After
|
|
@ -4,18 +4,21 @@
|
|||
* 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.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.community.dialect.SQLServerLegacyDialect;
|
||||
import org.hibernate.dialect.DatabaseVersion;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.SQLServer2005Dialect;
|
||||
import org.hibernate.orm.test.dialect.unit.lockhint.AbstractLockHintTest;
|
||||
|
||||
/**
|
||||
* @author Vlad Mihalcea
|
||||
*/
|
||||
public class SQLServer2005LockHintsTest extends AbstractLockHintTest {
|
||||
public static final Dialect DIALECT = new SQLServer2005Dialect();
|
||||
public static final Dialect DIALECT = new SQLServerLegacyDialect( DatabaseVersion.make( 9 ) );
|
||||
|
||||
protected String getLockHintUsed() {
|
||||
return "with (updlock,holdlock,rowlock,nowait)";
|
|
@ -82,12 +82,13 @@ import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithM
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||
private final static DatabaseVersion MINIMUM_VERSION = DatabaseVersion.make( 10, 0 );
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 2100;
|
||||
|
||||
private final StandardSequenceExporter exporter;
|
||||
|
||||
public SQLServerDialect() {
|
||||
this( DatabaseVersion.make( 8, 0 ) );
|
||||
this( MINIMUM_VERSION );
|
||||
}
|
||||
|
||||
public SQLServerDialect(DatabaseVersion version) {
|
||||
|
@ -104,6 +105,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
return version.isSameOrAfter(11) ? new SqlServerSequenceExporter(this) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DatabaseVersion getMinimumSupportedVersion() {
|
||||
return MINIMUM_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerDefaultKeywords() {
|
||||
super.registerDefaultKeywords();
|
||||
|
@ -113,13 +119,11 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
protected String columnType(int sqlTypeCode) {
|
||||
switch ( sqlTypeCode ) {
|
||||
// there is no 'double' type in SQL server
|
||||
// but 'float' is double precision by default
|
||||
if ( sqlTypeCode == DOUBLE ) {
|
||||
case DOUBLE:
|
||||
return "float";
|
||||
}
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
switch ( sqlTypeCode ) {
|
||||
// Prefer 'varchar(max)' and 'varbinary(max)' to
|
||||
// the deprecated TEXT and IMAGE types. Note that
|
||||
// the length of a VARCHAR or VARBINARY column must
|
||||
|
@ -133,21 +137,19 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
case BLOB:
|
||||
return "varbinary(max)";
|
||||
case DATE:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "date" : super.columnType( sqlTypeCode );
|
||||
return "date";
|
||||
case TIME:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "time" : super.columnType( sqlTypeCode );
|
||||
return "time";
|
||||
case TIMESTAMP:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "datetime2($p)" : super.columnType( sqlTypeCode );
|
||||
return "datetime2($p)";
|
||||
case TIMESTAMP_WITH_TIMEZONE:
|
||||
return getVersion().isSameOrAfter( 10 ) ? "datetimeoffset($p)" : super.columnType( sqlTypeCode );
|
||||
}
|
||||
return "datetimeoffset($p)";
|
||||
}
|
||||
return super.columnType( sqlTypeCode );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String castType(int sqlTypeCode) {
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
switch ( sqlTypeCode ) {
|
||||
case VARCHAR:
|
||||
case LONG32VARCHAR:
|
||||
|
@ -162,7 +164,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
case BLOB:
|
||||
return "varbinary(max)";
|
||||
}
|
||||
}
|
||||
return super.castType( sqlTypeCode );
|
||||
}
|
||||
|
||||
|
@ -170,10 +171,8 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
protected void registerColumnTypes(TypeContributions typeContributions, ServiceRegistry serviceRegistry) {
|
||||
super.registerColumnTypes( typeContributions, serviceRegistry );
|
||||
final DdlTypeRegistry ddlTypeRegistry = typeContributions.getTypeConfiguration().getDdlTypeRegistry();
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOMETRY, "geometry", this ) );
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( GEOGRAPHY, "geography", this ) );
|
||||
}
|
||||
ddlTypeRegistry.addDescriptor( new DdlTypeImpl( SQLXML, "xml", this ) );
|
||||
}
|
||||
|
||||
|
@ -189,7 +188,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public TimeZoneSupport getTimeZoneSupport() {
|
||||
return getVersion().isSameOrAfter( 10 ) ? TimeZoneSupport.NATIVE : TimeZoneSupport.NONE;
|
||||
return TimeZoneSupport.NATIVE;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -248,16 +247,14 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
functionFactory.octetLength_pattern( "datalength(?1)" );
|
||||
functionFactory.bitLength_pattern( "datalength(?1)*8" );
|
||||
|
||||
if ( getVersion().isSameOrAfter( 10 ) ) {
|
||||
functionFactory.locate_charindex();
|
||||
functionFactory.stddevPopSamp_stdevp();
|
||||
functionFactory.varPopSamp_varp();
|
||||
}
|
||||
|
||||
if ( getVersion().isSameOrAfter( 11 ) ) {
|
||||
queryEngine.getSqmFunctionRegistry().register(
|
||||
"format",
|
||||
new SQLServerFormatEmulation( this, queryEngine.getTypeConfiguration() )
|
||||
new SQLServerFormatEmulation( queryEngine.getTypeConfiguration() )
|
||||
);
|
||||
|
||||
//actually translate() was added in 2017 but
|
||||
|
@ -376,19 +373,16 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
if ( getVersion().isSameOrAfter( 11 ) ) {
|
||||
return SQLServer2012LimitHandler.INSTANCE;
|
||||
}
|
||||
else if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
else {
|
||||
//this is a stateful class, don't cache
|
||||
//it in the Dialect!
|
||||
return new SQLServer2005LimitHandler();
|
||||
}
|
||||
else {
|
||||
return new TopLimitHandler(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return getVersion().isSameOrAfter( 10 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -424,7 +418,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public String appendLockHint(LockOptions lockOptions, String tableName) {
|
||||
if ( getVersion().isSameOrAfter( 9 ) ) {
|
||||
LockMode lockMode = lockOptions.getAliasSpecificLockMode( tableName );
|
||||
if (lockMode == null) {
|
||||
lockMode = lockOptions.getLockMode();
|
||||
|
@ -450,21 +443,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
return tableName;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( lockOptions.getLockMode() ) {
|
||||
case UPGRADE_NOWAIT:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE:
|
||||
return tableName + " with (updlock,rowlock)";
|
||||
case PESSIMISTIC_READ:
|
||||
return tableName + " with (holdlock,rowlock)";
|
||||
case UPGRADE_SKIPLOCKED:
|
||||
return tableName + " with (updlock,rowlock,readpast)";
|
||||
default:
|
||||
return tableName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
|
@ -523,17 +501,17 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public boolean supportsNonQueryWithCTE() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsSkipLocked() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsNoWait() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -605,7 +583,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public boolean supportsLateral() {
|
||||
return getVersion().isSameOrAfter( 9 );
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -615,9 +593,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
|
||||
@Override
|
||||
public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() {
|
||||
if ( getVersion().isBefore( 9 ) ) {
|
||||
return super.buildSQLExceptionConversionDelegate(); //null
|
||||
}
|
||||
return (sqlException, message, sql) -> {
|
||||
final String sqlState = JdbcExceptionHelper.extractSqlState( sqlException );
|
||||
final int errorCode = JdbcExceptionHelper.extractErrorCode( sqlException );
|
||||
|
@ -675,11 +650,6 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
|||
case SECOND:
|
||||
//this should evaluate to a floating point type
|
||||
return "(datepart(second,?2)+datepart(nanosecond,?2)/1e9)";
|
||||
case WEEK:
|
||||
// Thanks https://www.sqlservercentral.com/articles/a-simple-formula-to-calculate-the-iso-week-number
|
||||
if ( getVersion().isBefore( 10 ) ) {
|
||||
return "(DATEPART(dy,DATEADD(dd,DATEDIFF(dd,'17530101',?2)/7*7,'17530104'))+6)/7)";
|
||||
}
|
||||
default:
|
||||
return "datepart(?1,?2)";
|
||||
}
|
||||
|
|
|
@ -130,7 +130,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
|
||||
private void renderLockHint(LockMode lockMode) {
|
||||
if ( getDialect().getVersion().isSameOrAfter( 9 ) ) {
|
||||
final int effectiveLockTimeout = getEffectiveLockTimeout( lockMode );
|
||||
switch ( lockMode ) {
|
||||
case PESSIMISTIC_WRITE:
|
||||
|
@ -177,25 +176,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch ( lockMode ) {
|
||||
case UPGRADE_NOWAIT:
|
||||
case PESSIMISTIC_WRITE:
|
||||
case WRITE: {
|
||||
appendSql( " with (updlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
case PESSIMISTIC_READ: {
|
||||
appendSql( " with (holdlock,rowlock)" );
|
||||
break;
|
||||
}
|
||||
case UPGRADE_SKIPLOCKED: {
|
||||
appendSql( " with (updlock,rowlock,readpast)" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderForUpdateClause(QuerySpec querySpec, ForUpdateClause forUpdateClause) {
|
||||
|
@ -228,7 +208,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
return null;
|
||||
}
|
||||
else {
|
||||
if ( version.isBefore( 9 ) || !hasOffset ) {
|
||||
if ( !hasOffset ) {
|
||||
return hasLimit ? OffsetFetchClauseMode.TOP_ONLY : null;
|
||||
}
|
||||
else if ( version.isBefore( 11 ) || !isRowsOnlyFetchClauseType( queryPart ) ) {
|
||||
|
@ -289,7 +269,7 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
|
||||
@Override
|
||||
protected boolean needsRowsToSkip() {
|
||||
return getDialect().getVersion().isBefore( 9 );
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -334,9 +314,6 @@ public class SQLServerSqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
@Override
|
||||
public void visitOffsetFetchClause(QueryPart queryPart) {
|
||||
if ( !isRowNumberingCurrentQueryPart() ) {
|
||||
if ( getDialect().getVersion().isBefore( 9 ) && !queryPart.isRoot() && queryPart.getOffsetClauseExpression() != null ) {
|
||||
throw new IllegalArgumentException( "Can't emulate offset clause in subquery" );
|
||||
}
|
||||
// Note that SQL Server is very strict i.e. it requires an order by clause for TOP or OFFSET
|
||||
final OffsetFetchClauseMode offsetFetchClauseMode = getOffsetFetchClauseMode( queryPart );
|
||||
if ( offsetFetchClauseMode == OffsetFetchClauseMode.STANDARD ) {
|
||||
|
|
|
@ -9,21 +9,12 @@ package org.hibernate.dialect.function;
|
|||
import java.util.List;
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.query.sqm.function.AbstractSqmSelfRenderingFunctionDescriptor;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionArgumentTypeResolvers;
|
||||
import org.hibernate.query.sqm.produce.function.StandardFunctionReturnTypeResolvers;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.spi.SqlAppender;
|
||||
import org.hibernate.sql.ast.tree.SqlAstNode;
|
||||
import org.hibernate.sql.ast.tree.expression.Expression;
|
||||
import org.hibernate.sql.ast.tree.expression.Format;
|
||||
import org.hibernate.type.StandardBasicTypes;
|
||||
import org.hibernate.type.spi.TypeConfiguration;
|
||||
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.STRING;
|
||||
import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEMPORAL;
|
||||
|
||||
/**
|
||||
* SQL Server behaves strangely when the first argument to format is of the type time, so we cast to datetime.
|
||||
*
|
||||
|
@ -31,11 +22,8 @@ import static org.hibernate.query.sqm.produce.function.FunctionParameterType.TEM
|
|||
*/
|
||||
public class SQLServerFormatEmulation extends FormatFunction {
|
||||
|
||||
private final SQLServerDialect dialect;
|
||||
|
||||
public SQLServerFormatEmulation(SQLServerDialect dialect, TypeConfiguration typeConfiguration) {
|
||||
public SQLServerFormatEmulation(TypeConfiguration typeConfiguration) {
|
||||
super( "format", typeConfiguration );
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,644 @@
|
|||
/*
|
||||
* 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.orm.test.dialect;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.LockMode;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.SQLServerDialect;
|
||||
import org.hibernate.query.spi.Limit;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Unit test of the behavior of the SQLServerDialect utility methods
|
||||
*
|
||||
* @author Valotasion Yoryos
|
||||
* @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com)
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class SQLServer2008DialectTestCase extends BaseUnitTestCase {
|
||||
private SQLServerDialect dialect;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
dialect = new SQLServerDialect();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
dialect = null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLimitString() {
|
||||
String input = "select distinct f1 as f53245 from table849752 order by f234, f67 desc";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select distinct top(?) f1 as f53245 from table849752 order by f234, f67 desc) row_)" +
|
||||
" select f53245 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( input, toRowSelection( 10, 15 ) ).toLowerCase(Locale.ROOT) );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10736")
|
||||
public void testGetLimitStringWithNewlineAfterSelect() {
|
||||
final String query = "select" + System.lineSeparator() + "* FROM Employee E WHERE E.firstName = :firstName";
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
query + ") row_) select * from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 25 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10736")
|
||||
public void testGetLimitStringWithNewlineAfterSelectWithMultipleSpaces() {
|
||||
final String query = "select " + System.lineSeparator() + "* FROM Employee E WHERE E.firstName = :firstName";
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
query + ") row_) select * from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 25 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8507")
|
||||
public void testGetLimitStringWithNewlineAfterColumnList() {
|
||||
final String query = "select E.fieldA,E.fieldB\r\nFROM Employee E WHERE E.firstName = :firstName";
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select E.fieldA as col0_,E.fieldB\r\n as col1_" +
|
||||
"FROM Employee E WHERE E.firstName = :firstName) row_) select col0_,col1_ from query_ " +
|
||||
"where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 25 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-6950")
|
||||
public void testGetLimitStringWithFromColumnName() {
|
||||
final String fromColumnNameSQL = "select persistent0_.rid as rid1688_, " +
|
||||
"persistent0_.deviationfromtarget as deviati16_1688_, " + // "from" character sequence as a part of the column name
|
||||
"persistent0_.sortindex as sortindex1688_ " +
|
||||
"from m_evalstate persistent0_ " +
|
||||
"where persistent0_.customerid=?";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
fromColumnNameSQL + ") row_) " +
|
||||
"select rid1688_,deviati16_1688_,sortindex1688_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( fromColumnNameSQL, toRowSelection( 1, 10 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8301")
|
||||
public void testGetLimitStringAliasGeneration() {
|
||||
final String notAliasedSQL = "select column1, column2, column3, column4 from table1";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select column1 as col0_, column2 as col1_, column3 as col2_, column4 as col3_ from table1) row_) " +
|
||||
"select col0_,col1_,col2_,col3_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( notAliasedSQL, toRowSelection( 3, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10994")
|
||||
public void testGetLimitStringAliasGenerationWithAliasesNoAs() {
|
||||
final String aliasedSQLNoAs = "select column1 c1, column c2, column c3, column c4 from table1";
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select column1 c1, column c2, column c3, column c4 from table1) row_) " +
|
||||
"select c1,c2,c3,c4 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( aliasedSQLNoAs, toRowSelection( 3, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11352")
|
||||
public void testPagingWithColumnNameStartingWithFrom() {
|
||||
final String sql = "select column1 c1, from_column c2 from table1";
|
||||
assertEquals( "with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select column1 c1, from_column c2 from table1) row_) " +
|
||||
"select c1,c2 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql(sql, toRowSelection(3, 5)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-7019")
|
||||
public void testGetLimitStringWithSubselect() {
|
||||
final String subselectInSelectClauseSQL = "select persistent0_.id as col_0_0_, " +
|
||||
"(select max(persistent1_.acceptancedate) " +
|
||||
"from av_advisoryvariant persistent1_ " +
|
||||
"where persistent1_.clientid=persistent0_.id) as col_1_0_ " +
|
||||
"from c_customer persistent0_ " +
|
||||
"where persistent0_.type='v'";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
subselectInSelectClauseSQL + ") row_) " +
|
||||
"select col_0_0_,col_1_0_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( subselectInSelectClauseSQL, toRowSelection( 2, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11084")
|
||||
public void testGetLimitStringWithSelectDistinctSubselect() {
|
||||
final String selectDistinctSubselectSQL = "select col0_.CONTENTID as CONTENT1_12_ " +
|
||||
"where col0_.CONTENTTYPE='PAGE' and (col0_.CONTENTID in " +
|
||||
"(select distinct col2_.PREVVER from CONTENT col2_ where (col2_.PREVVER is not null)))";
|
||||
|
||||
assertEquals(
|
||||
"select top(?) col0_.CONTENTID as CONTENT1_12_ " +
|
||||
"where col0_.CONTENTTYPE='PAGE' and (col0_.CONTENTID in " +
|
||||
"(select distinct col2_.PREVVER from CONTENT col2_ where (col2_.PREVVER is not null)))",
|
||||
dialect.getLimitHandler().processSql( selectDistinctSubselectSQL, toRowSelection( 0, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11084")
|
||||
public void testGetLimitStringWithSelectDistinctSubselectNotFirst() {
|
||||
final String selectDistinctSubselectSQL = "select col0_.CONTENTID as CONTENT1_12_ FROM CONTEXT col0_ " +
|
||||
"where col0_.CONTENTTYPE='PAGE' and (col0_.CONTENTID in " +
|
||||
"(select distinct col2_.PREVVER from CONTENT col2_ where (col2_.PREVVER is not null)))";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ " +
|
||||
"from (" + selectDistinctSubselectSQL + ") row_) " +
|
||||
"select CONTENT1_12_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( selectDistinctSubselectSQL, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-6728")
|
||||
public void testGetLimitStringCaseSensitive() {
|
||||
final String caseSensitiveSQL = "select persistent0_.id, persistent0_.uid AS tmp1, " +
|
||||
"(select case when persistent0_.name = 'Smith' then 'Neo' else persistent0_.id end) " +
|
||||
"from C_Customer persistent0_ " +
|
||||
"where persistent0_.type='Va' " +
|
||||
"order by persistent0_.Order";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) persistent0_.id as col0_, persistent0_.uid AS tmp1, " +
|
||||
"(select case when persistent0_.name = 'Smith' then 'Neo' else persistent0_.id end) as col1_ " +
|
||||
"from C_Customer persistent0_ where persistent0_.type='Va' order by persistent0_.Order) " +
|
||||
"row_) select col0_,tmp1,col1_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( caseSensitiveSQL, toRowSelection( 1, 2 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-6310")
|
||||
public void testGetLimitStringDistinctWithinAggregation() {
|
||||
final String distinctInAggregateSQL = "select aggregate_function(distinct p.n) as f1 from table849752 p order by f1";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) aggregate_function(distinct p.n) as f1 from table849752 p order by f1) row_) " +
|
||||
"select f1 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( distinctInAggregateSQL, toRowSelection( 2, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10994")
|
||||
public void testGetLimitStringDistinctWithinAggregationWithoutAlias() {
|
||||
final String distinctInAggregateSQL = "select aggregate_function(distinct p.n) from table849752 p order by f1";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) aggregate_function(distinct p.n) as col0_ from table849752 p order by f1) row_) " +
|
||||
"select col0_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( distinctInAggregateSQL, toRowSelection( 2, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10994")
|
||||
public void testGetLimitStringDistinctWithinAggregationWithAliasNoAs() {
|
||||
final String distinctInAggregateSQL = "select aggregate_function(distinct p.n) f1 from table849752 p order by f1";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) aggregate_function(distinct p.n) f1 from table849752 p order by f1) row_) " +
|
||||
"select f1 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( distinctInAggregateSQL, toRowSelection( 2, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-7370")
|
||||
public void testGetLimitStringWithMaxOnly() {
|
||||
final String query = "select product2x0_.id as id0_, product2x0_.description as descript2_0_ " +
|
||||
"from Product2 product2x0_ order by product2x0_.id";
|
||||
|
||||
assertEquals(
|
||||
"select top(?) product2x0_.id as id0_, product2x0_.description as descript2_0_ " +
|
||||
"from Product2 product2x0_ order by product2x0_.id",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 0, 1 ) )
|
||||
);
|
||||
|
||||
final String distinctQuery = "select distinct product2x0_.id as id0_, product2x0_.description as descript2_0_ " +
|
||||
"from Product2 product2x0_ order by product2x0_.id";
|
||||
|
||||
assertEquals(
|
||||
"select distinct top(?) product2x0_.id as id0_, product2x0_.description as descript2_0_ " +
|
||||
"from Product2 product2x0_ order by product2x0_.id",
|
||||
dialect.getLimitHandler().processSql( distinctQuery, toRowSelection( 0, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-7781")
|
||||
public void testGetLimitStringWithCastOperator() {
|
||||
final String query = "select cast(lc302_doku6_.redniBrojStavke as varchar(255)) as col_0_0_, lc302_doku6_.dokumentiID as col_1_0_ " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) cast(lc302_doku6_.redniBrojStavke as varchar(255)) as col_0_0_, lc302_doku6_.dokumentiID as col_1_0_ " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC) row_) " +
|
||||
"select col_0_0_,col_1_0_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10994")
|
||||
public void testGetLimitStringWithCastOperatorWithAliasNoAs() {
|
||||
final String query = "select cast(lc302_doku6_.redniBrojStavke as varchar(255)) f1, lc302_doku6_.dokumentiID f2 " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) cast(lc302_doku6_.redniBrojStavke as varchar(255)) f1, lc302_doku6_.dokumentiID f2 " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC) row_) " +
|
||||
"select f1,f2 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-10994")
|
||||
public void testGetLimitStringWithCastOperatorWithoutAliases() {
|
||||
final String query = "select cast(lc302_doku6_.redniBrojStavke as varchar(255)), lc302_doku6_.dokumentiID " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) cast(lc302_doku6_.redniBrojStavke as varchar(255)) as col0_, lc302_doku6_.dokumentiID as col1_ " +
|
||||
"from LC302_Dokumenti lc302_doku6_ order by lc302_doku6_.dokumentiID DESC) row_) " +
|
||||
"select col0_,col1_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8007")
|
||||
public void testGetLimitStringSelectingMultipleColumnsFromSeveralTables() {
|
||||
final String query = "select t1.*, t2.* from tab1 t1, tab2 t2 where t1.ref = t2.ref order by t1.id desc";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) t1.*, t2.* from tab1 t1, tab2 t2 where t1.ref = t2.ref order by t1.id desc) row_) " +
|
||||
"select * from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8007")
|
||||
public void testGetLimitStringSelectingAllColumns() {
|
||||
final String query = "select * from tab1 t1, tab2 t2 where t1.ref = t2.ref order by t1.id desc";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) * from tab1 t1, tab2 t2 where t1.ref = t2.ref order by t1.id desc) row_) " +
|
||||
"select * from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 3 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11145")
|
||||
public void testGetLimitStringWithFromInColumnName() {
|
||||
final String query = "select [Created From Nonstock Item], field2 from table1";
|
||||
|
||||
assertEquals( "with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select [Created From Nonstock Item] as col0_, field2 as col1_ from table1) row_) " +
|
||||
"select col0_,col1_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11145")
|
||||
public void testGetLimitStringWithQuotedColumnNamesAndAlias() {
|
||||
final String query = "select [Created From Item] c1, field2 from table1";
|
||||
|
||||
assertEquals( "with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select [Created From Item] c1, field2 as col0_ from table1) row_) " +
|
||||
"select c1,col0_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11145")
|
||||
public void testGetLimitStringWithQuotedColumnNamesAndAliasWithAs() {
|
||||
final String query = "select [Created From Item] as c1, field2 from table1";
|
||||
|
||||
assertEquals( "with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select [Created From Item] as c1, field2 as col0_ from table1) row_) " +
|
||||
"select c1,col0_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11324")
|
||||
public void testGetLimitStringWithSelectClauseNestedQueryUsingParenthesis() {
|
||||
final String query = "select t1.c1 as col_0_0, (select case when count(t2.c1)>0 then 'ADDED' else 'UNMODIFIED' end from table2 t2 WHERE (t2.c1 in (?))) as col_1_0 from table1 t1 WHERE 1=1 ORDER BY t1.c1 ASC";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) t1.c1 as col_0_0, (select case when count(t2.c1)>0 then 'ADDED' else 'UNMODIFIED' end from table2 t2 WHERE (t2.c1 in (?))) as col_1_0 from table1 t1 WHERE 1=1 ORDER BY t1.c1 ASC) row_) " +
|
||||
"select col_0_0,col_1_0 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11650")
|
||||
public void testGetLimitWithStringValueContainingParenthesis() {
|
||||
final String query = "select t1.c1 as col_0_0 FROM table1 t1 where t1.c1 = '(123' ORDER BY t1.c1 ASC";
|
||||
|
||||
assertEquals(
|
||||
"with query_ as (select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"select top(?) t1.c1 as col_0_0 FROM table1 t1 where t1.c1 = '(123' ORDER BY t1.c1 ASC) row_) select col_0_0 from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 1, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11324")
|
||||
public void testGetLimitStringWithSelectClauseNestedQueryUsingParenthesisOnlyTop() {
|
||||
final String query = "select t1.c1 as col_0_0, (select case when count(t2.c1)>0 then 'ADDED' else 'UNMODIFIED' end from table2 t2 WHERE (t2.c1 in (?))) as col_1_0 from table1 t1 WHERE 1=1 ORDER BY t1.c1 ASC";
|
||||
|
||||
assertEquals(
|
||||
"select top(?) t1.c1 as col_0_0, (select case when count(t2.c1)>0 then 'ADDED' else 'UNMODIFIED' end from table2 t2 WHERE (t2.c1 in (?))) as col_1_0 from table1 t1 WHERE 1=1 ORDER BY t1.c1 ASC",
|
||||
dialect.getLimitHandler().processSql( query, toRowSelection( 0, 5 ) )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8916")
|
||||
public void testGetLimitStringUsingCTEQueryNoOffset() {
|
||||
Limit selection = toRowSelection( 0, 5 );
|
||||
|
||||
// test top-based CTE with single CTE query_ definition with no odd formatting
|
||||
final String query1 = "WITH a (c1, c2) AS (SELECT c1, c2 FROM t) SELECT c1, c2 FROM a";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS (SELECT c1, c2 FROM t) SELECT top(?) c1, c2 FROM a",
|
||||
dialect.getLimitHandler().processSql( query1, selection )
|
||||
);
|
||||
|
||||
// test top-based CTE with single CTE query_ definition and various tab, newline spaces
|
||||
final String query2 = " \n\tWITH a (c1\n\t,c2)\t\nAS (SELECT\n\tc1,c2 FROM t)\t\nSELECT c1, c2 FROM a";
|
||||
assertEquals(
|
||||
"WITH a (c1\n\t,c2)\t\nAS (SELECT\n\tc1,c2 FROM t)\t\nSELECT top(?) c1, c2 FROM a",
|
||||
dialect.getLimitHandler().processSql( query2, selection )
|
||||
);
|
||||
|
||||
// test top-based CTE with multiple CTE query_ definitions with no odd formatting
|
||||
final String query3 = "WITH a (c1, c2) AS (SELECT c1, c2 FROM t1), b (b1, b2) AS (SELECT b1, b2 FROM t2) " +
|
||||
"SELECT c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS (SELECT c1, c2 FROM t1), b (b1, b2) AS (SELECT b1, b2 FROM t2) " +
|
||||
"SELECT top(?) c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1",
|
||||
dialect.getLimitHandler().processSql( query3, selection )
|
||||
);
|
||||
|
||||
// test top-based CTE with multiple CTE query_ definitions and various tab, newline spaces
|
||||
final String query4 = " \n\r\tWITH a (c1, c2) AS\n\r (SELECT c1, c2 FROM t1)\n\r, b (b1, b2)\tAS\t" +
|
||||
"(SELECT b1, b2 FROM t2) SELECT c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS\n\r (SELECT c1, c2 FROM t1)\n\r, b (b1, b2)\tAS\t(SELECT b1, b2 FROM t2)" +
|
||||
" SELECT top(?) c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1",
|
||||
dialect.getLimitHandler().processSql( query4, selection )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-8916")
|
||||
public void testGetLimitStringUsingCTEQueryWithOffset() {
|
||||
Limit selection = toRowSelection( 1, 5 );
|
||||
|
||||
// test non-top based CTE with single CTE query_ definition with no odd formatting
|
||||
final String query1 = "WITH a (c1, c2) AS (SELECT c1, c2 FROM t) SELECT c1, c2 FROM a";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS (SELECT c1, c2 FROM t) , query_ as (select row_.*,row_number() over " +
|
||||
"(order by current_timestamp) as rownumber_ from (SELECT c1 as col0_, c2 as col1_ " +
|
||||
"FROM a) row_) select col0_,col1_ from query_ where rownumber_>=? " +
|
||||
"and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query1, selection )
|
||||
);
|
||||
|
||||
// test non-top based CTE with single CTE query_ definition and various tab, newline spaces
|
||||
final String query2 = " \n\tWITH a (c1\n\t,c2)\t\nAS (SELECT\n\tc1,c2 FROM t)\t\nSELECT c1, c2 FROM a";
|
||||
assertEquals(
|
||||
"WITH a (c1\n\t,c2)\t\nAS (SELECT\n\tc1,c2 FROM t)\t\n, query_ as (select row_.*,row_number()" +
|
||||
" over (order by current_timestamp) as rownumber_ from (SELECT c1 as col0_, c2 " +
|
||||
"as col1_ FROM a) row_) select col0_,col1_ from query_ where rownumber_>=" +
|
||||
"? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query2, selection )
|
||||
);
|
||||
|
||||
// test non-top based CTE with multiple CTE query_ definitions with no odd formatting
|
||||
final String query3 = "WITH a (c1, c2) AS (SELECT c1, c2 FROM t1), b (b1, b2) AS (SELECT b1, b2 FROM t2) " +
|
||||
" SELECT c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS (SELECT c1, c2 FROM t1), b (b1, b2) AS (SELECT b1, b2 FROM t2) , query_ as (" +
|
||||
"select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"SELECT c1 as col0_, c2 as col1_, b1 as col2_, b2 as col3_ FROM t1, t2 WHERE t1.c1 = t2.b1) row_)" +
|
||||
" select col0_,col1_,col2_,col3_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query3, selection )
|
||||
);
|
||||
|
||||
// test top-based CTE with multiple CTE query_ definitions and various tab, newline spaces
|
||||
final String query4 = " \n\r\tWITH a (c1, c2) AS\n\r (SELECT c1, c2 FROM t1)\n\r, b (b1, b2)\tAS\t(SELECT b1, " +
|
||||
"b2 FROM t2) SELECT c1, c2, b1, b2 FROM t1, t2 WHERE t1.c1 = t2.b1";
|
||||
assertEquals(
|
||||
"WITH a (c1, c2) AS\n\r (SELECT c1, c2 FROM t1)\n\r, b (b1, b2)\tAS\t(SELECT b1, b2 FROM t2) , query_ as (" +
|
||||
"select row_.*,row_number() over (order by current_timestamp) as rownumber_ from (" +
|
||||
"SELECT c1 as col0_, c2 as col1_, b1 as col2_, b2 as col3_ FROM t1, t2 WHERE t1.c1 = t2.b1) row_)" +
|
||||
" select col0_,col1_,col2_,col3_ from query_ where rownumber_>=? and rownumber_<?",
|
||||
dialect.getLimitHandler().processSql( query4, selection )
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintReadPastLocking() {
|
||||
final String expectedLockHint = "tab1 with (updlock,rowlock,readpast)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_SKIPLOCKED );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintReadPastLockingNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (updlock,rowlock,readpast,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_SKIPLOCKED );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticRead() {
|
||||
final String expectedLockHint = "tab1 with (holdlock,rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_READ );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticReadNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_READ );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintWrite() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.WRITE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintWriteWithNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.WRITE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoWait() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_NOWAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoWaitNoTimeout() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.UPGRADE_NOWAIT );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgrade() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintUpgradeNoTimeout() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticWrite() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-9635")
|
||||
public void testAppendLockHintPessimisticWriteNoTimeOut() {
|
||||
final String expectedLockHint = "tab1 with (updlock,holdlock,rowlock,nowait)";
|
||||
|
||||
LockOptions lockOptions = new LockOptions( LockMode.PESSIMISTIC_WRITE );
|
||||
lockOptions.setTimeOut( LockOptions.NO_WAIT );
|
||||
String lockHint = dialect.appendLockHint( lockOptions, "tab1" );
|
||||
|
||||
assertEquals( expectedLockHint, lockHint );
|
||||
}
|
||||
|
||||
private Limit toRowSelection(int firstRow, int maxRows) {
|
||||
Limit selection = new Limit();
|
||||
selection.setFirstRow( firstRow );
|
||||
selection.setMaxRows( maxRows );
|
||||
return selection;
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ public class SQLServerLockHintsTest extends AbstractLockHintTest {
|
|||
public static final Dialect DIALECT = new SQLServerDialect();
|
||||
|
||||
protected String getLockHintUsed() {
|
||||
return "with (updlock,rowlock)";
|
||||
return "with (updlock,holdlock,rowlock)";
|
||||
}
|
||||
|
||||
protected Dialect getDialectUnderTest() {
|
||||
|
|
|
@ -29,12 +29,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
* @author Vlad Mihalcea
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-10183")
|
||||
@RequiresDialect(value = SQLServerDialect.class, majorVersion = 10)
|
||||
@RequiresDialect(value = SQLServerDialect.class)
|
||||
@DomainModel(
|
||||
annotatedClasses = SQLServer2008NationalizedScalarQueryTest.User.class
|
||||
annotatedClasses = SQLServerNationalizedScalarQueryTest.User.class
|
||||
)
|
||||
@SessionFactory
|
||||
public class SQLServer2008NationalizedScalarQueryTest {
|
||||
public class SQLServerNationalizedScalarQueryTest {
|
||||
|
||||
|
||||
@Test
|
|
@ -22,7 +22,6 @@ import org.hibernate.tool.hbm2ddl.SchemaExport;
|
|||
import org.hibernate.tool.schema.TargetType;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialect;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -32,8 +31,8 @@ import org.junit.jupiter.api.Test;
|
|||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-10529")
|
||||
@RequiresDialect(value = SQLServerDialect.class, majorVersion = 10)
|
||||
public class SQLServer2008NVarCharTypeTest {
|
||||
@RequiresDialect(value = SQLServerDialect.class)
|
||||
public class SQLServerNVarCharTypeTest {
|
||||
private StandardServiceRegistry ssr;
|
||||
private MetadataImplementor metadata;
|
||||
private SchemaExport schemaExport;
|
Loading…
Reference in New Issue