[jOOQ/jOOQ#15906] Add QOM for Constraint types

This commit is contained in:
Lukas Eder 2024-11-15 18:01:49 +01:00
parent 6b248fdc87
commit 918d3a3831
8 changed files with 250 additions and 887 deletions

View File

@ -341,7 +341,7 @@ implements
if (addConstraint != null) {
if (ctx.family() == FIREBIRD)
ctx.visit(K_ADD).sql(' ').visit(DSL.check(((ConstraintImpl) addConstraint).$check()));
ctx.visit(K_ADD).sql(' ').visit(DSL.check(((QOM.Check) addConstraint).$condition()));
else
ctx.visit(K_ADD).sql(' ').visit(addConstraint);
}

View File

@ -1422,7 +1422,7 @@ implements
}
ctx.sql(' ').visit(K_CONSTRAINT).sql(' ').visit(alterConstraint);
ConstraintImpl.acceptEnforced(ctx, alterConstraintEnforced);
AbstractConstraint.acceptEnforced(ctx, alterConstraintEnforced);
});
ctx.end(ALTER_TABLE_ALTER);
@ -1682,7 +1682,7 @@ implements
.sql(' ')
.visit(dropConstraint);
}
else if (dropConstraintType == PRIMARY_KEY && NO_SUPPORT_DROP_CONSTRAINT.contains(c.dialect()) || ConstraintImpl.NO_SUPPORT_NAMED_PK.contains(c.dialect())) {
else if (dropConstraintType == PRIMARY_KEY && NO_SUPPORT_DROP_CONSTRAINT.contains(c.dialect()) || AbstractConstraint.NO_SUPPORT_NAMED_PK.contains(c.dialect())) {
c.visit(K_DROP).sql(' ').visit(K_PRIMARY_KEY);
}
else {

File diff suppressed because it is too large Load Diff

View File

@ -197,7 +197,7 @@ implements
if (!Tools.isEmpty(constraints)) {
if (ctx.family() == FIREBIRD) {
ctx.formatSeparator().visit(DSL.check(DSL.and(Tools.map(constraints, c -> ((ConstraintImpl) c).$check()))));
ctx.formatSeparator().visit(DSL.check(DSL.and(Tools.map(constraints, c -> ((QOM.Check) c).$condition()))));
}
else {
boolean indent = constraints.size() > 1;

View File

@ -53,6 +53,8 @@ import org.jooq.Function1;
import org.jooq.Record;
import org.jooq.conf.ParamType;
import org.jooq.impl.QOM.WithOrWithoutData;
import org.jooq.impl.QOM;
import org.jooq.impl.QOM.PrimaryKey;
import org.jooq.impl.QOM.TableCommitAction;
import org.jooq.tools.StringUtils;
@ -466,7 +468,7 @@ implements
if (ctx.family() == CLICKHOUSE) {
ctx.formatSeparator().visit(K_ENGINE).sql(' ');
if (anyMatch(tableElements, e -> e instanceof ConstraintImpl && isNotEmpty(((ConstraintImpl) e).$primaryKey())))
if (anyMatch(tableElements, e -> e instanceof PrimaryKey))
ctx.visit(unquotedName("MergeTree")).sql("()");
else
ctx.visit(unquotedName("Log")).sql("()");
@ -544,7 +546,7 @@ implements
// [#6841] SQLite has a weird requirement of the PRIMARY KEY keyword being on the column directly,
// when there is an identity. Thus, we must not repeat the primary key specification here.
if (((ConstraintImpl) constraint).supported(ctx, table) && (ctx.family() != SQLITE || !matchingPrimaryKey(constraint, identity)))
if (((AbstractConstraint) constraint).supported(ctx, table) && (ctx.family() != SQLITE || !matchingPrimaryKey(constraint, identity)))
ctx.sql(',')
.formatSeparator()
.visit(constraint);
@ -607,9 +609,9 @@ implements
// [#10551] [#11268] TODO: Make this behaviour configurable
if (REQUIRE_NON_PK_COLUMNS.contains(ctx.dialect())) {
Field<?>[] primaryKeyColumns = primaryKeyColumns();
List<? extends Field<?>> primaryKeyColumns = primaryKeyColumns();
if (primaryKeyColumns != null && primaryKeyColumns.length == $columns().size()) {
if (primaryKeyColumns != null && primaryKeyColumns.size() == $columns().size()) {
ctx.sql(',').formatSeparator();
ctx.visit(DSL.field(name("dummy")));
@ -632,11 +634,11 @@ implements
return type;
}
private final Field<?>[] primaryKeyColumns() {
private final List<? extends Field<?>> primaryKeyColumns() {
return Tools.findAny(
$constraints(),
c -> c instanceof ConstraintImpl && ((ConstraintImpl) c).$primaryKey() != null,
c -> ((ConstraintImpl) c).$primaryKey()
c -> c instanceof QOM.PrimaryKey,
c -> ((QOM.PrimaryKey) c).$fields()
);
}
@ -645,7 +647,7 @@ implements
}
private final boolean matchingPrimaryKey(Constraint constraint, Field<?> identity) {
if (constraint instanceof ConstraintImpl c)
if (constraint instanceof PrimaryKeyConstraintImpl c)
return c.matchingPrimaryKey(identity);
return false;

View File

@ -124,6 +124,7 @@ import org.jooq.impl.DefaultParseContext.IgnoreQuery;
import org.jooq.impl.QOM.Cascade;
import org.jooq.impl.QOM.CycleOption;
import org.jooq.impl.QOM.ForeignKeyRule;
import org.jooq.impl.QOM.PrimaryKey;
import org.jooq.tools.JooqLogger;
@SuppressWarnings({ "rawtypes", "unchecked" })
@ -378,7 +379,7 @@ final class Interpreter {
MutableTable mt = newTable(table, schema, query.$columns(), query.$select(), query.$comment(), query.$temporary() ? TableOptions.temporaryTable(query.$onCommit()) : TableOptions.table());
for (Constraint constraint : query.$constraints())
addConstraint(query, (ConstraintImpl) constraint, mt);
addConstraint(query, constraint, mt);
for (Index index : query.$indexes()) {
IndexImpl impl = (IndexImpl) index;
@ -386,25 +387,25 @@ final class Interpreter {
}
}
private final void addForeignKey(MutableTable mt, ConstraintImpl impl) {
private final void addForeignKey(MutableTable mt, QOM.ForeignKey foreignKey) {
if (delayForeignKeyDeclarations)
delayForeignKey(mt, impl);
delayForeignKey(mt, foreignKey);
else
addForeignKey0(mt, impl);
addForeignKey0(mt, foreignKey);
}
private static class DelayedForeignKey {
final MutableTable table;
final ConstraintImpl constraint;
final QOM.ForeignKey constraint;
DelayedForeignKey(MutableTable mt, ConstraintImpl constraint) {
DelayedForeignKey(MutableTable mt, QOM.ForeignKey constraint) {
this.table = mt;
this.constraint = constraint;
}
}
private final void delayForeignKey(MutableTable mt, ConstraintImpl impl) {
delayedForeignKeyDeclarations.add(new DelayedForeignKey(mt, impl));
private final void delayForeignKey(MutableTable mt, QOM.ForeignKey foreignKey) {
delayedForeignKeyDeclarations.add(new DelayedForeignKey(mt, foreignKey));
}
private final void applyDelayedForeignKeys() {
@ -417,20 +418,20 @@ final class Interpreter {
}
}
private final void addForeignKey0(MutableTable mt, ConstraintImpl impl) {
MutableSchema ms = getSchema(impl.$referencesTable().getSchema());
private final void addForeignKey0(MutableTable mt, QOM.ForeignKey key) {
MutableSchema ms = getSchema(key.$referencesTable().getSchema());
if (ms == null)
throw notExists(impl.$referencesTable().getSchema());
throw notExists(key.$referencesTable().getSchema());
MutableTable mrf = ms.table(impl.$referencesTable(), true);
MutableTable mrf = ms.table(key.$referencesTable(), true);
MutableUniqueKey mu = null;
if (mrf == null)
throw notExists(impl.$referencesTable());
throw notExists(key.$referencesTable());
List<MutableField> mfs = mt.fields(impl.$foreignKey(), true);
List<MutableField> mrfs = mrf.fields(impl.$references(), true);
List<MutableField> mfs = mt.fields(key.$fields(), true);
List<MutableField> mrfs = mrf.fields(key.$referencesFields(), true);
if (!mrfs.isEmpty())
mu = mrf.uniqueKey(mrfs);
@ -438,10 +439,10 @@ final class Interpreter {
mrfs = (mu = mrf.primaryKey).fields;
if (mu == null)
throw primaryKeyNotExists(impl.$referencesTable());
throw primaryKeyNotExists(key.$referencesTable());
mt.foreignKeys.add(new MutableForeignKey(
(UnqualifiedName) impl.getUnqualifiedName(), mt, mfs, mu, mrfs, impl.$onDelete(), impl.$onUpdate(), impl.$enforced()
(UnqualifiedName) key.getUnqualifiedName(), mt, mfs, mu, mrfs, key.$deleteRule(), key.$updateRule(), key.$enforced()
));
}
@ -589,7 +590,7 @@ final class Interpreter {
for (TableElement fc : query.$add())
if (fc instanceof Field<?> f)
addField(existing, Integer.MAX_VALUE, (UnqualifiedName) fc.getUnqualifiedName(), f.getDataType());
else if (fc instanceof ConstraintImpl c)
else if (fc instanceof Constraint c)
addConstraint(query, c, existing);
else
throw unsupportedQuery(query);
@ -615,7 +616,7 @@ final class Interpreter {
addField(existing, Integer.MAX_VALUE, name, dataType);
}
else if (query.$addConstraint() != null) {
addConstraint(query, (ConstraintImpl) query.$addConstraint(), existing);
addConstraint(query, query.$addConstraint(), existing);
}
else if (query.$alterColumn() != null) {
MutableField existingField = find(existing.fields, query.$alterColumn());
@ -666,30 +667,30 @@ final class Interpreter {
existing.constraint(query.$alterConstraint(), true).enforced = query.$alterConstraintEnforced();
}
else if (query.$dropColumns() != null) {
List<MutableField> fields = existing.fields(query.$dropColumns().toArray(EMPTY_FIELD), false);
List<MutableField> fields = existing.fields(query.$dropColumns(), false);
if (fields.size() < query.$dropColumns().size() && !query.$ifExistsColumn())
existing.fields(query.$dropColumns().toArray(EMPTY_FIELD), true);
existing.fields(query.$dropColumns(), true);
dropColumns(existing, fields, query.$dropCascade());
}
else if (query.$dropConstraint() != null) dropConstraint: {
ConstraintImpl impl = (ConstraintImpl) query.$dropConstraint();
Constraint constraint = query.$dropConstraint();
if (impl.getUnqualifiedName().empty()) {
if (impl.$foreignKey() != null) {
if (constraint.getUnqualifiedName().empty()) {
if (constraint instanceof QOM.ForeignKey) {
throw new DataDefinitionException("Cannot drop unnamed foreign key");
}
else if (impl.$check() != null) {
else if (constraint instanceof QOM.Check) {
throw new DataDefinitionException("Cannot drop unnamed check constraint");
}
else if (impl.$unique() != null) {
else if (constraint instanceof QOM.UniqueKey u) {
Iterator<MutableUniqueKey> uks = existing.uniqueKeys.iterator();
while (uks.hasNext()) {
MutableUniqueKey key = uks.next();
if (key.fieldsEquals(impl.$unique())) {
if (key.fieldsEquals(u.$fields())) {
cascade(key, null, query.$dropCascade());
uks.remove();
break dropConstraint;
@ -701,7 +702,7 @@ final class Interpreter {
else {
Iterator<MutableForeignKey> fks = existing.foreignKeys.iterator();
while (fks.hasNext()) {
if (fks.next().nameEquals((UnqualifiedName) impl.getUnqualifiedName())) {
if (fks.next().nameEquals((UnqualifiedName) constraint.getUnqualifiedName())) {
fks.remove();
break dropConstraint;
}
@ -713,7 +714,7 @@ final class Interpreter {
while (uks.hasNext()) {
MutableUniqueKey key = uks.next();
if (key.nameEquals((UnqualifiedName) impl.getUnqualifiedName())) {
if (key.nameEquals((UnqualifiedName) constraint.getUnqualifiedName())) {
cascade(key, null, query.$dropCascade());
uks.remove();
break dropConstraint;
@ -725,14 +726,14 @@ final class Interpreter {
while (chks.hasNext()) {
MutableCheck check = chks.next();
if (check.nameEquals((UnqualifiedName) impl.getUnqualifiedName())) {
if (check.nameEquals((UnqualifiedName) constraint.getUnqualifiedName())) {
chks.remove();
break dropConstraint;
}
}
if (existing.primaryKey != null) {
if (existing.primaryKey.nameEquals((UnqualifiedName) impl.getUnqualifiedName())) {
if (existing.primaryKey.nameEquals((UnqualifiedName) constraint.getUnqualifiedName())) {
cascade(existing.primaryKey, null, query.$dropCascade());
existing.primaryKey = null;
break dropConstraint;
@ -745,7 +746,7 @@ final class Interpreter {
while (it.hasNext()) {
DelayedForeignKey key = it.next();
if (existing.equals(key.table) && key.constraint.getUnqualifiedName().equals(impl.getUnqualifiedName())) {
if (existing.equals(key.table) && key.constraint.getUnqualifiedName().equals(constraint.getUnqualifiedName())) {
it.remove();
break dropConstraint;
}
@ -805,21 +806,22 @@ final class Interpreter {
existing.fields.add(index, field);
}
private final void addConstraint(Query query, ConstraintImpl impl, MutableTable existing) {
if (!impl.getUnqualifiedName().empty() && existing.constraint(impl) != null)
throw alreadyExists(impl);
private final void addConstraint(Query query, Constraint constraint, MutableTable existing) {
if (!constraint.getUnqualifiedName().empty() && existing.constraint(constraint) != null)
throw alreadyExists(constraint);
if (impl.$primaryKey() != null)
if (constraint instanceof QOM.PrimaryKey p) {
if (existing.primaryKey != null)
throw alreadyExists(impl);
throw alreadyExists(constraint);
else
existing.primaryKey = new MutableUniqueKey((UnqualifiedName) impl.getUnqualifiedName(), existing, existing.fields(impl.$primaryKey(), true), impl.$enforced());
else if (impl.$unique() != null)
existing.uniqueKeys.add(new MutableUniqueKey((UnqualifiedName) impl.getUnqualifiedName(), existing, existing.fields(impl.$unique(), true), impl.$enforced()));
else if (impl.$foreignKey() != null)
addForeignKey(existing, impl);
else if (impl.$check() != null)
existing.checks.add(new MutableCheck((UnqualifiedName) impl.getUnqualifiedName(), existing, impl.$check(), impl.$enforced()));
existing.primaryKey = new MutableUniqueKey((UnqualifiedName) constraint.getUnqualifiedName(), existing, existing.fields(p.$fields(), true), p.$enforced());
}
else if (constraint instanceof QOM.UniqueKey u)
existing.uniqueKeys.add(new MutableUniqueKey((UnqualifiedName) constraint.getUnqualifiedName(), existing, existing.fields(u.$fields(), true), u.$enforced()));
else if (constraint instanceof QOM.ForeignKey f)
addForeignKey(existing, f);
else if (constraint instanceof QOM.Check c)
existing.checks.add(new MutableCheck((UnqualifiedName) constraint.getUnqualifiedName(), existing, c.$condition(), c.$enforced()));
else
throw unsupportedQuery(query);
}
@ -1212,8 +1214,8 @@ final class Interpreter {
// TODO: Support NOT NULL constraints
if (query.$constraints() != null)
for (Constraint constraint : query.$constraints())
if (((ConstraintImpl) constraint).$check() != null)
md.checks.add(new MutableCheck(constraint));
if (constraint instanceof QOM.Check c)
md.checks.add(new MutableCheck(c));
}
private final void accept0(AlterDomainImpl<?> query) {
@ -1234,7 +1236,7 @@ final class Interpreter {
if (find(existing.checks, addConstraint) != null)
throw alreadyExists(addConstraint);
existing.checks.add(new MutableCheck(addConstraint));
existing.checks.add(new MutableCheck((QOM.Check) addConstraint));
}
else if (query.$dropConstraint() != null) {
Constraint dropConstraint = query.$dropConstraint();
@ -1996,7 +1998,7 @@ final class Interpreter {
return constraint(constraint, false);
}
final List<MutableField> fields(Field<?>[] fs, boolean failIfNotFound) {
final List<MutableField> fields(Collection<? extends Field<?>> fs, boolean failIfNotFound) {
List<MutableField> result = new ArrayList<>();
for (Field<?> f : fs) {
@ -2299,23 +2301,23 @@ final class Interpreter {
this.fields = fields;
}
final boolean fieldsEquals(Field<?>[] f) {
if (fields.size() != f.length)
final boolean fieldsEquals(List<? extends Field<?>> f) {
if (fields.size() != f.size())
return false;
else
return allMatch(fields, (x, i) -> x.nameEquals((UnqualifiedName) f[i].getUnqualifiedName()));
return allMatch(fields, (x, i) -> x.nameEquals((UnqualifiedName) f.get(i).getUnqualifiedName()));
}
}
private final class MutableCheck extends MutableConstraint {
Condition condition;
MutableCheck(Constraint constraint) {
MutableCheck(QOM.Check check) {
this(
(UnqualifiedName) constraint.getUnqualifiedName(),
(UnqualifiedName) check.getUnqualifiedName(),
null,
((ConstraintImpl) constraint).$check(),
((ConstraintImpl) constraint).$enforced()
check.$condition(),
check.$enforced()
);
}

View File

@ -767,6 +767,7 @@ import org.jooq.conf.RenderQuotedNames;
import org.jooq.impl.QOM.DocumentOrContent;
import org.jooq.impl.QOM.JSONOnNull;
import org.jooq.impl.QOM.JoinHint;
import org.jooq.impl.QOM.PrimaryKey;
// ...
import org.jooq.impl.QOM.UEmpty;
import org.jooq.impl.QOM.XMLPassingMechanism;
@ -4720,20 +4721,20 @@ final class DefaultParseContext extends AbstractScope implements ParseContext {
PrimaryKeySpecification pk = parsePrimaryKeySpecification(constraint, true);
constraints.add(pk.constraint());
if (pk.identity()) {
ConstraintImpl c = (ConstraintImpl) pk.constraint();
PrimaryKey c = (PrimaryKey) pk.constraint();
replacement:
if (c.$primaryKey().length == 1) {
if (c.$fields().size() == 1) {
for (int i = 0; i < fields.size(); i++) {
Field<?> f = fields.get(i);
if (f.getName().equalsIgnoreCase(c.$primaryKey()[0].getName())) {
if (f.getName().equalsIgnoreCase(c.$fields().get(0).getName())) {
fields.set(i, field(f.getQualifiedName(), f.getDataType().identity(true)));
break replacement;
}
}
throw expected("Column not found: " + c.$primaryKey()[0].getName());
throw expected("Column not found: " + c.$fields().get(0).getName());
}
else
throw expected("Single column primary key with inline identity");

View File

@ -681,18 +681,92 @@ public final class QOM {
@NotNull WithOrdinalityTable<?> $table(Table<?> newTable);
}
public interface PrimaryKey extends Constraint {
/**
* A <code>PRIMARY KEY</code> constraint.
*/
public interface PrimaryKey
extends
org.jooq.Constraint
{
@Override
@NotNull Name $name();
@CheckReturnValue
@NotNull Constraint $name(Name newName);
boolean $enforced();
@CheckReturnValue
@NotNull Constraint $enforced(boolean newEnforced);
@NotNull UnmodifiableList<? extends Field<?>> $fields();
@CheckReturnValue
@NotNull PrimaryKey $fields(UnmodifiableList<? extends Field<?>> newFields);
}
public interface UniqueKey extends Constraint {
/**
* A <code>UNIQUE</code> constraint.
*/
public interface UniqueKey
extends
org.jooq.Constraint
{
@Override
@NotNull Name $name();
@CheckReturnValue
@NotNull Constraint $name(Name newName);
boolean $enforced();
@CheckReturnValue
@NotNull Constraint $enforced(boolean newEnforced);
@NotNull UnmodifiableList<? extends Field<?>> $fields();
@CheckReturnValue
@NotNull UniqueKey $fields(UnmodifiableList<? extends Field<?>> newFields);
}
public interface ForeignKey extends Constraint {
/**
* A <code>FOREIGN KEY</code> constraint.
*/
public interface ForeignKey
extends
org.jooq.Constraint
{
@Override
@NotNull Name $name();
@CheckReturnValue
@NotNull Constraint $name(Name newName);
boolean $enforced();
@CheckReturnValue
@NotNull Constraint $enforced(boolean newEnforced);
@NotNull UnmodifiableList<? extends Field<?>> $fields();
@NotNull Constraint $references();
@CheckReturnValue
@NotNull ForeignKey $fields(UnmodifiableList<? extends Field<?>> newFields);
@NotNull Table<?> $referencesTable();
@CheckReturnValue
@NotNull ForeignKey $referencesTable(Table<?> newReferencesTable);
@NotNull UnmodifiableList<? extends Field<?>> $referencesFields();
@CheckReturnValue
@NotNull ForeignKey $referencesFields(UnmodifiableList<? extends Field<?>> newReferencesFields);
@Nullable ForeignKeyRule $deleteRule();
@CheckReturnValue
@NotNull ForeignKey $deleteRule(ForeignKeyRule newDeleteRule);
@Nullable ForeignKeyRule $updateRule();
@CheckReturnValue
@NotNull ForeignKey $updateRule(ForeignKeyRule newDeleteRule);
}
public interface Check extends Constraint {
/**
* A <code>CHECK</code> constraint.
*/
public interface Check
extends
org.jooq.Constraint
{
@Override
@NotNull Name $name();
@CheckReturnValue
@NotNull Constraint $name(Name newName);
boolean $enforced();
@CheckReturnValue
@NotNull Constraint $enforced(boolean newEnforced);
@NotNull Condition $condition();
@CheckReturnValue
@NotNull Check $condition(Condition newCondition);
}
// -------------------------------------------------------------------------