Skip to content

Commit 42c6185

Browse files
committed
HHH-19729 Fix column check constraint hoisting to table level and handle dialects that don't support named check constraints on column level
1 parent b362341 commit 42c6185

File tree

7 files changed

+84
-50
lines changed

7 files changed

+84
-50
lines changed

hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/MariaDBLegacyDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,11 @@ public boolean supportsColumnCheck() {
216216
return getVersion().isSameOrAfter( 10, 2 );
217217
}
218218

219+
@Override
220+
public boolean supportsNamedColumnCheck() {
221+
return false;
222+
}
223+
219224
@Override
220225
public boolean doesRoundTemporalOnOverflow() {
221226
// See https://jira.mariadb.org/browse/MDEV-16991

hibernate-core/src/main/java/org/hibernate/boot/model/internal/AnnotatedColumn.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -967,7 +967,7 @@ void applyCheckConstraints(jakarta.persistence.CheckConstraint[] checkConstraint
967967
if ( isNotEmpty( checkConstraintAnnotationUsages ) ) {
968968
for ( jakarta.persistence.CheckConstraint checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) {
969969
addCheckConstraint(
970-
checkConstraintAnnotationUsage.name(),
970+
nullIfEmpty( checkConstraintAnnotationUsage.name() ),
971971
checkConstraintAnnotationUsage.constraint(),
972972
checkConstraintAnnotationUsage.options()
973973
);
@@ -983,7 +983,7 @@ void applyCheckConstraint(PropertyData inferredData, int length) {
983983
if ( checksAnn != null ) {
984984
final Check[] checkAnns = checksAnn.value();
985985
for ( Check checkAnn : checkAnns ) {
986-
addCheckConstraint( checkAnn.name(), checkAnn.constraints() );
986+
addCheckConstraint( nullIfEmpty( checkAnn.name() ), checkAnn.constraints() );
987987
}
988988
}
989989
else {

hibernate-core/src/main/java/org/hibernate/boot/model/internal/TableBinder.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
4545
import static org.hibernate.internal.util.StringHelper.isQuoted;
4646
import static org.hibernate.internal.util.StringHelper.nullIfBlank;
47+
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
4748
import static org.hibernate.internal.util.StringHelper.unquote;
4849
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
4950

@@ -896,7 +897,7 @@ static void addTableCheck(
896897
for ( jakarta.persistence.CheckConstraint checkConstraintAnnotationUsage : checkConstraintAnnotationUsages ) {
897898
table.addCheck(
898899
new CheckConstraint(
899-
checkConstraintAnnotationUsage.name(),
900+
nullIfEmpty( checkConstraintAnnotationUsage.name() ),
900901
checkConstraintAnnotationUsage.constraint(),
901902
checkConstraintAnnotationUsage.options()
902903
)

hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4188,6 +4188,16 @@ public boolean supportsColumnCheck() {
41884188
return true;
41894189
}
41904190

4191+
/**
4192+
* Does this dialect support named column-level check constraints?
4193+
*
4194+
* @return True if named column-level {@code check} constraints are supported;
4195+
* false otherwise.
4196+
*/
4197+
public boolean supportsNamedColumnCheck() {
4198+
return supportsColumnCheck();
4199+
}
4200+
41914201
/**
41924202
* Does this dialect support table-level check constraints?
41934203
*

hibernate-core/src/main/java/org/hibernate/dialect/MariaDBDialect.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,11 @@ public boolean supportsColumnCheck() {
242242
return true;
243243
}
244244

245+
@Override
246+
public boolean supportsNamedColumnCheck() {
247+
return false;
248+
}
249+
245250
@Override
246251
public boolean doesRoundTemporalOnOverflow() {
247252
// See https://jira.mariadb.org/browse/MDEV-16991

hibernate-core/src/main/java/org/hibernate/tool/schema/internal/ColumnDefinitions.java

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -144,34 +144,41 @@ private static void appendConstraints(
144144
}
145145

146146
if ( dialect.supportsColumnCheck() ) {
147-
// some databases (Maria, SQL Server) don't like multiple 'check' clauses
148147
final List<CheckConstraint> checkConstraints = column.getCheckConstraints();
149-
long anonConstraints = checkConstraints.stream().filter(CheckConstraint::isAnonymous).count();
150-
if ( anonConstraints == 1 ) {
151-
for ( CheckConstraint constraint : checkConstraints ) {
152-
definition.append( constraint.constraintString( dialect ) );
148+
boolean hasAnonymousConstraints = false;
149+
for ( CheckConstraint constraint : checkConstraints ) {
150+
if ( constraint.isAnonymous() ) {
151+
if ( !hasAnonymousConstraints ) {
152+
definition.append(" check (");
153+
hasAnonymousConstraints = true;
154+
}
155+
else {
156+
definition.append(" and ");
157+
}
158+
definition.append( constraint.getConstraintInParens() );
153159
}
154160
}
155-
else {
156-
boolean first = true;
161+
if ( hasAnonymousConstraints ) {
162+
definition.append( ')' );
163+
}
164+
165+
if ( !dialect.supportsTableCheck() ) {
166+
// When table check constraints are not supported, try to render all named constraints
157167
for ( CheckConstraint constraint : checkConstraints ) {
158-
if ( constraint.isAnonymous() ) {
159-
if ( first ) {
160-
definition.append(" check (");
161-
first = false;
162-
}
163-
else {
164-
definition.append(" and ");
165-
}
166-
definition.append( constraint.getConstraintInParens() );
168+
if ( constraint.isNamed() ) {
169+
definition.append( constraint.constraintString( dialect ) );
167170
}
168171
}
169-
if ( !first ) {
170-
definition.append(")");
171-
}
172+
}
173+
else if ( !hasAnonymousConstraints && dialect.supportsNamedColumnCheck() ) {
174+
// Otherwise only render the first named constraint as column constraint if there are no anonymous
175+
// constraints and named column check constraint are supported, because some database don't like
176+
// multiple check clauses.
177+
// Note that the TableExporter will take care of named constraints then
172178
for ( CheckConstraint constraint : checkConstraints ) {
173179
if ( constraint.isNamed() ) {
174180
definition.append( constraint.constraintString( dialect ) );
181+
break;
175182
}
176183
}
177184
}

hibernate-core/src/main/java/org/hibernate/tool/schema/internal/StandardTableExporter.java

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -197,37 +197,43 @@ protected void applyTableTypeString(StringBuilder buf) {
197197

198198
protected void applyTableCheck(Table table, StringBuilder buf) {
199199
if ( dialect.supportsTableCheck() ) {
200-
if ( !dialect.supportsColumnCheck() ) {
201-
for ( Column column : table.getColumns() ) {
202-
// some databases (Maria, SQL Server) don't like multiple 'check' clauses
203-
final List<CheckConstraint> checkConstraints = column.getCheckConstraints();
204-
long anonConstraints = checkConstraints.stream().filter( CheckConstraint::isAnonymous ).count();
205-
if ( anonConstraints == 1 ) {
206-
for ( CheckConstraint constraint : checkConstraints ) {
207-
buf.append( "," ).append( constraint.constraintString( dialect ) );
208-
}
209-
}
210-
else {
211-
boolean first = true;
212-
for ( CheckConstraint constraint : checkConstraints ) {
213-
if ( constraint.isAnonymous() ) {
214-
if ( first ) {
215-
buf.append( "," ).append( " check (" );
216-
first = false;
217-
}
218-
else {
219-
buf.append( " and " );
220-
}
221-
buf.append( constraint.getConstraintInParens() );
200+
for ( Column column : table.getColumns() ) {
201+
final List<CheckConstraint> checkConstraints = column.getCheckConstraints();
202+
boolean hasAnonymousConstraints = false;
203+
if ( !dialect.supportsColumnCheck() ) {
204+
for ( CheckConstraint constraint : checkConstraints ) {
205+
if ( constraint.isAnonymous() ) {
206+
if ( !hasAnonymousConstraints ) {
207+
buf.append( ", check (" );
208+
hasAnonymousConstraints = true;
209+
}
210+
else {
211+
buf.append( " and " );
222212
}
213+
buf.append( constraint.getConstraintInParens() );
223214
}
224-
if ( !first ) {
225-
buf.append( ")" );
215+
}
216+
if ( hasAnonymousConstraints ) {
217+
buf.append( ')' );
218+
}
219+
}
220+
else {
221+
hasAnonymousConstraints = checkConstraints.stream().anyMatch( CheckConstraint::isAnonymous );
222+
}
223+
224+
// Since some databases don't like when multiple check clauses appear for a colum definition,
225+
// named constraints need to be hoisted to the table definition.
226+
// Skip the first named constraint if the column has no anonymous constraints and the dialect
227+
// supports named column check constraints, because ColumnDefinitions will render the first check
228+
// constraint already.
229+
boolean skipNextNamedConstraint = !hasAnonymousConstraints && dialect.supportsNamedColumnCheck();
230+
for ( CheckConstraint constraint : checkConstraints ) {
231+
if ( constraint.isNamed() ) {
232+
if ( skipNextNamedConstraint ) {
233+
skipNextNamedConstraint = false;
226234
}
227-
for ( CheckConstraint constraint : checkConstraints ) {
228-
if ( constraint.isNamed() ) {
229-
buf.append( constraint.constraintString( dialect ) );
230-
}
235+
else {
236+
buf.append( ',' ).append( constraint.constraintString( dialect ) );
231237
}
232238
}
233239
}

0 commit comments

Comments
 (0)