From 8e89bf6c587f2fb402f2a9626097b1889ca888b2 Mon Sep 17 00:00:00 2001 From: lukaseder Date: Tue, 2 Aug 2016 16:18:48 +0200 Subject: [PATCH] [#5437] Add support for loading the jooq-meta.xsd into org.jooq.Catalog / Schema / Table / etc. --- .../jooq/impl/InformationSchemaMetaImpl.java | 193 +++++++++++++++--- 1 file changed, 160 insertions(+), 33 deletions(-) diff --git a/jOOQ/src/main/java/org/jooq/impl/InformationSchemaMetaImpl.java b/jOOQ/src/main/java/org/jooq/impl/InformationSchemaMetaImpl.java index 76154637a0..8278176c6d 100644 --- a/jOOQ/src/main/java/org/jooq/impl/InformationSchemaMetaImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/InformationSchemaMetaImpl.java @@ -44,6 +44,7 @@ import static java.util.Collections.unmodifiableList; import static org.jooq.impl.DSL.name; import static org.jooq.util.xml.jaxb.TableConstraintType.PRIMARY_KEY; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -51,6 +52,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.xml.bind.JAXB; + import org.jooq.Catalog; import org.jooq.Configuration; import org.jooq.DataType; @@ -67,6 +70,7 @@ import org.jooq.exception.SQLDialectNotSupportedException; import org.jooq.util.xml.jaxb.Column; import org.jooq.util.xml.jaxb.InformationSchema; import org.jooq.util.xml.jaxb.KeyColumnUsage; +import org.jooq.util.xml.jaxb.ReferentialConstraint; import org.jooq.util.xml.jaxb.TableConstraint; /** @@ -75,6 +79,8 @@ import org.jooq.util.xml.jaxb.TableConstraint; final class InformationSchemaMetaImpl implements Meta { private final Configuration configuration; + private final InformationSchema source; + private final List catalogs; private final List schemas; private final Map schemasByName; @@ -84,10 +90,13 @@ final class InformationSchemaMetaImpl implements Meta { private final Map> tablesPerSchema; private final List> sequences; private final Map>> sequencesPerSchema; - private final List> primaryKeys; + private final List> primaryKeys; + private final Map> uniqueKeysByName; + private final Map referentialKeys; - InformationSchemaMetaImpl(Configuration configuration, InformationSchema schema) { + InformationSchemaMetaImpl(Configuration configuration, InformationSchema source) { this.configuration = configuration; + this.source = source; this.catalogs = new ArrayList(); this.schemas = new ArrayList(); this.schemasByName = new HashMap(); @@ -97,14 +106,21 @@ final class InformationSchemaMetaImpl implements Meta { this.tablesPerSchema = new HashMap>(); this.sequences = new ArrayList>(); this.sequencesPerSchema = new HashMap>>(); - this.primaryKeys = new ArrayList>(); + this.primaryKeys = new ArrayList>(); + this.uniqueKeysByName = new HashMap>(); + this.referentialKeys = new HashMap(); - init(schema); + init(source); } - +public static void main(String[] args) { + System.out.println(String.format("x y %0$s", "abc")); +} private final void init(InformationSchema meta) { - // Initialise base collections + List errors = new ArrayList(); + + // Catalogs / Schemas + // ------------------------------------------------------------------------------------------------------------- for (org.jooq.util.xml.jaxb.Schema xs : meta.getSchemata()) { InformationSchemaCatalog catalog = new InformationSchemaCatalog(xs.getCatalogName()); @@ -116,12 +132,25 @@ final class InformationSchemaMetaImpl implements Meta { schemasByName.put(name(xs.getCatalogName(), xs.getSchemaName()), is); } + // Tables + // ------------------------------------------------------------------------------------------------------------- + tableLoop: for (org.jooq.util.xml.jaxb.Table xt : meta.getTables()) { - InformationSchemaTable it = new InformationSchemaTable(xt.getTableName(), schemasByName.get(name(xt.getTableCatalog(), xt.getTableSchema()))); + Name schemaName = name(xt.getTableCatalog(), xt.getTableSchema()); + Schema schema = schemasByName.get(schemaName); + + if (schema == null) { + errors.add(String.format("Schema " + schemaName + " not defined for table " + xt.getTableName())); + continue tableLoop; + } + + InformationSchemaTable it = new InformationSchemaTable(xt.getTableName(), schema); tables.add(it); tablesByName.put(name(xt.getTableCatalog(), xt.getTableSchema(), xt.getTableName()), it); } + // Columns + // ------------------------------------------------------------------------------------------------------------- List columns = new ArrayList(meta.getColumns()); Collections.sort(columns, new Comparator() { @Override @@ -140,6 +169,7 @@ final class InformationSchemaMetaImpl implements Meta { } }); + columnLoop: for (Column xc : columns) { String typeName = xc.getDataType(); int length = xc.getCharacterMaximumLength() == null ? 0 : xc.getCharacterMaximumLength(); @@ -148,52 +178,85 @@ final class InformationSchemaMetaImpl implements Meta { boolean nullable = xc.isIsNullable() == null ? true : xc.isIsNullable(); // TODO: Exception handling should be moved inside SQLDataType + Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName()); + InformationSchemaTable table = tablesByName.get(tableName); + + if (table == null) { + errors.add(String.format("Table " + tableName + " not defined for column " + xc.getColumnName())); + continue columnLoop; + } + AbstractTable.createField( xc.getColumnName(), type(typeName, length, precision, scale, nullable), - tablesByName.get(name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName())) + table ); } + // Constraints + // ------------------------------------------------------------------------------------------------------------- Map>> columnsByConstraint = new HashMap>>(); List keyColumnUsages = new ArrayList(meta.getKeyColumnUsages()); Collections.sort(keyColumnUsages, new Comparator() { @Override public int compare(KeyColumnUsage o1, KeyColumnUsage o2) { - Integer p1 = o1.getOrdinalPosition(); - Integer p2 = o2.getOrdinalPosition(); + int p1 = o1.getOrdinalPosition(); + int p2 = o2.getOrdinalPosition(); - if (p1 == p2) - return 0; - if (p1 == null) - return -1; - if (p2 == null) - return 1; - - return p1.compareTo(p2); + return (p1 < p2) ? -1 : ((p1 == p2) ? 0 : 1); } }); + keyColumnLoop: for (KeyColumnUsage xc : keyColumnUsages) { - Name name = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName()); - List> fields = columnsByConstraint.get(name); + Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName()); + List> fields = columnsByConstraint.get(constraintName); if (fields == null) { fields = new ArrayList>(); - columnsByConstraint.put(name, fields); + columnsByConstraint.put(constraintName, fields); } - InformationSchemaTable table = tablesByName.get(name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName())); - fields.add((TableField) table.field(xc.getColumnName())); + Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName()); + InformationSchemaTable table = tablesByName.get(tableName); + + if (table == null) { + errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName)); + continue keyColumnLoop; + } + + TableField field = (TableField) table.field(xc.getColumnName()); + + if (field == null) { + errors.add(String.format("Column " + xc.getColumnName() + " not defined for table " + tableName)); + continue keyColumnLoop; + } + + fields.add(field); } + tableConstraintLoop: for (TableConstraint xc : meta.getTableConstraints()) { switch (xc.getConstraintType()) { case PRIMARY_KEY: case UNIQUE: { - InformationSchemaTable table = tablesByName.get(name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName())); - List> c = columnsByConstraint.get(name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName())); - UniqueKey key = AbstractKeys.createUniqueKey(table, xc.getConstraintName(), c.toArray(new TableField[0])); + Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName()); + Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName()); + InformationSchemaTable table = tablesByName.get(tableName); + + if (table == null) { + errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName)); + continue tableConstraintLoop; + } + + List> c = columnsByConstraint.get(constraintName); + + if (c == null || c.isEmpty()) { + errors.add(String.format("No columns defined for constraint " + constraintName)); + continue tableConstraintLoop; + } + + UniqueKeyImpl key = (UniqueKeyImpl) AbstractKeys.createUniqueKey(table, xc.getConstraintName(), c.toArray(new TableField[0])); if (xc.getConstraintType() == PRIMARY_KEY) { table.primaryKey = key; @@ -201,13 +264,65 @@ final class InformationSchemaMetaImpl implements Meta { } table.uniqueKeys.add(key); - + uniqueKeysByName.put(constraintName, key); break; } } } + for (ReferentialConstraint xr : meta.getReferentialConstraints()) { + referentialKeys.put( + name(xr.getConstraintCatalog(), xr.getConstraintSchema(), xr.getConstraintName()), + name(xr.getUniqueConstraintCatalog(), xr.getUniqueConstraintSchema(), xr.getUniqueConstraintName()) + ); + } + + tableConstraintLoop: + for (TableConstraint xc : meta.getTableConstraints()) { + switch (xc.getConstraintType()) { + case FOREIGN_KEY: { + Name tableName = name(xc.getTableCatalog(), xc.getTableSchema(), xc.getTableName()); + Name constraintName = name(xc.getConstraintCatalog(), xc.getConstraintSchema(), xc.getConstraintName()); + InformationSchemaTable table = tablesByName.get(tableName); + + if (table == null) { + errors.add(String.format("Table " + tableName + " not defined for constraint " + constraintName)); + continue tableConstraintLoop; + } + + List> c = columnsByConstraint.get(constraintName); + + if (c == null || c.isEmpty()) { + errors.add(String.format("No columns defined for constraint " + constraintName)); + continue tableConstraintLoop; + } + + UniqueKeyImpl uniqueKey = uniqueKeysByName.get(referentialKeys.get(constraintName)); + + if (uniqueKey == null) { + errors.add(String.format("No unique key defined for foreign key " + constraintName)); + continue tableConstraintLoop; + } + + ForeignKey key = AbstractKeys.createForeignKey(uniqueKey, table, xc.getConstraintName(), c.toArray(new TableField[0])); + table.foreignKeys.add(key); + break; + } + } + } + + // Sequences + // ------------------------------------------------------------------------------------------------------------- + sequenceLoop: for (org.jooq.util.xml.jaxb.Sequence xs : meta.getSequences()) { + Name schemaName = name(xs.getSequenceCatalog(), xs.getSequenceSchema()); + Schema schema = schemasByName.get(schemaName); + + if (schema == null) { + errors.add(String.format("Schema " + schemaName + " not defined for sequence " + xs.getSequenceName())); + continue sequenceLoop; + } + String typeName = xs.getDataType(); int length = xs.getCharacterMaximumLength() == null ? 0 : xs.getCharacterMaximumLength(); int precision = xs.getNumericPrecision() == null ? 0 : xs.getNumericPrecision(); @@ -217,14 +332,15 @@ final class InformationSchemaMetaImpl implements Meta { @SuppressWarnings({ "rawtypes", "unchecked" }) InformationSchemaSequence is = new InformationSchemaSequence( xs.getSequenceName(), - schemasByName.get(name(xs.getSequenceCatalog(), xs.getSequenceSchema())), + schema, type(typeName, length, precision, scale, nullable) ); sequences.add(is); } - // Initialise indexes + // Lookups + // ------------------------------------------------------------------------------------------------------------- for (Schema s : schemas) { Catalog c = s.getCatalog(); List list = schemasPerCatalog.get(c); @@ -260,6 +376,9 @@ final class InformationSchemaMetaImpl implements Meta { list.add(q); } + + if (!errors.isEmpty()) + throw new IllegalArgumentException(errors.toString()); } private final DataType type(String typeName, int length, int precision, int scale, boolean nullable) { @@ -350,10 +469,11 @@ final class InformationSchemaMetaImpl implements Meta { /** * Generated UID */ - private static final long serialVersionUID = 4314110578549768267L; + private static final long serialVersionUID = 4314110578549768267L; - UniqueKey primaryKey; - List> uniqueKeys = new ArrayList>(); + UniqueKey primaryKey; + final List> uniqueKeys = new ArrayList>(); + final List> foreignKeys = new ArrayList>(); public InformationSchemaTable(String name, Schema schema) { super(name, schema); @@ -371,7 +491,7 @@ final class InformationSchemaMetaImpl implements Meta { @Override public List> getReferences() { - return super.getReferences(); + return Collections.unmodifiableList(foreignKeys); } } @@ -386,4 +506,11 @@ final class InformationSchemaMetaImpl implements Meta { super(name, schema, type); } } + + @Override + public String toString() { + StringWriter writer = new StringWriter(); + JAXB.marshal(source, writer); + return writer.toString(); + } }