mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-22 02:58:05 +00:00
HHH-15762 work around weird semantics of null in unique index on DB2/T-SQL
This commit is contained in:
parent
0253e1fe7a
commit
5172d8798f
@ -11,6 +11,7 @@
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
@ -32,7 +33,9 @@
|
||||
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.LegacyDB2SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.DB2UniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -41,6 +44,7 @@
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
@ -207,7 +211,9 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
|
||||
}
|
||||
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return new DB2UniqueDelegate( this );
|
||||
return getDB2Version().isSameOrAfter(10,5)
|
||||
? new AlterTableUniqueIndexDelegate( this )
|
||||
: new SkipNullableUniqueDelegate( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -487,6 +493,11 @@ public boolean dropConstraints() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return unique ? " exclude null keys" : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
return getDB2Version().isBefore( 9, 7 )
|
||||
|
@ -17,10 +17,12 @@
|
||||
import org.hibernate.dialect.sequence.DB2iSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
@ -28,6 +30,8 @@
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An SQL dialect for DB2 for iSeries previously known as DB2/400.
|
||||
*
|
||||
@ -69,9 +73,21 @@ public DatabaseVersion getDB2Version() {
|
||||
|
||||
@Override
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return getVersion().isSameOrAfter(7, 3)
|
||||
? new AlterTableUniqueDelegate(this)
|
||||
: super.createUniqueDelegate();
|
||||
return getVersion().isSameOrAfter(7, 1)
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique where not null index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,8 +18,12 @@
|
||||
import org.hibernate.dialect.sequence.DB2zSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
@ -31,6 +35,8 @@
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||
|
||||
/**
|
||||
@ -80,6 +86,25 @@ public DatabaseVersion getDB2Version() {
|
||||
return DB2_LUW_VERSION9;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return getVersion().isSameOrAfter(11)
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique where not null index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
// Supported at least since DB2 z/OS 9.0
|
||||
|
@ -30,6 +30,9 @@
|
||||
import org.hibernate.dialect.sequence.SQLServer16SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SQLServerSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -39,6 +42,7 @@
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
@ -71,6 +75,7 @@
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
@ -91,6 +96,7 @@ public class SQLServerLegacyDialect extends AbstractTransactSQLDialect {
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 2100;
|
||||
|
||||
private final StandardSequenceExporter exporter;
|
||||
private final UniqueDelegate uniqueDelegate;
|
||||
|
||||
public SQLServerLegacyDialect() {
|
||||
this( DatabaseVersion.make( 8, 0 ) );
|
||||
@ -99,17 +105,25 @@ public SQLServerLegacyDialect() {
|
||||
public SQLServerLegacyDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
exporter = createSequenceExporter(version);
|
||||
uniqueDelegate = createUniqueDelgate(version);
|
||||
}
|
||||
|
||||
public SQLServerLegacyDialect(DialectResolutionInfo info) {
|
||||
super(info);
|
||||
exporter = createSequenceExporter(info);
|
||||
uniqueDelegate = createUniqueDelgate(info);
|
||||
}
|
||||
|
||||
private StandardSequenceExporter createSequenceExporter(DatabaseVersion version) {
|
||||
return version.isSameOrAfter(11) ? new SqlServerSequenceExporter(this) : null;
|
||||
}
|
||||
|
||||
private UniqueDelegate createUniqueDelgate(DatabaseVersion version) {
|
||||
return version.isSameOrAfter(10)
|
||||
? new AlterTableUniqueIndexDelegate(this)
|
||||
: new SkipNullableUniqueDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerDefaultKeywords() {
|
||||
super.registerDefaultKeywords();
|
||||
@ -952,6 +966,36 @@ public Exporter<Sequence> getSequenceExporter() {
|
||||
return exporter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique nonclustered index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
if (unique) {
|
||||
StringBuilder tail = new StringBuilder();
|
||||
for ( Column column : columns ) {
|
||||
if ( column.isNullable() ) {
|
||||
tail.append( tail.length() == 0 ? " where " : " and " )
|
||||
.append( column.getQuotedName( this ) )
|
||||
.append( " is not null" );
|
||||
}
|
||||
}
|
||||
return tail.toString();
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static class SqlServerSequenceExporter extends StandardSequenceExporter {
|
||||
|
||||
public SqlServerSequenceExporter(Dialect dialect) {
|
||||
|
@ -17,6 +17,8 @@
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.CountFunction;
|
||||
import org.hibernate.dialect.function.IntegralTimestampaddFunction;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -68,6 +70,7 @@ public class SybaseLegacyDialect extends AbstractTransactSQLDialect {
|
||||
|
||||
//All Sybase dialects share an IN list size limit.
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 250000;
|
||||
private final UniqueDelegate uniqueDelegate = new SkipNullableUniqueDelegate(this);
|
||||
|
||||
public SybaseLegacyDialect() {
|
||||
this( DatabaseVersion.make( 11, 0 ) );
|
||||
@ -338,4 +341,8 @@ public NameQualifierSupport getNameQualifierSupport() {
|
||||
return NameQualifierSupport.CATALOG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
}
|
||||
|
@ -2020,9 +2020,15 @@ private void buildUniqueKeyFromColumnNames(
|
||||
for ( int index = 0; index < size; index++ ) {
|
||||
final String logicalColumnName = columnNames[index];
|
||||
try {
|
||||
final String physicalColumnName = getPhysicalColumnName( table, logicalColumnName );
|
||||
columns[index] = new Column( physicalColumnName );
|
||||
unbound.add( columns[index] );
|
||||
Column column = table.getColumn( buildingContext.getMetadataCollector(), logicalColumnName );
|
||||
if ( column == null ) {
|
||||
throw new AnnotationException(
|
||||
"Table '" + table.getName() + "' has no column named '" + logicalColumnName
|
||||
+ "' matching the column specified in '@UniqueConstraint'"
|
||||
);
|
||||
}
|
||||
columns[index] = column;
|
||||
unbound.add( column );
|
||||
//column equals and hashcode is based on column name
|
||||
}
|
||||
catch ( MappingException e ) {
|
||||
|
@ -91,9 +91,7 @@ public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws
|
||||
}
|
||||
|
||||
private void addConstraintToColumn(final String columnName ) {
|
||||
Column column = table.getColumn(
|
||||
new Column( buildingContext.getMetadataCollector().getPhysicalColumnName( table, columnName ) )
|
||||
);
|
||||
Column column = table.getColumn( buildingContext.getMetadataCollector(), columnName );
|
||||
if ( column == null ) {
|
||||
throw new AnnotationException(
|
||||
"Table '" + table.getName() + "' has no column named '" + columnName
|
||||
|
@ -10,8 +10,6 @@
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.function.CastingConcatFunction;
|
||||
import org.hibernate.dialect.function.TransactSQLStrFunction;
|
||||
import org.hibernate.dialect.unique.CreateTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.query.sqm.NullOrdering;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
@ -51,8 +49,6 @@
|
||||
*/
|
||||
public abstract class AbstractTransactSQLDialect extends Dialect {
|
||||
|
||||
private final UniqueDelegate uniqueDelegate = new CreateTableUniqueDelegate(this);
|
||||
|
||||
public AbstractTransactSQLDialect(DatabaseVersion version) {
|
||||
super(version);
|
||||
}
|
||||
@ -392,9 +388,4 @@ public void appendBinaryLiteral(SqlAppender appender, byte[] bytes) {
|
||||
appender.appendSql( "0x" );
|
||||
PrimitiveByteArrayJavaType.INSTANCE.appendString( appender, bytes );
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
||||
import org.hibernate.dialect.pagination.LimitHandler;
|
||||
import org.hibernate.dialect.sequence.DB2SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.DB2UniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -30,6 +30,7 @@
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.spi.RuntimeModelCreationContext;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
@ -66,6 +67,7 @@
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
@ -181,7 +183,7 @@ protected void registerColumnTypes(TypeContributions typeContributions, ServiceR
|
||||
}
|
||||
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return new DB2UniqueDelegate( this );
|
||||
return new AlterTableUniqueIndexDelegate( this );
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -495,6 +497,11 @@ public boolean dropConstraints() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return unique ? " exclude null keys" : "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public SequenceSupport getSequenceSupport() {
|
||||
return DB2SequenceSupport.INSTANCE;
|
||||
|
@ -16,10 +16,9 @@
|
||||
import org.hibernate.dialect.sequence.DB2iSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.NoSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.sql.ast.SqlAstTranslator;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
@ -27,6 +26,8 @@
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An SQL dialect for DB2 for iSeries previously known as DB2/400.
|
||||
*
|
||||
@ -73,10 +74,15 @@ public DatabaseVersion getDB2Version() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UniqueDelegate createUniqueDelegate() {
|
||||
return getVersion().isSameOrAfter(7, 3)
|
||||
? new AlterTableUniqueDelegate(this)
|
||||
: super.createUniqueDelegate();
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique where not null index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,6 +18,7 @@
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.spi.QueryEngine;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
import org.hibernate.query.sqm.TemporalUnit;
|
||||
@ -27,6 +28,8 @@
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.hibernate.type.SqlTypes.TIMESTAMP_WITH_TIMEZONE;
|
||||
|
||||
/**
|
||||
@ -82,6 +85,18 @@ public DatabaseVersion getDB2Version() {
|
||||
return DB2_LUW_VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique where not null index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsDistinctFromPredicate() {
|
||||
// Supported at least since DB2 z/OS 9.0
|
||||
|
@ -102,6 +102,7 @@
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.internal.util.io.StreamCopier;
|
||||
import org.hibernate.loader.BatchLoadSizingStrategy;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Constraint;
|
||||
import org.hibernate.mapping.ForeignKey;
|
||||
import org.hibernate.mapping.Index;
|
||||
@ -1983,6 +1984,14 @@ public String getCreateTableString() {
|
||||
return "create table";
|
||||
}
|
||||
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
return unique ? "create unique index" : "create index";
|
||||
}
|
||||
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Command used to alter a table.
|
||||
*
|
||||
|
@ -24,6 +24,8 @@
|
||||
import org.hibernate.dialect.sequence.SQLServer16SequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SQLServerSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -33,6 +35,7 @@
|
||||
import org.hibernate.exception.LockTimeoutException;
|
||||
import org.hibernate.exception.spi.SQLExceptionConversionDelegate;
|
||||
import org.hibernate.internal.util.JdbcExceptionHelper;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.query.sqm.CastType;
|
||||
import org.hibernate.query.sqm.FetchClauseType;
|
||||
import org.hibernate.query.sqm.IntervalType;
|
||||
@ -68,6 +71,7 @@
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
@ -80,7 +84,7 @@
|
||||
import static org.hibernate.type.descriptor.DateTimeUtils.appendAsTimestampWithMicros;
|
||||
|
||||
/**
|
||||
* A dialect for Microsoft SQL Server 2000 and above
|
||||
* A dialect for Microsoft SQL Server 2008 and above
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
@ -89,6 +93,7 @@ public class SQLServerDialect extends AbstractTransactSQLDialect {
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 2100;
|
||||
|
||||
private final StandardSequenceExporter exporter;
|
||||
private final UniqueDelegate uniqueDelegate = new AlterTableUniqueIndexDelegate(this);
|
||||
|
||||
public SQLServerDialect() {
|
||||
this( MINIMUM_VERSION );
|
||||
@ -937,12 +942,42 @@ public String[] getDropSchemaCommand(String schemaName) {
|
||||
return super.getDropSchemaCommand( schemaName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexString(boolean unique) {
|
||||
// we only create unique indexes, as opposed to unique constraints,
|
||||
// when the column is nullable, so safe to infer unique => nullable
|
||||
return unique ? "create unique nonclustered index" : "create index";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCreateIndexTail(boolean unique, List<Column> columns) {
|
||||
if (unique) {
|
||||
StringBuilder tail = new StringBuilder();
|
||||
for ( Column column : columns ) {
|
||||
if ( column.isNullable() ) {
|
||||
tail.append( tail.length() == 0 ? " where " : " and " )
|
||||
.append( column.getQuotedName( this ) )
|
||||
.append( " is not null" );
|
||||
}
|
||||
}
|
||||
return tail.toString();
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameQualifierSupport getNameQualifierSupport() {
|
||||
return NameQualifierSupport.BOTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exporter<Sequence> getSequenceExporter() {
|
||||
if ( exporter == null ) {
|
||||
return super.getSequenceExporter();
|
||||
|
@ -10,6 +10,8 @@
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.function.CountFunction;
|
||||
import org.hibernate.dialect.function.IntegralTimestampaddFunction;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.UniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy;
|
||||
import org.hibernate.engine.jdbc.env.spi.IdentifierHelper;
|
||||
@ -50,6 +52,7 @@
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Types;
|
||||
|
||||
import jakarta.persistence.TemporalType;
|
||||
|
||||
|
||||
@ -66,6 +69,7 @@ public class SybaseDialect extends AbstractTransactSQLDialect {
|
||||
|
||||
//All Sybase dialects share an IN list size limit.
|
||||
private static final int PARAM_LIST_SIZE_LIMIT = 250000;
|
||||
private final UniqueDelegate uniqueDelegate = new SkipNullableUniqueDelegate(this);
|
||||
|
||||
public SybaseDialect() {
|
||||
this( MINIMUM_VERSION );
|
||||
@ -350,4 +354,8 @@ public NameQualifierSupport getNameQualifierSupport() {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public UniqueDelegate getUniqueDelegate() {
|
||||
return uniqueDelegate;
|
||||
}
|
||||
}
|
||||
|
@ -9,32 +9,31 @@
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
|
||||
import static org.hibernate.mapping.Index.buildSqlCreateIndexString;
|
||||
|
||||
/**
|
||||
* DB2 does not allow unique constraints on nullable columns, but it
|
||||
* does allow the creation of unique <em>indexes</em> instead, using
|
||||
* a different syntax.
|
||||
* A {@link UniqueDelegate} which uses {@code create unique index} commands when necessary.
|
||||
* <ul>
|
||||
* <li>DB2 does not allow unique constraints on nullable columns, but it does allow the creation
|
||||
* of unique indexes instead, using {@code create unique index ... exclude null keys}.
|
||||
* <li>SQL Server <em>does</em> allow unique constraints on nullable columns, but the semantics
|
||||
* are that two null values are non-unique. So here we need to jump through hoops with the
|
||||
* {@code create unique nonclustered index} command.
|
||||
* </ul>
|
||||
*
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
public class DB2UniqueDelegate extends AlterTableUniqueDelegate {
|
||||
/**
|
||||
* Constructs a DB2UniqueDelegate
|
||||
*
|
||||
* @param dialect The dialect
|
||||
*/
|
||||
public DB2UniqueDelegate( Dialect dialect ) {
|
||||
public class AlterTableUniqueIndexDelegate extends AlterTableUniqueDelegate {
|
||||
public AlterTableUniqueIndexDelegate(Dialect dialect ) {
|
||||
super( dialect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
|
||||
SqlStringGenerationContext context) {
|
||||
if ( hasNullable( uniqueKey ) ) {
|
||||
if ( uniqueKey.hasNullableColumn() ) {
|
||||
return buildSqlCreateIndexString(
|
||||
context,
|
||||
uniqueKey.getName(),
|
||||
@ -53,7 +52,7 @@ public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata m
|
||||
@Override
|
||||
public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata,
|
||||
SqlStringGenerationContext context) {
|
||||
if ( hasNullable( uniqueKey ) ) {
|
||||
if ( uniqueKey.hasNullableColumn() ) {
|
||||
return org.hibernate.mapping.Index.buildSqlDropIndexString(
|
||||
uniqueKey.getName(),
|
||||
context.format( uniqueKey.getTable().getQualifiedTableName() )
|
||||
@ -63,13 +62,4 @@ public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata
|
||||
return super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata, context );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasNullable(UniqueKey uniqueKey) {
|
||||
for ( Column column : uniqueKey.getColumns() ) {
|
||||
if ( column.isNullable() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -16,6 +16,14 @@
|
||||
/**
|
||||
* A {@link UniqueDelegate} which includes the unique constraint in the {@code create table}
|
||||
* statement, except when called during schema migration.
|
||||
* <ul>
|
||||
* <li>For columns marked {@linkplain jakarta.persistence.Column#unique() unique}, this results
|
||||
* in a {@code unique} column definition.
|
||||
* <li>For {@linkplain jakarta.persistence.UniqueConstraint#name named unique keys}, it results
|
||||
* in {@code constraint abc unique(a,b,c)} after the column list in {@code create table}.
|
||||
* <li>For unique keys with no explicit name, it results in {@code unique(x, y)} after the
|
||||
* column list.
|
||||
* </ul>
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
@ -52,17 +60,21 @@ public String getTableCreationUniqueConstraintsFragment(Table table, SqlStringGe
|
||||
// signature of getColumnDefinitionUniquenessFragment() doesn't let me
|
||||
// detect this case. (But that would be easy to fix!)
|
||||
if ( !isSingleColumnUnique( uniqueKey ) ) {
|
||||
fragment.append( ", " );
|
||||
if ( uniqueKey.isNameExplicit() ) {
|
||||
fragment.append( "constraint " ).append( uniqueKey.getName() ).append( " " );
|
||||
}
|
||||
fragment.append( uniqueConstraintSql( uniqueKey ) );
|
||||
appendUniqueConstraint( fragment, uniqueKey );
|
||||
}
|
||||
}
|
||||
return fragment.toString();
|
||||
}
|
||||
}
|
||||
|
||||
protected void appendUniqueConstraint(StringBuilder fragment, UniqueKey uniqueKey) {
|
||||
fragment.append( ", " );
|
||||
if ( uniqueKey.isNameExplicit() ) {
|
||||
fragment.append( "constraint " ).append( uniqueKey.getName() ).append( " " );
|
||||
}
|
||||
fragment.append( uniqueConstraintSql(uniqueKey) );
|
||||
}
|
||||
|
||||
private static boolean isSingleColumnUnique(UniqueKey uniqueKey) {
|
||||
return uniqueKey.getColumns().size() == 1
|
||||
&& uniqueKey.getColumn(0).isUnique();
|
||||
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.dialect.unique;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.UniqueKey;
|
||||
|
||||
/**
|
||||
* A {@link UniqueDelegate} that only creates unique constraints on not-null columns, and ignores requests for
|
||||
* uniqueness for nullable columns.
|
||||
* <p>
|
||||
* Needed because unique constraints on nullable columns in Sybase always consider null values to be non-unique.
|
||||
* There is simply no way to create a unique constraint with the semantics we want on a nullable column in Sybase >:-(
|
||||
* <p>
|
||||
* You might argue that this was a bad decision because if the programmer explicitly specifies an {@code @UniqueKey},
|
||||
* then we should damn well respect their wishes. But the simple answer is that the user should have also specified
|
||||
* {@code @Column(nullable=false)} if that is what they wanted. A unique key on a nullable column just really doesn't
|
||||
* make sense in Sybase, except, perhaps, in some incredibly corner cases.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class SkipNullableUniqueDelegate extends CreateTableUniqueDelegate {
|
||||
public SkipNullableUniqueDelegate(Dialect dialect) {
|
||||
super( dialect );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnDefinitionUniquenessFragment(Column column, SqlStringGenerationContext context) {
|
||||
return column.isNullable() ? "" : super.getColumnDefinitionUniquenessFragment(column, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendUniqueConstraint(StringBuilder fragment, UniqueKey uniqueKey) {
|
||||
if ( !uniqueKey.hasNullableColumn() ) {
|
||||
super.appendUniqueConstraint( fragment, uniqueKey );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata, SqlStringGenerationContext context) {
|
||||
return uniqueKey.hasNullableColumn() ? "" : super.getAlterTableToAddUniqueKeyCommand( uniqueKey, metadata, context );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlterTableToDropUniqueKeyCommand(UniqueKey uniqueKey, Metadata metadata, SqlStringGenerationContext context) {
|
||||
return uniqueKey.hasNullableColumn() ? "" : super.getAlterTableToDropUniqueKeyCommand( uniqueKey, metadata, context );
|
||||
}
|
||||
}
|
@ -32,16 +32,21 @@ public String format(String sql) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
if ( sql.toLowerCase(Locale.ROOT).startsWith( "create table" ) ) {
|
||||
String lowerCaseSql = sql.toLowerCase(Locale.ROOT);
|
||||
if ( lowerCaseSql.startsWith( "create table" ) ) {
|
||||
return formatCreateTable( sql );
|
||||
}
|
||||
else if ( sql.toLowerCase(Locale.ROOT).startsWith( "create" ) ) {
|
||||
return sql;
|
||||
}
|
||||
else if ( sql.toLowerCase(Locale.ROOT).startsWith( "alter table" ) ) {
|
||||
else if ( lowerCaseSql.startsWith( "create index" )
|
||||
|| lowerCaseSql.startsWith("create unique") ) {
|
||||
return formatAlterTable( sql );
|
||||
}
|
||||
else if ( sql.toLowerCase(Locale.ROOT).startsWith( "comment on" ) ) {
|
||||
else if ( lowerCaseSql.startsWith( "create" ) ) {
|
||||
return sql;
|
||||
}
|
||||
else if ( lowerCaseSql.startsWith( "alter table" ) ) {
|
||||
return formatAlterTable( sql );
|
||||
}
|
||||
else if ( lowerCaseSql.startsWith( "comment on" ) ) {
|
||||
return formatCommentOn( sql );
|
||||
}
|
||||
else {
|
||||
|
@ -11,13 +11,11 @@
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.model.naming.Identifier;
|
||||
import org.hibernate.boot.model.relational.Exportable;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.Mapping;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
@ -27,6 +25,9 @@
|
||||
|
||||
/**
|
||||
* A mapping model object representing an {@linkplain jakarta.persistence.Index index} on a relational database table.
|
||||
* <p>
|
||||
* We regularize the semantics of unique constraints on nullable columns: two null values are not considered to be
|
||||
* "equal" for the purpose of determining uniqueness, just as specified by ANSI SQL and common sense.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
@ -49,9 +50,8 @@ public static String buildSqlCreateIndexString(
|
||||
java.util.List<Column> columns,
|
||||
java.util.Map<Column, String> columnOrderMap,
|
||||
boolean unique) {
|
||||
StringBuilder buf = new StringBuilder( "create" )
|
||||
.append( unique ? " unique" : "" )
|
||||
.append( " index " )
|
||||
StringBuilder statement = new StringBuilder( dialect.getCreateIndexString( unique ) )
|
||||
.append( " " )
|
||||
.append( dialect.qualifyIndexName() ? name : StringHelper.unqualify( name ) )
|
||||
.append( " on " )
|
||||
.append( tableName )
|
||||
@ -62,15 +62,17 @@ public static String buildSqlCreateIndexString(
|
||||
first = false;
|
||||
}
|
||||
else {
|
||||
buf.append(", ");
|
||||
statement.append(", ");
|
||||
}
|
||||
buf.append( column.getQuotedName( dialect ) );
|
||||
statement.append( column.getQuotedName( dialect ) );
|
||||
if ( columnOrderMap.containsKey( column ) ) {
|
||||
buf.append( " " ).append( columnOrderMap.get( column ) );
|
||||
statement.append( " " ).append( columnOrderMap.get( column ) );
|
||||
}
|
||||
}
|
||||
buf.append( ")" );
|
||||
return buf.toString();
|
||||
statement.append( ")" );
|
||||
statement.append( dialect.getCreateIndexTail( unique, columns ) );
|
||||
|
||||
return statement.toString();
|
||||
}
|
||||
|
||||
public static String buildSqlCreateIndexString(
|
||||
|
@ -19,6 +19,7 @@
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.Remove;
|
||||
import org.hibernate.boot.Metadata;
|
||||
@ -28,6 +29,7 @@
|
||||
import org.hibernate.boot.model.relational.Namespace;
|
||||
import org.hibernate.boot.model.relational.QualifiedTableName;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.boot.spi.InFlightMetadataCollector;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.tool.schema.extract.spi.ColumnInformation;
|
||||
import org.hibernate.tool.schema.extract.spi.TableInformation;
|
||||
@ -246,6 +248,14 @@ public Column getColumn(Identifier name) {
|
||||
return columns.get( name.getCanonicalName() );
|
||||
}
|
||||
|
||||
@Internal
|
||||
public Column getColumn(InFlightMetadataCollector collector, String logicalName) {
|
||||
if ( name == null ) {
|
||||
return null;
|
||||
}
|
||||
return getColumn( new Column( collector.getPhysicalColumnName( this, logicalName ) ) );
|
||||
}
|
||||
|
||||
public Column getColumn(int n) {
|
||||
final Iterator<Column> iter = columns.values().iterator();
|
||||
for ( int i = 0; i < n - 1; i++ ) {
|
||||
|
@ -60,4 +60,13 @@ public boolean isNameExplicit() {
|
||||
public void setNameExplicit(boolean nameExplicit) {
|
||||
this.nameExplicit = nameExplicit;
|
||||
}
|
||||
|
||||
public boolean hasNullableColumn() {
|
||||
for ( Column column : getColumns() ) {
|
||||
if ( column.isNullable() ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,8 @@
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.dialect.SybaseDialect;
|
||||
import org.hibernate.testing.SkipForDialect;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
@ -21,6 +23,8 @@
|
||||
* @author <a href="mailto:bernhardt.manuel@gmail.com">Manuel Bernhardt</a>
|
||||
* @author Brett Meyer
|
||||
*/
|
||||
@SkipForDialect(value = SybaseDialect.class,
|
||||
comment = "Sybase does not properly support unique constraints on nullable columns")
|
||||
public class UniqueConstraintTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
@ -32,7 +36,7 @@ protected Class[] getAnnotatedClasses() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUniquenessConstraintWithSuperclassProperty() throws Exception {
|
||||
public void testUniquenessConstraintWithSuperclassProperty() {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
Room livingRoom = new Room();
|
||||
@ -55,7 +59,7 @@ public void testUniquenessConstraintWithSuperclassProperty() throws Exception {
|
||||
s.persist(house2);
|
||||
try {
|
||||
s.flush();
|
||||
fail( "Database constraint non-existant" );
|
||||
fail( "Database constraint non-existent" );
|
||||
}
|
||||
catch (PersistenceException e) {
|
||||
assertTyping( JDBCException.class, e.getCause() );
|
||||
|
@ -15,8 +15,6 @@
|
||||
import jakarta.persistence.PersistenceException;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.exception.ConstraintViolationException;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
@ -75,7 +73,7 @@ public static class Customer {
|
||||
@Column(name = "CUSTOMER_ACCOUNT_NUMBER")
|
||||
public Long customerAccountNumber;
|
||||
|
||||
@Basic
|
||||
@Basic(optional = false)
|
||||
@Column(name = "CUSTOMER_ID", unique = true)
|
||||
public String customerId;
|
||||
|
||||
|
@ -149,7 +149,7 @@ public static class ParentData {
|
||||
|
||||
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@JoinColumn(name = "parentId", nullable = false)
|
||||
@OrderColumn(name = "listOrder")
|
||||
@OrderColumn(name = "listOrder", nullable = false)
|
||||
private List<ChildData> children = new ArrayList<>();
|
||||
|
||||
public List<ChildData> getChildren() {
|
||||
|
@ -54,7 +54,7 @@ public void testGeneratedSql() {
|
||||
metadata,
|
||||
false
|
||||
);
|
||||
String expectedMappingTableSql = "create table personAddress (address_id bigint unique, " +
|
||||
String expectedMappingTableSql = "create table personAddress (address_id bigint, " +
|
||||
"person_id bigint not null, primary key (person_id))";
|
||||
|
||||
assertEquals( "Wrong SQL", expectedMappingTableSql, commands.get( 2 ) );
|
||||
|
@ -25,7 +25,7 @@ public abstract class Element {
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
@Column(unique = true)
|
||||
@Column(unique = true, nullable = false)
|
||||
@NaturalId
|
||||
private String code;
|
||||
}
|
||||
|
@ -85,12 +85,14 @@ public void testUniqueConstraintIsCorrectlyGenerated() throws Exception {
|
||||
isUniqueConstraintCreated = isUniqueConstraintCreated
|
||||
|| statement.startsWith("create unique index")
|
||||
&& statement.contains("category (code)")
|
||||
|| statement.startsWith("create unique nonclustered index")
|
||||
&& statement.contains("category (code)")
|
||||
|| statement.startsWith("alter table if exists category add constraint")
|
||||
&& statement.contains("unique (code)")
|
||||
|| statement.startsWith("alter table category add constraint")
|
||||
&& statement.contains("unique (code)")
|
||||
|| statement.startsWith("create table category")
|
||||
&& statement.contains("code " + varchar255 + dialect.getNullColumnString() + " unique")
|
||||
&& statement.contains("code " + varchar255 + " not null unique")
|
||||
|| statement.startsWith("create table category")
|
||||
&& statement.contains("unique(code)");
|
||||
}
|
||||
|
@ -20,10 +20,10 @@
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.DB2Dialect;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.MySQLDialect;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
@ -104,7 +104,7 @@ public void tearDown() {
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-11236")
|
||||
public void testUniqueConstraintIsGenerated() throws Exception {
|
||||
public void testUniqueConstraintIsDropped() throws Exception {
|
||||
|
||||
new IndividuallySchemaMigratorImpl( tool, DefaultSchemaFilter.INSTANCE )
|
||||
.doMigration(
|
||||
@ -114,18 +114,11 @@ public void testUniqueConstraintIsGenerated() throws Exception {
|
||||
new TargetDescriptorImpl()
|
||||
);
|
||||
|
||||
if ( getDialect().getUniqueDelegate() instanceof AlterTableUniqueDelegate ) {
|
||||
if ( getDialect() instanceof MySQLDialect ) {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been dropped",
|
||||
checkDropIndex( "test_entity_item", "item" ),
|
||||
is( true )
|
||||
);
|
||||
if ( !(getDialect().getUniqueDelegate() instanceof SkipNullableUniqueDelegate) ) {
|
||||
if ( getDialect().getUniqueDelegate() instanceof AlterTableUniqueIndexDelegate) {
|
||||
checkDropIndex( "test_entity_item", "item" );
|
||||
}
|
||||
else if ( getDialect() instanceof DB2Dialect ) {
|
||||
checkDB2DropIndex( "test_entity_item", "item" );
|
||||
}
|
||||
else {
|
||||
else if ( getDialect().getUniqueDelegate() instanceof AlterTableUniqueDelegate ) {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been dropped",
|
||||
checkDropConstraint( "test_entity_item", "item" ),
|
||||
@ -133,6 +126,11 @@ else if ( getDialect() instanceof DB2Dialect ) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
assertThat(
|
||||
checkDropConstraint( "test_entity_children", "child" ),
|
||||
is( true )
|
||||
);
|
||||
}
|
||||
|
||||
protected Dialect getDialect() {
|
||||
@ -140,43 +138,42 @@ protected Dialect getDialect() {
|
||||
}
|
||||
|
||||
private boolean checkDropConstraint(String tableName, String columnName) throws IOException {
|
||||
String regex = getDialect().getAlterTableString( tableName ) + " drop constraint";
|
||||
|
||||
String regex = getDialect().getAlterTableString( tableName ) + getDialect().getDropUniqueKeyString();
|
||||
if ( getDialect().supportsIfExistsBeforeConstraintName() ) {
|
||||
regex += " if exists";
|
||||
}
|
||||
regex += " uk_(.)*";
|
||||
if ( getDialect().supportsIfExistsAfterConstraintName() ) {
|
||||
regex += " if exists";
|
||||
}
|
||||
|
||||
return isMatching( regex );
|
||||
}
|
||||
|
||||
private boolean checkDropIndex(String tableName, String columnName) throws IOException {
|
||||
String regex = "alter table ";
|
||||
|
||||
if ( getDialect().supportsIfExistsAfterAlterTable() ) {
|
||||
regex += "if exists ";
|
||||
}
|
||||
regex += tableName;
|
||||
if ( getDialect().supportsIfExistsAfterTableName() ) {
|
||||
regex += " if exists";
|
||||
}
|
||||
regex += " drop index";
|
||||
|
||||
if ( getDialect().supportsIfExistsBeforeConstraintName() ) {
|
||||
regex += " if exists";
|
||||
}
|
||||
regex += " uk.*";
|
||||
regex += "uk_.*";
|
||||
if ( getDialect().supportsIfExistsAfterConstraintName() ) {
|
||||
regex += " if exists";
|
||||
}
|
||||
|
||||
regex += ";";
|
||||
return isMatching( regex );
|
||||
}
|
||||
|
||||
private boolean checkDB2DropIndex(String tableName, String columnName) throws IOException {
|
||||
// private boolean checkAlterTableDropIndex(String tableName, String columnName) throws IOException {
|
||||
// String regex = "alter table ";
|
||||
//
|
||||
// if ( getDialect().supportsIfExistsAfterAlterTable() ) {
|
||||
// regex += "if exists ";
|
||||
// }
|
||||
// regex += tableName;
|
||||
// if ( getDialect().supportsIfExistsAfterTableName() ) {
|
||||
// regex += " if exists";
|
||||
// }
|
||||
// regex += " drop index";
|
||||
//
|
||||
// if ( getDialect().supportsIfExistsBeforeConstraintName() ) {
|
||||
// regex += " if exists";
|
||||
// }
|
||||
// regex += " uk.*";
|
||||
// if ( getDialect().supportsIfExistsAfterConstraintName() ) {
|
||||
// regex += " if exists";
|
||||
// }
|
||||
//
|
||||
// return isMatching( regex );
|
||||
// }
|
||||
|
||||
private boolean checkDropIndex(String tableName, String columnName) throws IOException {
|
||||
String regex = "drop index " + tableName + ".uk.*";
|
||||
return isMatching( regex );
|
||||
}
|
||||
|
@ -18,10 +18,11 @@
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.DB2Dialect;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.AlterTableUniqueIndexDelegate;
|
||||
import org.hibernate.dialect.unique.CreateTableUniqueDelegate;
|
||||
import org.hibernate.dialect.unique.SkipNullableUniqueDelegate;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.tool.hbm2ddl.SchemaExport;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
@ -67,26 +68,28 @@ public void testUniqueConstraintIsGenerated() throws Exception {
|
||||
.setOutputFile( output.getAbsolutePath() )
|
||||
.create( EnumSet.of( TargetType.SCRIPT ), metadata );
|
||||
|
||||
if ( getDialect() instanceof DB2Dialect) {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been generated",
|
||||
isCreateUniqueIndexGenerated("test_entity_item", "item"),
|
||||
is(true)
|
||||
);
|
||||
}
|
||||
else {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been generated",
|
||||
isUniqueConstraintGenerated("test_entity_item", "item"),
|
||||
is(true)
|
||||
);
|
||||
}
|
||||
if ( !(getDialect().getUniqueDelegate() instanceof SkipNullableUniqueDelegate) ) {
|
||||
if ( getDialect().getUniqueDelegate() instanceof AlterTableUniqueIndexDelegate ) {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been generated",
|
||||
isCreateUniqueIndexGenerated("test_entity_item", "item"),
|
||||
is(true)
|
||||
);
|
||||
}
|
||||
else {
|
||||
assertThat(
|
||||
"The test_entity_item table unique constraint has not been generated",
|
||||
isUniqueConstraintGenerated("test_entity_item", "item"),
|
||||
is(true)
|
||||
);
|
||||
}
|
||||
|
||||
assertThat(
|
||||
"The test_entity_children table unique constraint has not been generated",
|
||||
isUniqueConstraintGenerated( "test_entity_children", "child" ),
|
||||
is( true )
|
||||
);
|
||||
assertThat(
|
||||
"The test_entity_children table unique constraint has not been generated",
|
||||
isUniqueConstraintGenerated( "test_entity_children", "child" ),
|
||||
is( true )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private Dialect getDialect() {
|
||||
@ -118,7 +121,8 @@ else if ( dialect.getUniqueDelegate() instanceof AlterTableUniqueDelegate) {
|
||||
}
|
||||
|
||||
private boolean isCreateUniqueIndexGenerated(String tableName, String columnName) throws IOException {
|
||||
String regex = "create unique index uk.* on " + tableName + " \\(" + columnName + "\\);";
|
||||
String regex = "create unique (nonclustered )?index uk.* on " + tableName
|
||||
+ " \\(" + columnName + "\\)( where .*| exclude null keys)?;";
|
||||
final String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase();
|
||||
final String[] split = fileContent.split( System.lineSeparator() );
|
||||
Pattern p = Pattern.compile( regex );
|
||||
|
@ -108,7 +108,7 @@ private SimpleEntity newEntity(int id) {
|
||||
public static class SimpleEntity {
|
||||
@Id
|
||||
public Integer id;
|
||||
@Column(unique = true, name = "entity_key")
|
||||
@Column(unique = true, nullable = false, name = "entity_key")
|
||||
public String key;
|
||||
public String name;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user