[jOOQ/jOOQ#9736] InformationSchema, XMLDatabase and XMLGenerator support

This commit is contained in:
Lukas Eder 2024-11-14 15:43:25 +01:00
parent 1859e47554
commit ec256172c5
11 changed files with 354 additions and 11 deletions

View File

@ -40,6 +40,7 @@ package org.jooq.codegen;
import static org.jooq.impl.QOM.GenerationOption.STORED;
import static org.jooq.impl.QOM.GenerationOption.VIRTUAL;
import static org.jooq.tools.StringUtils.isBlank;
import static org.jooq.util.xml.XmlUtils.foreignKeyRule;
import static org.jooq.util.xml.jaxb.TableConstraintType.CHECK;
import static org.jooq.util.xml.jaxb.TableConstraintType.FOREIGN_KEY;
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;
@ -326,6 +327,8 @@ public class XMLGenerator extends AbstractGenerator {
rc.setUniqueConstraintCatalog(referenced.getCatalog().getOutputName());
rc.setUniqueConstraintSchema(referenced.getSchema().getOutputName());
rc.setUniqueConstraintName(referenced.getOutputName());
rc.setDeleteRule(foreignKeyRule(f.getDeleteRule()));
rc.setUpdateRule(foreignKeyRule(f.getUpdateRule()));
is.getTableConstraints().add(tc);
is.getReferentialConstraints().add(rc);

View File

@ -215,6 +215,28 @@ public class DefaultRelations implements Relations {
String uniqueKeyName,
TableDefinition uniqueKeyTable,
boolean enforced
) {
addForeignKey(
foreignKeyName,
foreignKeyTable,
foreignKeyColumn,
uniqueKeyName,
uniqueKeyTable,
enforced,
null,
null
);
}
public void addForeignKey(
String foreignKeyName,
TableDefinition foreignKeyTable,
ColumnDefinition foreignKeyColumn,
String uniqueKeyName,
TableDefinition uniqueKeyTable,
boolean enforced,
ForeignKeyRule deleteRule,
ForeignKeyRule updateRule
) {
UniqueKeyDefinition uk = keys.get(key(uniqueKeyTable, uniqueKeyName));
Key key = key(foreignKeyTable, foreignKeyName);
@ -233,7 +255,17 @@ public class DefaultRelations implements Relations {
return;
}
addForeignKey(foreignKeyName, foreignKeyTable, foreignKeyColumn, uniqueKeyName, uniqueKeyTable, getNextUkColumn(key, uk), enforced);
addForeignKey(
foreignKeyName,
foreignKeyTable,
foreignKeyColumn,
uniqueKeyName,
uniqueKeyTable,
getNextUkColumn(key, uk),
enforced,
deleteRule,
updateRule
);
}
private final Map<Key, Integer> nextUkColumnIndex = new HashMap<>();
@ -257,6 +289,30 @@ public class DefaultRelations implements Relations {
TableDefinition uniqueKeyTable,
Integer positionInUniqueKey,
boolean enforced
) {
addForeignKey(
foreignKeyName,
foreignKeyTable,
foreignKeyColumn,
uniqueKeyName,
uniqueKeyTable,
positionInUniqueKey,
enforced,
null,
null
);
}
public void addForeignKey(
String foreignKeyName,
TableDefinition foreignKeyTable,
ColumnDefinition foreignKeyColumn,
String uniqueKeyName,
TableDefinition uniqueKeyTable,
Integer positionInUniqueKey,
boolean enforced,
ForeignKeyRule deleteRule,
ForeignKeyRule updateRule
) {
if (positionInUniqueKey == null) {
addForeignKey(
@ -265,7 +321,9 @@ public class DefaultRelations implements Relations {
foreignKeyColumn,
uniqueKeyName,
uniqueKeyTable,
enforced
enforced,
deleteRule,
updateRule
);
}
else {
@ -279,7 +337,9 @@ public class DefaultRelations implements Relations {
uniqueKeyName,
uniqueKeyTable,
uniqueKey.getKeyColumns().get(positionInUniqueKey - 1),
enforced
enforced,
deleteRule,
updateRule
);
}
}

View File

@ -44,6 +44,7 @@ import static org.jooq.impl.DSL.name;
import static org.jooq.tools.StringUtils.defaultIfBlank;
import static org.jooq.tools.StringUtils.defaultIfNull;
import static org.jooq.tools.StringUtils.isBlank;
import static org.jooq.util.xml.XmlUtils.foreignKeyRule;
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;
import static org.jooq.util.xml.jaxb.TableConstraintType.UNIQUE;
@ -118,6 +119,7 @@ import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.jooq.tools.jdbc.JDBCUtils;
import org.jooq.util.jaxb.tools.MiniJAXB;
import org.jooq.util.xml.XmlUtils;
import org.jooq.util.xml.jaxb.CheckConstraint;
import org.jooq.util.xml.jaxb.Index;
import org.jooq.util.xml.jaxb.IndexColumnUsage;
@ -441,7 +443,9 @@ public class XMLDatabase extends AbstractDatabase {
uniqueKey,
uniqueKeyTable,
usage.getPositionInUniqueConstraint(),
!FALSE.equals(fktc.isEnforced())
!FALSE.equals(fktc.isEnforced()),
foreignKeyRule(fk.getDeleteRule()),
foreignKeyRule(fk.getUpdateRule())
);
}
}

View File

@ -47,6 +47,7 @@ module org.jooq {
exports org.jooq.tools.reflect;
exports org.jooq.types;
exports org.jooq.util.jaxb.tools;
exports org.jooq.util.xml;
exports org.jooq.util.xml.jaxb;

View File

@ -40,6 +40,7 @@ package org.jooq.impl;
import static org.jooq.impl.QOM.GenerationOption.STORED;
import static org.jooq.impl.QOM.GenerationOption.VIRTUAL;
import static org.jooq.tools.StringUtils.isBlank;
import static org.jooq.util.xml.XmlUtils.foreignKeyRule;
import static org.jooq.util.xml.jaxb.TableConstraintType.CHECK;
import static org.jooq.util.xml.jaxb.TableConstraintType.FOREIGN_KEY;
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;
@ -69,6 +70,7 @@ import org.jooq.SortOrder;
// ...
import org.jooq.Table;
import org.jooq.UniqueKey;
import org.jooq.util.xml.XmlUtils;
import org.jooq.util.xml.jaxb.CheckConstraint;
import org.jooq.util.xml.jaxb.Column;
import org.jooq.util.xml.jaxb.IndexColumnUsage;
@ -530,9 +532,9 @@ final class InformationSchemaExport {
result.getKeyColumnUsages().add(kc);
}
if (constraintType == FOREIGN_KEY) {
if (key instanceof ForeignKey<?, ?> fk) {
ReferentialConstraint rc = new ReferentialConstraint();
UniqueKey<?> uk = ((ForeignKey<?, ?>) key).getKey();
UniqueKey<?> uk = fk.getKey();
String ukCatalogName = catalogName(uk.getTable());
String ukSchemaName = schemaName(uk.getTable());
@ -550,6 +552,8 @@ final class InformationSchemaExport {
rc.setConstraintName(key.getName());
rc.setUniqueConstraintName(uk.getName());
rc.setDeleteRule(foreignKeyRule(fk.getDeleteRule()));
rc.setUpdateRule(foreignKeyRule(fk.getUpdateRule()));
result.getReferentialConstraints().add(rc);
}

View File

@ -49,6 +49,7 @@ import static org.jooq.impl.QOM.GenerationOption.VIRTUAL;
import static org.jooq.impl.Tools.EMPTY_CHECK;
import static org.jooq.impl.Tools.EMPTY_SORTFIELD;
import static org.jooq.tools.StringUtils.defaultIfNull;
import static org.jooq.util.xml.XmlUtils.foreignKeyRule;
import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY;
import java.math.BigInteger;
@ -91,8 +92,10 @@ import org.jooq.TableOptions.TableType;
// ...
import org.jooq.UniqueKey;
import org.jooq.exception.SQLDialectNotSupportedException;
import org.jooq.impl.QOM.ForeignKeyRule;
import org.jooq.impl.QOM.GenerationOption;
import org.jooq.tools.StringUtils;
import org.jooq.util.xml.XmlUtils;
import org.jooq.util.xml.jaxb.CheckConstraint;
import org.jooq.util.xml.jaxb.Column;
import org.jooq.util.xml.jaxb.IndexColumnUsage;
@ -122,7 +125,7 @@ final class InformationSchemaMetaImpl extends AbstractMeta {
private final Map<Schema, List<Sequence<?>>> sequencesPerSchema;
private final List<UniqueKeyImpl<Record>> primaryKeys;
private final Map<Name, UniqueKeyImpl<Record>> keysByName;
private final Map<Name, Name> referentialKeys;
private final Map<Name, ReferentialKey> referentialKeys;
private final Map<Name, IndexImpl> indexesByName;
@ -474,9 +477,15 @@ final class InformationSchemaMetaImpl extends AbstractMeta {
}
for (ReferentialConstraint xr : meta.getReferentialConstraints()) {
Name fk = name(xr.getConstraintCatalog(), xr.getConstraintSchema(), xr.getConstraintName());
referentialKeys.put(
name(xr.getConstraintCatalog(), xr.getConstraintSchema(), xr.getConstraintName()),
name(xr.getUniqueConstraintCatalog(), xr.getUniqueConstraintSchema(), xr.getUniqueConstraintName())
fk,
new ReferentialKey(
fk,
name(xr.getUniqueConstraintCatalog(), xr.getUniqueConstraintSchema(), xr.getUniqueConstraintName()),
foreignKeyRule(xr.getDeleteRule()),
foreignKeyRule(xr.getUpdateRule())
)
);
}
@ -500,7 +509,8 @@ final class InformationSchemaMetaImpl extends AbstractMeta {
continue tableConstraintLoop;
}
UniqueKeyImpl<Record> uniqueKey = keysByName.get(referentialKeys.get(constraintName));
ReferentialKey fk = referentialKeys.get(constraintName);
UniqueKeyImpl<Record> uniqueKey = keysByName.get(fk.uk());
if (uniqueKey == null) {
errors.add("No unique key defined for foreign key " + constraintName);
@ -511,7 +521,10 @@ final class InformationSchemaMetaImpl extends AbstractMeta {
uniqueKey,
table,
xc.getConstraintName(),
c.toArray(new TableField[0])
c.toArray(new TableField[0]),
!FALSE.equals(xc.isEnforced()),
fk.deleteRule(),
fk.updateRule()
);
table.foreignKeys.add(key);
break;
@ -947,4 +960,6 @@ final class InformationSchemaMetaImpl extends AbstractMeta {
private static final <T> List<T> unmodifiableList(List<? extends T> list) {
return list == null ? emptyList() : Collections.unmodifiableList(list);
}
private static final record ReferentialKey(Name fk, Name uk, ForeignKeyRule deleteRule, ForeignKeyRule updateRule) {}
}

View File

@ -429,6 +429,22 @@ public final class Internal {
return createForeignKey(table, (Name) null, fields, key, key.getFieldsArray(), true);
}
/**
* Factory method for foreign keys.
*/
@NotNull
public static final <R extends Record, U extends Record> ForeignKey<R, U> createForeignKey(
UniqueKey<U> key,
Table<R> table,
String name,
TableField<R, ?>[] fields,
boolean enforced,
ForeignKeyRule deleteRule,
ForeignKeyRule updateRule
) {
return createForeignKey(table, DSL.name(name), fields, key, key.getFieldsArray(), enforced, deleteRule, updateRule);
}
/**
* Factory method for foreign keys.
*/

View File

@ -0,0 +1,100 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Other licenses:
* -----------------------------------------------------------------------------
* Commercial licenses for this work are available. These replace the above
* Apache-2.0 license and offer limited warranties, support, maintenance, and
* commercial database integrations.
*
* For more information, please visit: https://www.jooq.org/legal/licensing
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package org.jooq.util.xml;
import org.jooq.impl.QOM;
import org.jooq.util.xml.jaxb.ForeignKeyRule;
import org.jetbrains.annotations.ApiStatus.Internal;
/**
* Internal utilities for this package.
*
* @author Lukas Eder
*/
@Internal
public final class XmlUtils {
/**
* Convert from {@link ForeignKeyRule} (JAXB class) to
* {@link org.jooq.impl.QOM.ForeignKeyRule} (model class).
*/
public static final QOM.ForeignKeyRule foreignKeyRule(ForeignKeyRule rule) {
if (rule == null)
return null;
switch (rule) {
case CASCADE:
return QOM.ForeignKeyRule.CASCADE;
case NO_ACTION:
return QOM.ForeignKeyRule.NO_ACTION;
case RESTRICT:
return QOM.ForeignKeyRule.RESTRICT;
case SET_DEFAULT:
return QOM.ForeignKeyRule.SET_DEFAULT;
case SET_NULL:
return QOM.ForeignKeyRule.SET_NULL;
default:
throw new IllegalArgumentException("Unsupported rule: " + rule);
}
}
/**
* Convert from {@link org.jooq.impl.QOM.ForeignKeyRule} (model class) to
* {@link ForeignKeyRule} (JAXB class).
*/
public static final ForeignKeyRule foreignKeyRule(QOM.ForeignKeyRule rule) {
if (rule == null)
return null;
switch (rule) {
case CASCADE:
return ForeignKeyRule.CASCADE;
case NO_ACTION:
return ForeignKeyRule.NO_ACTION;
case RESTRICT:
return ForeignKeyRule.RESTRICT;
case SET_DEFAULT:
return ForeignKeyRule.SET_DEFAULT;
case SET_NULL:
return ForeignKeyRule.SET_NULL;
default:
throw new IllegalArgumentException("Unsupported rule: " + rule);
}
}
}

View File

@ -0,0 +1,71 @@
package org.jooq.util.xml.jaxb;
import jakarta.xml.bind.annotation.XmlEnum;
import jakarta.xml.bind.annotation.XmlEnumValue;
import jakarta.xml.bind.annotation.XmlType;
/**
* <p>Java class for ForeignKeyRule.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <pre>
* &lt;simpleType name="ForeignKeyRule"&gt;
* &lt;restriction base="{http://www.w3.org/2001/XMLSchema}string"&gt;
* &lt;enumeration value="CASCADE"/&gt;
* &lt;enumeration value="SET NULL"/&gt;
* &lt;enumeration value="SET DEFAULT"/&gt;
* &lt;enumeration value="RESTRICT"/&gt;
* &lt;enumeration value="NO ACTION"/&gt;
* &lt;/restriction&gt;
* &lt;/simpleType&gt;
* </pre>
*
*/
@XmlType(name = "ForeignKeyRule")
@XmlEnum
public enum ForeignKeyRule {
CASCADE("CASCADE"),
@XmlEnumValue("SET NULL")
SET_NULL("SET NULL"),
@XmlEnumValue("SET DEFAULT")
SET_DEFAULT("SET DEFAULT"),
RESTRICT("RESTRICT"),
@XmlEnumValue("NO ACTION")
NO_ACTION("NO ACTION");
private final String value;
ForeignKeyRule(String v) {
value = v;
}
public String value() {
return value;
}
public static ForeignKeyRule fromValue(String v) {
for (ForeignKeyRule c: ForeignKeyRule.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
@Override
public String toString() {
switch (this) {
case SET_NULL:
return "SET NULL";
case SET_DEFAULT:
return "SET DEFAULT";
case NO_ACTION:
return "NO ACTION";
default:
return this.name();
}
}
}

View File

@ -5,6 +5,7 @@ import java.io.Serializable;
import jakarta.xml.bind.annotation.XmlAccessType;
import jakarta.xml.bind.annotation.XmlAccessorType;
import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlSchemaType;
import jakarta.xml.bind.annotation.XmlType;
import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.jooq.util.jaxb.tools.StringAdapter;
@ -28,6 +29,8 @@ import org.jooq.util.jaxb.tools.XMLBuilder;
* &lt;element name="unique_constraint_catalog" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
* &lt;element name="unique_constraint_schema" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/&gt;
* &lt;element name="unique_constraint_name" type="{http://www.w3.org/2001/XMLSchema}string"/&gt;
* &lt;element name="delete_rule" type="{http://www.jooq.org/xsd/jooq-meta-3.20.0.xsd}ForeignKeyRule" minOccurs="0"/&gt;
* &lt;element name="update_rule" type="{http://www.jooq.org/xsd/jooq-meta-3.20.0.xsd}ForeignKeyRule" minOccurs="0"/&gt;
* &lt;/all&gt;
* &lt;/restriction&gt;
* &lt;/complexContent&gt;
@ -65,6 +68,12 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
@XmlElement(name = "unique_constraint_name", required = true)
@XmlJavaTypeAdapter(StringAdapter.class)
protected String uniqueConstraintName;
@XmlElement(name = "delete_rule")
@XmlSchemaType(name = "string")
protected ForeignKeyRule deleteRule;
@XmlElement(name = "update_rule")
@XmlSchemaType(name = "string")
protected ForeignKeyRule updateRule;
public String getConstraintCatalog() {
return constraintCatalog;
@ -114,6 +123,22 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
this.uniqueConstraintName = value;
}
public ForeignKeyRule getDeleteRule() {
return deleteRule;
}
public void setDeleteRule(ForeignKeyRule value) {
this.deleteRule = value;
}
public ForeignKeyRule getUpdateRule() {
return updateRule;
}
public void setUpdateRule(ForeignKeyRule value) {
this.updateRule = value;
}
public ReferentialConstraint withConstraintCatalog(String value) {
setConstraintCatalog(value);
return this;
@ -144,6 +169,16 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
return this;
}
public ReferentialConstraint withDeleteRule(ForeignKeyRule value) {
setDeleteRule(value);
return this;
}
public ReferentialConstraint withUpdateRule(ForeignKeyRule value) {
setUpdateRule(value);
return this;
}
@Override
public final void appendTo(XMLBuilder builder) {
builder.append("constraint_catalog", constraintCatalog);
@ -152,6 +187,8 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
builder.append("unique_constraint_catalog", uniqueConstraintCatalog);
builder.append("unique_constraint_schema", uniqueConstraintSchema);
builder.append("unique_constraint_name", uniqueConstraintName);
builder.append("delete_rule", deleteRule);
builder.append("update_rule", updateRule);
}
@Override
@ -227,6 +264,24 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
return false;
}
}
if (deleteRule == null) {
if (other.deleteRule!= null) {
return false;
}
} else {
if (!deleteRule.equals(other.deleteRule)) {
return false;
}
}
if (updateRule == null) {
if (other.updateRule!= null) {
return false;
}
} else {
if (!updateRule.equals(other.updateRule)) {
return false;
}
}
return true;
}
@ -240,6 +295,8 @@ public class ReferentialConstraint implements Serializable, XMLAppendable
result = ((prime*result)+((uniqueConstraintCatalog == null)? 0 :uniqueConstraintCatalog.hashCode()));
result = ((prime*result)+((uniqueConstraintSchema == null)? 0 :uniqueConstraintSchema.hashCode()));
result = ((prime*result)+((uniqueConstraintName == null)? 0 :uniqueConstraintName.hashCode()));
result = ((prime*result)+((deleteRule == null)? 0 :deleteRule.hashCode()));
result = ((prime*result)+((updateRule == null)? 0 :updateRule.hashCode()));
return result;
}

View File

@ -252,6 +252,8 @@
<element name="unique_constraint_catalog" type="string" minOccurs="0" maxOccurs="1" />
<element name="unique_constraint_schema" type="string" minOccurs="0" maxOccurs="1" />
<element name="unique_constraint_name" type="string" minOccurs="1" maxOccurs="1" />
<element name="delete_rule" type="tns:ForeignKeyRule" minOccurs="0" maxOccurs="1" />
<element name="update_rule" type="tns:ForeignKeyRule" minOccurs="0" maxOccurs="1" />
</all>
</complexType>
@ -484,4 +486,14 @@
<enumeration value="GLOBAL TEMPORARY"/>
</restriction>
</simpleType>
<simpleType name="ForeignKeyRule">
<restriction base="string">
<enumeration value="CASCADE"/>
<enumeration value="SET NULL"/>
<enumeration value="SET DEFAULT"/>
<enumeration value="RESTRICT"/>
<enumeration value="NO ACTION"/>
</restriction>
</simpleType>
</schema>