[jOOQ/jOOQ#9425] Add Meta.diff(Meta) WIP

This commit is contained in:
Lukas Eder 2019-10-23 12:26:16 +02:00
parent a94ae56491
commit dca2b4b6e3
3 changed files with 255 additions and 14 deletions

View File

@ -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.
* <p>

View File

@ -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<Catalog> getCatalogs() throws DataAccessException {
public final List<Catalog> getCatalogs() {
initCatalogs();
return Collections.unmodifiableList(new ArrayList<>(cachedCatalogs.values()));
}
@ -108,7 +107,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
protected abstract List<Catalog> getCatalogs0() throws DataAccessException;
protected abstract List<Catalog> getCatalogs0();
@Override
public final List<Schema> getSchemas(String name) {
@ -127,7 +126,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
@Override
public final List<Schema> getSchemas() throws DataAccessException {
public final List<Schema> getSchemas() {
initSchemas();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedSchemas.values()));
}
@ -145,7 +144,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
protected List<Schema> getSchemas0() throws DataAccessException {
protected List<Schema> getSchemas0() {
List<Schema> 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<Table<?>> getTables() throws DataAccessException {
public final List<Table<?>> getTables() {
initTables();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedTables.values()));
}
@ -187,7 +186,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
protected List<Table<?>> getTables0() throws DataAccessException {
protected List<Table<?>> getTables0() {
List<Table<?>> 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<Sequence<?>> getSequences() throws DataAccessException {
public final List<Sequence<?>> getSequences() {
initSequences();
return Collections.unmodifiableList(new ArrayList<>(cachedQualifiedSequences.values()));
}
@ -229,7 +228,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
}
}
protected List<Sequence<?>> getSequences0() throws DataAccessException {
protected List<Sequence<?>> getSequences0() {
List<Sequence<?>> 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<UniqueKey<?>> getPrimaryKeys() throws DataAccessException {
public final List<UniqueKey<?>> getPrimaryKeys() {
initPrimaryKeys();
return Collections.unmodifiableList(cachedPrimaryKeys);
}
@ -247,7 +246,7 @@ abstract class AbstractMeta extends AbstractScope implements Meta, Serializable
cachedPrimaryKeys = new ArrayList<>(getPrimaryKeys0());
}
protected List<UniqueKey<?>> getPrimaryKeys0() throws DataAccessException {
protected List<UniqueKey<?>> getPrimaryKeys0() {
List<UniqueKey<?>> 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());
}
}

View File

@ -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<Query> appendCatalogs(final List<Query> queries, final Iterator<Catalog> i1, final Iterator<Catalog> i2) {
return append(queries, i1, i2,
null,
null,
new Merge<Catalog>() {
@Override
public void merge(List<Query> q, Catalog c1, Catalog c2) {
appendSchemas(q, sorted(c1.getSchemas()), sorted(c2.getSchemas()));
}
}
);
}
private final List<Query> appendSchemas(final List<Query> queries, final Iterator<Schema> i1, final Iterator<Schema> i2) {
// TODO Cascade semantics when creating and deleting
return append(queries, i1, i2,
new Create<Schema>() {
@Override
public void create(List<Query> q, Schema s) {
q.add(ctx.createSchema(s));
}
},
new Drop<Schema>() {
@Override
public void drop(List<Query> q, Schema s) {
q.add(ctx.dropSchema(s));
}
},
new Merge<Schema>() {
@Override
public void merge(List<Query> q, Schema s1, Schema s2) {
appendTables(q, sorted(s1.getTables()), sorted(s2.getTables()));
}
}
);
}
private final List<Query> appendTables(final List<Query> queries, final Iterator<Table<?>> i1, final Iterator<Table<?>> i2) {
// TODO Cascade semantics when creating and deleting
return append(queries, i1, i2,
new Create<Table<?>>() {
@Override
public void create(List<Query> q, Table<?> t) {
q.addAll(Arrays.asList(ctx.ddl(t).queries()));
}
},
new Drop<Table<?>>() {
@Override
public void drop(List<Query> q, Table<?> t) {
q.add(ctx.dropTable(t));
}
},
new Merge<Table<?>>() {
@Override
public void merge(List<Query> q, Table<?> t1, Table<?> t2) {
appendColumns(queries, t1, t2, sorted(t1.fields()), sorted(t2.fields()));
}
}
);
}
private final List<Query> appendColumns(final List<Query> queries, final Table<?> t1, final Table<?> t2, final Iterator<Field<?>> i1, final Iterator<Field<?>> 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 <N extends Named> List<Query> append(
List<Query> queries,
Iterator<N> i1,
Iterator<N> i2,
Create<N> create,
Drop<N> drop,
Merge<N> 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<N extends Named> {
void create(List<Query> queries, N named);
}
private static interface Drop<N extends Named> {
void drop(List<Query> queries, N named);
}
private static interface Merge<N extends Named> {
void merge(List<Query> queries, N named1, N named2);
}
private static final <N extends Named> Iterator<N> sorted(N... array) {
List<N> result = Arrays.asList(array);
Collections.sort(result, COMP);
return result.iterator();
}
private static final <N extends Named> Iterator<N> sorted(List<N> list) {
List<N> result = new ArrayList<>(list);
Collections.sort(result, COMP);
return result.iterator();
}
private static final class NamedComparator implements Comparator<Named> {
@Override
public int compare(Named o1, Named o2) {
return o1.getQualifiedName().compareTo(o2.getQualifiedName());
}
}
}