package org.jooq.impl;
import static org.jooq.DDLFlag.CHECK;
import static org.jooq.DDLFlag.COMMENT;
import static org.jooq.DDLFlag.DOMAIN;
import static org.jooq.DDLFlag.FOREIGN_KEY;
import static org.jooq.DDLFlag.INDEX;
import static org.jooq.DDLFlag.PRIMARY_KEY;
import static org.jooq.DDLFlag.SCHEMA;
import static org.jooq.DDLFlag.SEQUENCE;
import static org.jooq.DDLFlag.TABLE;
import static org.jooq.DDLFlag.UNIQUE;
import static org.jooq.TableOptions.TableType.VIEW;
import static org.jooq.impl.Comparators.KEY_COMP;
import static org.jooq.impl.Comparators.NAMED_COMP;
import static org.jooq.impl.DSL.constraint;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import org.jooq.Check;
import org.jooq.Comment;
import org.jooq.Constraint;
import org.jooq.ConstraintEnforcementStep;
import org.jooq.CreateDomainAsStep;
import org.jooq.CreateDomainConstraintStep;
import org.jooq.CreateDomainDefaultStep;
import org.jooq.CreateSequenceFlagsStep;
import org.jooq.CreateTableOnCommitStep;
import org.jooq.CreateViewAsStep;
import org.jooq.DDLExportConfiguration;
import org.jooq.DDLFlag;
import org.jooq.DSLContext;
import org.jooq.Domain;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Index;
import org.jooq.Key;
import org.jooq.Meta;
import org.jooq.Named;
import org.jooq.Queries;
import org.jooq.Query;
import org.jooq.Schema;
import org.jooq.Sequence;
import org.jooq.Table;
import org.jooq.TableOptions;
import org.jooq.TableOptions.OnCommit;
import org.jooq.TableOptions.TableType;
import org.jooq.UniqueKey;
import org.jooq.tools.StringUtils;
final class DDL {
private final DSLContext ctx;
private final DDLExportConfiguration configuration;
DDL(DSLContext ctx, DDLExportConfiguration configuration) {
this.ctx = ctx;
this.configuration = configuration;
}
private final List<Query> createTableOrView(Table<?> table, Collection<? extends Constraint> constraints) {
boolean temporary = table.getType() == TableType.TEMPORARY;
boolean view = table.getType().isView();
OnCommit onCommit = table.getOptions().onCommit();
if (view) {
List<Query> result = new ArrayList<>();
result.add(
applyAs((configuration.createViewIfNotExists()
? ctx.createViewIfNotExists(table, table.fields())
: configuration.createOrReplaceView()
? ctx.createOrReplaceView(table, table.fields())
: ctx.createView(table, table.fields())), table.getOptions())
);
if (!constraints.isEmpty() && configuration.includeConstraintsOnViews())
result.addAll(alterTableAddConstraints(table));
return result;
}
CreateTableOnCommitStep s0 =
(configuration.createTableIfNotExists()
? temporary
? ctx.createTemporaryTableIfNotExists(table)
: ctx.createTableIfNotExists(table)
: temporary
? ctx.createTemporaryTable(table)
: ctx.createTable(table))
.columns(sortIf(Arrays.asList(table.fields()), !configuration.respectColumnOrder()))
.constraints(constraints);
if (temporary && onCommit != null) {
switch (table.getOptions().onCommit()) {
case DELETE_ROWS:
return Arrays.<Query>asList(s0.onCommitDeleteRows());
case PRESERVE_ROWS:
return Arrays.<Query>asList(s0.onCommitPreserveRows());
case DROP:
return Arrays.<Query>asList(s0.onCommitDrop());
default:
throw new IllegalStateException("Unsupported flag: " + onCommit);
}
}
return Arrays.<Query>asList(s0);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private final Query applyAs(CreateViewAsStep q, TableOptions options) {
if (options.select() != null)
return q.as(options.select());
else if (StringUtils.isBlank(options.source()))
return q.as("");
Query[] queries = ctx.parser().parse(options.source()).queries();
if (queries.length > 0 && queries[0] instanceof CreateViewImpl)
return q.as(((CreateViewImpl<?>) queries[0]).$select());
else
return q.as("");
}
final Query createSequence(Sequence<?> sequence) {
CreateSequenceFlagsStep result = configuration.createSequenceIfNotExists()
? ctx.createSequenceIfNotExists(sequence)
: ctx.createSequence(sequence);
if (sequence.getStartWith() != null)
result = result.startWith(sequence.getStartWith());
else if (configuration.defaultSequenceFlags())
result = result.startWith(1);
if (sequence.getIncrementBy() != null)
result = result.incrementBy(sequence.getIncrementBy());
else if (configuration.defaultSequenceFlags())
result = result.incrementBy(1);
if (sequence.getMinvalue() != null)
result = result.minvalue(sequence.getMinvalue());
else if (configuration.defaultSequenceFlags())
result = result.noMinvalue();
if (sequence.getMaxvalue() != null)
result = result.maxvalue(sequence.getMaxvalue());
else if (configuration.defaultSequenceFlags())
result = result.noMaxvalue();
if (sequence.getCycle())
result = result.cycle();
else if (configuration.defaultSequenceFlags())
result = result.noCycle();
if (sequence.getCache() != null)
result = result.cache(sequence.getCache());
else if (configuration.defaultSequenceFlags())
result = result.noCache();
return result;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
final Query createDomain(Domain<?> domain) {
CreateDomainAsStep s1 = configuration.createDomainIfNotExists()
? ctx.createDomainIfNotExists(domain)
: ctx.createDomain(domain);
CreateDomainDefaultStep s2 = s1.as(domain.getDataType());
CreateDomainConstraintStep s3 = domain.getDataType().defaulted()
? s2.default_(domain.getDataType().default_())
: s2;
if (domain.getChecks().isEmpty())
return s3;
List<Constraint> constraints = new ArrayList<>();
for (Check check : domain.getChecks())
constraints.add(check.constraint());
return s3.constraints(constraints);
}
final List<Query> createTableOrView(Table<?> table) {
return createTableOrView(table, constraints(table));
}
private final List<Query> createIndex(Table<?> table) {
List<Query> result = new ArrayList<>();
if (configuration.flags().contains(DDLFlag.INDEX))
for (Index i : sortIf(table.getIndexes(), !configuration.respectIndexOrder()))
result.add(
(configuration.createIndexIfNotExists()
? i.getUnique()
? ctx.createUniqueIndexIfNotExists(i)
: ctx.createIndexIfNotExists(i)
: i.getUnique()
? ctx.createUniqueIndex(i)
: ctx.createIndex(i))
.on(i.getTable(), i.getFields())
);
return result;
}
private final List<Query> alterTableAddConstraints(Table<?> table) {
return alterTableAddConstraints(table, constraints(table));
}
private final List<Query> alterTableAddConstraints(Table<?> table, List<Constraint> constraints) {
List<Query> result = new ArrayList<>(constraints.size());
for (Constraint constraint : constraints)
result.add(ctx.alterTable(table).add(constraint));
return result;
}
private final List<Constraint> constraints(Table<?> table) {
List<Constraint> result = new ArrayList<>();
result.addAll(primaryKeys(table));
result.addAll(uniqueKeys(table));
result.addAll(foreignKeys(table));
result.addAll(checks(table));
return result;
}
private final List<Constraint> primaryKeys(Table<?> table) {
List<Constraint> result = new ArrayList<>();
if (configuration.flags().contains(PRIMARY_KEY) && (table.getType() != VIEW || configuration.includeConstraintsOnViews()))
for (UniqueKey<?> key : table.getKeys())
if (key.isPrimary())
result.add(enforced(constraint(key.getUnqualifiedName()).primaryKey(key.getFieldsArray()), key.enforced()));
return result;
}
private final List<Constraint> uniqueKeys(Table<?> table) {
List<Constraint> result = new ArrayList<>();
if (configuration.flags().contains(UNIQUE) && (table.getType() != VIEW || configuration.includeConstraintsOnViews()))
for (UniqueKey<?> key : sortKeysIf(table.getKeys(), !configuration.respectConstraintOrder()))
if (!key.isPrimary())
result.add(enforced(constraint(key.getUnqualifiedName()).unique(key.getFieldsArray()), key.enforced()));
return result;
}
private final List<Constraint> foreignKeys(Table<?> table) {
List<Constraint> result = new ArrayList<>();
if (configuration.flags().contains(FOREIGN_KEY) && (table.getType() != VIEW || configuration.includeConstraintsOnViews()))
for (ForeignKey<?, ?> key : sortKeysIf(table.getReferences(), !configuration.respectConstraintOrder()))
result.add(enforced(constraint(key.getUnqualifiedName()).foreignKey(key.getFieldsArray()).references(key.getKey().getTable(), key.getKeyFieldsArray()), key.enforced()));
return result;
}
private final List<Constraint> checks(Table<?> table) {
List<Constraint> result = new ArrayList<>();
if (configuration.flags().contains(CHECK) && (table.getType() != VIEW || configuration.includeConstraintsOnViews()))
for (Check<?> check : sortIf(table.getChecks(), !configuration.respectConstraintOrder()))
result.add(enforced(constraint(check.getUnqualifiedName()).check(check.condition()), check.enforced()));
return result;
}
final Queries queries(Table<?>... tables) {
List<Query> queries = new ArrayList<>();
for (Table<?> table : tables) {
if (configuration.flags().contains(TABLE))
queries.addAll(createTableOrView(table));
else
queries.addAll(alterTableAddConstraints(table));
queries.addAll(createIndex(table));
queries.addAll(commentOn(table));
}
return ctx.queries(queries);
}
private final List<Query> (Table<?> table) {
List<Query> result = new ArrayList<>();
if (configuration.flags().contains(COMMENT)) {
Comment tComment = table.getCommentPart();
if (!StringUtils.isEmpty(tComment.getComment()))
if (table.getType().isView())
result.add(ctx.commentOnView(table).is(tComment));
else
result.add(ctx.commentOnTable(table).is(tComment));
for (Field<?> field : sortIf(Arrays.asList(table.fields()), !configuration.respectColumnOrder())) {
Comment fComment = field.getCommentPart();
if (!StringUtils.isEmpty(fComment.getComment()))
result.add(ctx.commentOnColumn(field).is(fComment));
}
}
return result;
}
final Queries queries(Meta meta) {
List<Query> queries = new ArrayList<>();
List<Schema> schemas = sortIf(meta.getSchemas(), !configuration.respectSchemaOrder());
for (Schema schema : schemas)
if (configuration.flags().contains(SCHEMA) && !schema.getUnqualifiedName().empty())
if (configuration.createSchemaIfNotExists())
queries.add(ctx.createSchemaIfNotExists(schema.getUnqualifiedName()));
else
queries.add(ctx.createSchema(schema.getUnqualifiedName()));
if (configuration.flags().contains(TABLE)) {
for (Schema schema : schemas) {
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder())) {
List<Constraint> constraints = new ArrayList<>();
constraints.addAll(primaryKeys(table));
constraints.addAll(uniqueKeys(table));
constraints.addAll(checks(table));
queries.addAll(createTableOrView(table, constraints));
}
}
}
else {
for (Schema schema : schemas) {
if (configuration.flags().contains(PRIMARY_KEY))
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
for (Constraint constraint : sortIf(primaryKeys(table), !configuration.respectConstraintOrder()))
queries.add(ctx.alterTable(table).add(constraint));
if (configuration.flags().contains(UNIQUE))
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
for (Constraint constraint : sortIf(uniqueKeys(table), !configuration.respectConstraintOrder()))
queries.add(ctx.alterTable(table).add(constraint));
if (configuration.flags().contains(CHECK))
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
for (Constraint constraint : sortIf(checks(table), !configuration.respectConstraintOrder()))
queries.add(ctx.alterTable(table).add(constraint));
}
}
if (configuration.flags().contains(FOREIGN_KEY))
for (Schema schema : schemas)
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
for (Constraint constraint : foreignKeys(table))
queries.add(ctx.alterTable(table).add(constraint));
if (configuration.flags().contains(DOMAIN))
for (Schema schema : schemas)
for (Domain<?> domain : sortIf(schema.getDomains(), !configuration.respectDomainOrder()))
queries.add(createDomain(domain));
if (configuration.flags().contains(SEQUENCE))
for (Schema schema : schemas)
for (Sequence<?> sequence : sortIf(schema.getSequences(), !configuration.respectSequenceOrder()))
queries.add(createSequence(sequence));
if (configuration.flags().contains(COMMENT))
for (Schema schema : schemas)
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
queries.addAll(commentOn(table));
if (configuration.flags().contains(INDEX))
for (Schema schema : schemas)
for (Table<?> table : sortIf(schema.getTables(), !configuration.respectTableOrder()))
queries.addAll(createIndex(table));
return ctx.queries(queries);
}
private final <K extends Key<?>> List<K> sortKeysIf(List<K> input, boolean sort) {
if (sort) {
List<K> result = new ArrayList<>(input);
Collections.sort(result, KEY_COMP);
Collections.sort(result, NAMED_COMP);
return result;
}
return input;
}
private final <N extends Named> List<N> sortIf(List<N> input, boolean sort) {
if (sort) {
List<N> result = new ArrayList<>(input);
Collections.sort(result, NAMED_COMP);
return result;
}
return input;
}
private final Constraint enforced(ConstraintEnforcementStep check, boolean enforced) {
return check;
}
}