diff --git a/jOOQ/src/main/java/org/jooq/Meta.java b/jOOQ/src/main/java/org/jooq/Meta.java
index a214b61fe1..2ada8c788a 100644
--- a/jOOQ/src/main/java/org/jooq/Meta.java
+++ b/jOOQ/src/main/java/org/jooq/Meta.java
@@ -219,6 +219,14 @@ public interface Meta extends Scope {
*/
Queries ddl(DDLExportConfiguration configuration) throws DataAccessException;
+ /**
+ * Generate a migration script to get from this meta data to another one.
+ *
+ * @throws DataAccessException If something went wrong fetching the meta
+ * objects
+ */
+ Queries diff(Meta other) throws DataAccessException;
+
/**
* Export to the {@link InformationSchema} format.
*
diff --git a/jOOQ/src/main/java/org/jooq/impl/AbstractMeta.java b/jOOQ/src/main/java/org/jooq/impl/AbstractMeta.java
index 7e03bf2af7..9142f42989 100644
--- a/jOOQ/src/main/java/org/jooq/impl/AbstractMeta.java
+++ b/jOOQ/src/main/java/org/jooq/impl/AbstractMeta.java
@@ -58,7 +58,6 @@ import org.jooq.Schema;
import org.jooq.Sequence;
import org.jooq.Table;
import org.jooq.UniqueKey;
-import org.jooq.exception.DataAccessException;
import org.jooq.util.xml.jaxb.InformationSchema;
/**
@@ -95,7 +94,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final List getCatalogs() throws DataAccessException {
+ public final List getCatalogs() {
initCatalogs();
return Collections.unmodifiableList(new ArrayList<>(cachedCatalogs.values()));
}
@@ -108,7 +107,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
- protected abstract List getCatalogs0() throws DataAccessException;
+ protected abstract List getCatalogs0();
@Override
public final List getSchemas(String name) {
@@ -127,7 +126,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final List getSchemas() throws DataAccessException {
+ public final List getSchemas() {
initSchemas();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedSchemas.values()));
}
@@ -145,7 +144,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
- protected List getSchemas0() throws DataAccessException {
+ protected List getSchemas0() {
List result = new ArrayList<>();
for (Catalog catalog : getCatalogs())
result.addAll(catalog.getSchemas());
@@ -169,7 +168,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final List> getTables() throws DataAccessException {
+ public final List> getTables() {
initTables();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedTables.values()));
}
@@ -187,7 +186,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
- protected List> getTables0() throws DataAccessException {
+ protected List> getTables0() {
List> result = new ArrayList<>();
for (Schema schema : getSchemas())
result.addAll(schema.getTables());
@@ -211,7 +210,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final List> getSequences() throws DataAccessException {
+ public final List> getSequences() {
initSequences();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedSequences.values()));
}
@@ -229,7 +228,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
- protected List> getSequences0() throws DataAccessException {
+ protected List> getSequences0() {
List> result = new ArrayList<>();
for (Schema schema : getSchemas())
result.addAll(schema.getSequences());
@@ -237,7 +236,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final List> getPrimaryKeys() throws DataAccessException {
+ public final List> getPrimaryKeys() {
initPrimaryKeys();
return Collections.unmodifiableList(cachedPrimaryKeys);
}
@@ -247,7 +246,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
cachedPrimaryKeys = new ArrayList<>(getPrimaryKeys0());
}
- protected List> getPrimaryKeys0() throws DataAccessException {
+ protected List> getPrimaryKeys0() {
List> result = new ArrayList<>();
for (Table> table : getTables())
if (table.getPrimaryKey() != null)
@@ -285,21 +284,26 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
- public final Queries ddl() throws DataAccessException {
+ public final Queries ddl() {
return ddl(new DDLExportConfiguration());
}
// [#9396] TODO Fix this. Subclasses should not need to override this to get
// correct results
@Override
- public /* non-final */ Queries ddl(DDLExportConfiguration exportConfiguration) throws DataAccessException {
+ public /* non-final */ Queries ddl(DDLExportConfiguration exportConfiguration) {
return new DDL(this, exportConfiguration).queries();
}
+ @Override
+ public final Queries diff(Meta other) {
+ return new Diff(configuration(), this, other).queries();
+ }
+
// [#9396] TODO Fix this. Subclasses should not need to override this to get
// correct results
@Override
- public /* non-final */ InformationSchema informationSchema() throws DataAccessException {
+ public /* non-final */ InformationSchema informationSchema() {
return InformationSchemaExport.exportCatalogs(configuration(), getCatalogs());
}
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/Diff.java b/jOOQ/src/main/java/org/jooq/impl/Diff.java
new file mode 100644
index 0000000000..398708a5b1
--- /dev/null
+++ b/jOOQ/src/main/java/org/jooq/impl/Diff.java
@@ -0,0 +1,229 @@
+/*
+ * 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
+ *
+ * http://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
+ * ASL 2.0 and offer limited warranties, support, maintenance, and commercial
+ * database integrations.
+ *
+ * For more information, please visit: http://www.jooq.org/licenses
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ */
+package org.jooq.impl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import org.jooq.Catalog;
+import org.jooq.Configuration;
+import org.jooq.DSLContext;
+import org.jooq.Field;
+import org.jooq.Meta;
+import org.jooq.Named;
+import org.jooq.Queries;
+import org.jooq.Query;
+import org.jooq.Schema;
+import org.jooq.Table;
+
+/**
+ * A class producing a diff between two {@link Meta} objects.
+ *
+ * @author Lukas Eder
+ */
+final class Diff {
+
+ private static final NamedComparator COMP = new NamedComparator();
+ private final DSLContext ctx;
+ private final Meta meta1;
+ private final Meta meta2;
+
+ Diff(Configuration configuration, Meta meta1, Meta meta2) {
+ this.ctx = configuration.dsl();
+ this.meta1 = meta1;
+ this.meta2 = meta2;
+ }
+
+ final Queries queries() {
+ return ctx.queries(appendCatalogs(new ArrayList<>(), sorted(meta1.getCatalogs()), sorted(meta2.getCatalogs())));
+ }
+
+ private final List appendCatalogs(final List queries, final Iterator i1, final Iterator i2) {
+ return append(queries, i1, i2,
+ null,
+ null,
+ new Merge() {
+ @Override
+ public void merge(List q, Catalog c1, Catalog c2) {
+ appendSchemas(q, sorted(c1.getSchemas()), sorted(c2.getSchemas()));
+ }
+ }
+ );
+ }
+
+ private final List appendSchemas(final List queries, final Iterator i1, final Iterator i2) {
+ // TODO Cascade semantics when creating and deleting
+ return append(queries, i1, i2,
+ new Create() {
+ @Override
+ public void create(List q, Schema s) {
+ q.add(ctx.createSchema(s));
+ }
+ },
+ new Drop() {
+ @Override
+ public void drop(List q, Schema s) {
+ q.add(ctx.dropSchema(s));
+ }
+ },
+ new Merge() {
+ @Override
+ public void merge(List q, Schema s1, Schema s2) {
+ appendTables(q, sorted(s1.getTables()), sorted(s2.getTables()));
+ }
+ }
+ );
+ }
+
+ private final List appendTables(final List queries, final Iterator> i1, final Iterator> i2) {
+ // TODO Cascade semantics when creating and deleting
+ return append(queries, i1, i2,
+ new Create>() {
+ @Override
+ public void create(List q, Table> t) {
+ q.addAll(Arrays.asList(ctx.ddl(t).queries()));
+ }
+ },
+ new Drop>() {
+ @Override
+ public void drop(List q, Table> t) {
+ q.add(ctx.dropTable(t));
+ }
+ },
+ new Merge>() {
+ @Override
+ public void merge(List q, Table> t1, Table> t2) {
+ appendColumns(queries, t1, t2, sorted(t1.fields()), sorted(t2.fields()));
+ }
+ }
+ );
+ }
+
+ private final List appendColumns(final List queries, final Table> t1, final Table> t2, final Iterator> i1, final Iterator> i2) {
+ return append(queries, i1, i2,
+ (q, f) -> q.add(ctx.alterTable(t1).add(f)),
+ (q, f) -> q.add(ctx.alterTable(t1).drop(f)),
+ null
+ );
+ }
+
+ private final List append(
+ List queries,
+ Iterator i1,
+ Iterator i2,
+ Create create,
+ Drop drop,
+ Merge merge
+ ) {
+ N s1 = null;
+ N s2 = null;
+
+ for (;;) {
+ if (s1 == null && i1.hasNext())
+ s1 = i1.next();
+
+ if (s2 == null && i2.hasNext())
+ s2 = i2.next();
+
+ if (s1 == null && s2 == null)
+ break;
+
+ int comp = s1 == null
+ ? 1
+ : s2 == null
+ ? -1
+ : s1.getQualifiedName().compareTo(s2.getQualifiedName());
+
+ if (comp < 0) {
+ if (drop != null)
+ drop.drop(queries, s1);
+
+ s1 = null;
+ }
+ else if (comp > 0) {
+ if (create != null)
+ create.create(queries, s2);
+
+ s2 = null;
+ }
+ else {
+ if (merge != null)
+ merge.merge(queries, s1, s2);
+
+ s1 = s2 = null;
+ }
+ }
+
+ return queries;
+ }
+
+ private static interface Create {
+ void create(List queries, N named);
+ }
+
+ private static interface Drop {
+ void drop(List queries, N named);
+ }
+
+ private static interface Merge {
+ void merge(List queries, N named1, N named2);
+ }
+
+ private static final Iterator sorted(N... array) {
+ List result = Arrays.asList(array);
+ Collections.sort(result, COMP);
+ return result.iterator();
+ }
+
+ private static final Iterator sorted(List list) {
+ List result = new ArrayList<>(list);
+ Collections.sort(result, COMP);
+ return result.iterator();
+ }
+
+ private static final class NamedComparator implements Comparator {
+ @Override
+ public int compare(Named o1, Named o2) {
+ return o1.getQualifiedName().compareTo(o2.getQualifiedName());
+ }
+ }
+}