[jOOQ/jOOQ#9506] Added a History API draft
This commit is contained in:
parent
61fd552bb8
commit
80ac10e1c8
@ -43,6 +43,9 @@ import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
|
||||
/**
|
||||
* A source of migration content.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@ -51,25 +54,41 @@ public interface File {
|
||||
|
||||
/**
|
||||
* The path of the file within the repository.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
@NotNull
|
||||
String path();
|
||||
|
||||
/**
|
||||
* The name of the file.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
@NotNull
|
||||
String name();
|
||||
|
||||
/**
|
||||
* The file content.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
@Nullable
|
||||
String content();
|
||||
|
||||
/**
|
||||
* The file type.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
@NotNull
|
||||
ContentType type();
|
||||
}
|
||||
|
||||
@ -38,24 +38,37 @@
|
||||
package org.jooq;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
|
||||
/**
|
||||
* A set of files that are in a specific order.
|
||||
* <p>
|
||||
* This type will help make sure that duplicate occurrences of the same file
|
||||
* (e.g. edits) are streamlined.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
public interface Files extends Iterable<File> {
|
||||
|
||||
/**
|
||||
* The from version from which this migration is done.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@NotNull
|
||||
@Experimental
|
||||
Version from();
|
||||
|
||||
/**
|
||||
* The to version to which this migration is done.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@NotNull
|
||||
@Experimental
|
||||
Version to();
|
||||
}
|
||||
|
||||
86
jOOQ/src/main/java/org/jooq/History.java
Normal file
86
jOOQ/src/main/java/org/jooq/History.java
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq;
|
||||
|
||||
import org.jooq.exception.DataMigrationVerificationException;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The History of {@link Version} elements as installed by previous
|
||||
* {@link Migrations}.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@Experimental
|
||||
public interface History extends Iterable<Version> {
|
||||
|
||||
/**
|
||||
* The root {@link Version}.
|
||||
* <p>
|
||||
* This corresponds to the {@link Configuration#commitProvider()}'s
|
||||
* {@link Commits#root()} if migrations have been initialised on the
|
||||
* configured database.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*
|
||||
* @throws DataMigrationVerificationException If no root version is
|
||||
* available (e.g. because no migration has happened yet).
|
||||
*/
|
||||
@NotNull
|
||||
@Experimental
|
||||
Version root() throws DataMigrationVerificationException;
|
||||
|
||||
/**
|
||||
* The currently installed {@link Version}.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*
|
||||
* @throws DataMigrationVerificationException If no root version is
|
||||
* available (e.g. because no migration has happened yet).
|
||||
*/
|
||||
@NotNull
|
||||
@Experimental
|
||||
Version current();
|
||||
}
|
||||
@ -80,6 +80,17 @@ public interface Migrations {
|
||||
@NotNull
|
||||
Commits commits();
|
||||
|
||||
/**
|
||||
* Retrieve the {@link History} of previously installed {@link Version}
|
||||
* objects from the database.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
@NotNull
|
||||
History history();
|
||||
|
||||
/**
|
||||
* Create a migration from the currently installed version to a new version.
|
||||
* <p>
|
||||
|
||||
@ -423,6 +423,10 @@ final class CommitImpl extends AbstractNode<Commit> implements Commit {
|
||||
files.clear();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id().hashCode();
|
||||
|
||||
@ -175,6 +175,7 @@ import org.jooq.MergeKeyStep9;
|
||||
import org.jooq.MergeKeyStepN;
|
||||
import org.jooq.MergeUsingStep;
|
||||
import org.jooq.Meta;
|
||||
import org.jooq.Migrations;
|
||||
import org.jooq.Name;
|
||||
import org.jooq.Param;
|
||||
import org.jooq.Parser;
|
||||
@ -393,8 +394,8 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.jooq.Migrations migrations() {
|
||||
return new MigrationsImpl(this);
|
||||
public Migrations migrations() {
|
||||
return new MigrationsImpl(configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
279
jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java
Normal file
279
jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java
Normal file
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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
|
||||
* ASL 2.0 and offer limited warranties, support, maintenance, and commercial
|
||||
* database integrations.
|
||||
*
|
||||
* For more information, please visit: https://www.jooq.org/legal/licensing
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.schema;
|
||||
import static org.jooq.impl.History.HISTORY;
|
||||
import static org.jooq.impl.MigrationImpl.Status.SUCCESS;
|
||||
import static org.jooq.impl.Tools.isEmpty;
|
||||
import static org.jooq.tools.StringUtils.isBlank;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.Commit;
|
||||
import org.jooq.Commits;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.History;
|
||||
import org.jooq.Schema;
|
||||
import org.jooq.Version;
|
||||
import org.jooq.conf.InterpreterSearchSchema;
|
||||
import org.jooq.conf.MappedCatalog;
|
||||
import org.jooq.conf.MappedSchema;
|
||||
import org.jooq.conf.MigrationSchema;
|
||||
import org.jooq.conf.RenderMapping;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.exception.DataMigrationVerificationException;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class HistoryImpl extends AbstractScope implements History {
|
||||
|
||||
final DSLContext ctx;
|
||||
final DSLContext historyCtx;
|
||||
final Commits commits;
|
||||
final List<Version> versions;
|
||||
|
||||
HistoryImpl(Configuration configuration) {
|
||||
super(configuration);
|
||||
|
||||
this.ctx = configuration.dsl();
|
||||
this.historyCtx = initCtx(configuration, configuration.settings().getMigrationHistorySchema()).dsl();
|
||||
this.commits = configuration.commitProvider().provide();
|
||||
this.versions = initVersions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Iterator<Version> iterator() {
|
||||
return unmodifiableList(versions).iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Version root() {
|
||||
if (!isEmpty(versions))
|
||||
return versions.get(0);
|
||||
else
|
||||
throw new DataMigrationVerificationException("No versions are available");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Version current() {
|
||||
if (!isEmpty(versions))
|
||||
return versions.get(versions.size() - 1);
|
||||
else
|
||||
throw new DataMigrationVerificationException("No versions are available");
|
||||
}
|
||||
|
||||
final Set<Schema> schemas() {
|
||||
Set<Schema> set = new LinkedHashSet<>();
|
||||
|
||||
for (MigrationSchema schema : configuration.settings().getMigrationSchemata())
|
||||
addSchema(set, schema);
|
||||
|
||||
if (configuration.settings().getMigrationDefaultSchema() != null) {
|
||||
addSchema(set, configuration.settings().getMigrationDefaultSchema());
|
||||
set.add(DSL.schema(""));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private final void addSchema(Set<Schema> set, MigrationSchema schema) {
|
||||
if (schema != null)
|
||||
set.addAll(lookup(asList(schema(name(schema.getCatalog(), schema.getSchema())))));
|
||||
}
|
||||
|
||||
final Collection<Schema> lookup(List<Schema> schemas) {
|
||||
|
||||
// TODO: Refactor usages of getInterpreterSearchPath()
|
||||
Collection<Schema> result = schemas;
|
||||
List<InterpreterSearchSchema> searchPath = configuration().settings().getInterpreterSearchPath();
|
||||
|
||||
if (!searchPath.isEmpty()) {
|
||||
result = new HashSet<>();
|
||||
Schema defaultSchema = schema(name(searchPath.get(0).getCatalog(), searchPath.get(0).getSchema()));
|
||||
|
||||
for (Schema schema : schemas)
|
||||
if (schema.getQualifiedName().empty())
|
||||
result.add(defaultSchema);
|
||||
else
|
||||
result.add(schema);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static final Configuration initCtx(Configuration configuration, MigrationSchema defaultSchema) {
|
||||
if (defaultSchema != null) {
|
||||
Configuration result = configuration.derive();
|
||||
|
||||
if (!isBlank(defaultSchema.getCatalog())) {
|
||||
result.settings().withRenderMapping(new RenderMapping()
|
||||
.withCatalogs(new MappedCatalog()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getCatalog())
|
||||
.withSchemata(new MappedSchema()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getSchema())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (!isBlank(defaultSchema.getSchema())) {
|
||||
result.settings().withRenderMapping(new RenderMapping()
|
||||
.withSchemata(new MappedSchema()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getSchema())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return configuration;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final HistoryRecord currentHistoryRecord() {
|
||||
return existsHistory()
|
||||
? historyCtx.selectFrom(HISTORY)
|
||||
|
||||
// TODO: How to recover from failure?
|
||||
.where(HISTORY.STATUS.eq(inline(SUCCESS)))
|
||||
.orderBy(HISTORY.MIGRATED_AT.desc(), HISTORY.ID.desc())
|
||||
.limit(1)
|
||||
.fetchOne()
|
||||
: null;
|
||||
}
|
||||
|
||||
final boolean existsHistory() {
|
||||
|
||||
// [#8301] Find a better way to test if our table already exists
|
||||
try {
|
||||
Configuration c = historyCtx
|
||||
.configuration()
|
||||
.derive();
|
||||
c.data("org.jooq.tools.LoggerListener.exception.mute", true);
|
||||
c.dsl().fetchExists(HISTORY);
|
||||
return true;
|
||||
}
|
||||
catch (DataAccessException ignore) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private final List<Version> initVersions() {
|
||||
List<Version> result = new ArrayList<>();
|
||||
|
||||
if (existsHistory()) {
|
||||
result.add(commits.root().version());
|
||||
|
||||
for (HistoryRecord r : historyCtx
|
||||
.selectFrom(HISTORY)
|
||||
.where(HISTORY.STATUS.eq(inline(SUCCESS)))
|
||||
.orderBy(HISTORY.ID.asc())
|
||||
) {
|
||||
Commit commit = commits.get(r.getMigratedTo());
|
||||
|
||||
if (commit != null)
|
||||
result.add(commit.version());
|
||||
else
|
||||
throw new DataMigrationVerificationException("CommitProvider didn't provide version for ID: " + r.getMigratedTo());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
final void init() {
|
||||
|
||||
// TODO: What to do when initialising jOOQ-migrations on an existing database?
|
||||
// - Should there be init() commands that can be run explicitly by the user?
|
||||
// - Will we reverse engineer the production Meta snapshot first?
|
||||
if (!existsHistory()) {
|
||||
|
||||
// TODO: [#9506] Make this schema creation vendor agnostic
|
||||
// TODO: [#15225] This CREATE SCHEMA statement should never be necessary.
|
||||
if (TRUE.equals(historyCtx.settings().isMigrationHistorySchemaCreateSchemaIfNotExists())
|
||||
&& historyCtx.settings().getMigrationHistorySchema() != null
|
||||
|| TRUE.equals(historyCtx.settings().isMigrationSchemataCreateSchemaIfNotExists())
|
||||
&& historyCtx.settings().getMigrationDefaultSchema() != null)
|
||||
historyCtx.createSchemaIfNotExists("").execute();
|
||||
|
||||
historyCtx.meta(HISTORY).ddl().executeBatch();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return versions.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj instanceof HistoryImpl h) {
|
||||
return versions.equals(h.versions);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "History [" + current() + "]";
|
||||
}
|
||||
}
|
||||
@ -39,26 +39,19 @@ package org.jooq.impl;
|
||||
|
||||
import static java.lang.Boolean.FALSE;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.jooq.impl.DSL.createSchemaIfNotExists;
|
||||
import static org.jooq.impl.DSL.dropSchemaIfExists;
|
||||
import static org.jooq.impl.DSL.dropTableIfExists;
|
||||
import static org.jooq.impl.DSL.inline;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.schema;
|
||||
import static org.jooq.impl.History.HISTORY;
|
||||
import static org.jooq.impl.HistoryImpl.initCtx;
|
||||
import static org.jooq.impl.MigrationImpl.Status.FAILURE;
|
||||
import static org.jooq.impl.MigrationImpl.Status.MIGRATING;
|
||||
import static org.jooq.impl.MigrationImpl.Status.REVERTING;
|
||||
import static org.jooq.impl.MigrationImpl.Status.STARTING;
|
||||
import static org.jooq.impl.MigrationImpl.Status.SUCCESS;
|
||||
import static org.jooq.tools.StringUtils.isBlank;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jooq.Commit;
|
||||
@ -66,7 +59,6 @@ import org.jooq.Commits;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.Constants;
|
||||
import org.jooq.ContextTransactionalRunnable;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Files;
|
||||
import org.jooq.Meta;
|
||||
import org.jooq.Migration;
|
||||
@ -75,11 +67,6 @@ import org.jooq.MigrationListener;
|
||||
import org.jooq.Queries;
|
||||
import org.jooq.Query;
|
||||
import org.jooq.Schema;
|
||||
import org.jooq.conf.InterpreterSearchSchema;
|
||||
import org.jooq.conf.MappedCatalog;
|
||||
import org.jooq.conf.MappedSchema;
|
||||
import org.jooq.conf.MigrationSchema;
|
||||
import org.jooq.conf.RenderMapping;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.exception.DataMigrationException;
|
||||
import org.jooq.exception.DataMigrationVerificationException;
|
||||
@ -92,7 +79,7 @@ import org.jooq.tools.StopWatch;
|
||||
final class MigrationImpl extends AbstractScope implements Migration {
|
||||
|
||||
static final JooqLogger log = JooqLogger.getLogger(Migration.class);
|
||||
final DSLContext historyCtx;
|
||||
final HistoryImpl history;
|
||||
final Commit to;
|
||||
Commit from;
|
||||
Queries queries;
|
||||
@ -105,38 +92,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
));
|
||||
|
||||
this.to = to;
|
||||
this.historyCtx = initCtx(configuration(), configuration.settings().getMigrationHistorySchema()).dsl();
|
||||
}
|
||||
|
||||
private static final Configuration initCtx(Configuration configuration, MigrationSchema defaultSchema) {
|
||||
if (defaultSchema != null) {
|
||||
Configuration result = configuration.derive();
|
||||
|
||||
if (!isBlank(defaultSchema.getCatalog())) {
|
||||
result.settings().withRenderMapping(new RenderMapping()
|
||||
.withCatalogs(new MappedCatalog()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getCatalog())
|
||||
.withSchemata(new MappedSchema()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getSchema())
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (!isBlank(defaultSchema.getSchema())) {
|
||||
result.settings().withRenderMapping(new RenderMapping()
|
||||
.withSchemata(new MappedSchema()
|
||||
.withInput("")
|
||||
.withOutput(defaultSchema.getSchema())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
else
|
||||
return configuration;
|
||||
this.history = new HistoryImpl(configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -177,7 +133,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
}
|
||||
|
||||
private final void verify0(DefaultMigrationContext ctx) {
|
||||
HistoryRecord currentRecord = currentHistoryRecord();
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord();
|
||||
|
||||
if (currentRecord != null) {
|
||||
Commit currentCommit = commits().get(currentRecord.getMigratedTo());
|
||||
@ -195,39 +151,19 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
if (commits().get(commit.id()) == null)
|
||||
throw new DataMigrationVerificationException("Commit is not available from CommitProvider: " + commit.id());
|
||||
|
||||
for (Schema schema : lookup(commit.meta().getSchemas()))
|
||||
for (Schema schema : history.lookup(commit.meta().getSchemas()))
|
||||
if (!ctx.migratedSchemas().contains(schema))
|
||||
throw new DataMigrationVerificationException("Schema is referenced from commit, but not configured for migration: " + schema);
|
||||
}
|
||||
|
||||
private final Collection<Schema> lookup(List<Schema> schemas) {
|
||||
|
||||
// TODO: Refactor usages of getInterpreterSearchPath()
|
||||
Collection<Schema> result = schemas;
|
||||
List<InterpreterSearchSchema> searchPath = dsl().settings().getInterpreterSearchPath();
|
||||
|
||||
if (!searchPath.isEmpty()) {
|
||||
result = new HashSet<>();
|
||||
Schema defaultSchema = schema(name(searchPath.get(0).getCatalog(), searchPath.get(0).getSchema()));
|
||||
|
||||
for (Schema schema : schemas)
|
||||
if (schema.getQualifiedName().empty())
|
||||
result.add(defaultSchema);
|
||||
else
|
||||
result.add(schema);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private final Queries revertUntrackedQueries(Set<Schema> includedSchemas) {
|
||||
Commit currentCommit = currentCommit();
|
||||
Meta currentMeta = currentCommit.meta();
|
||||
Meta existingMeta = dsl().meta().filterSchemas(includedSchemas::contains);
|
||||
|
||||
Set<Schema> expectedSchemas = new HashSet<>();
|
||||
expectedSchemas.addAll(lookup(from().meta().getSchemas()));
|
||||
expectedSchemas.addAll(lookup(to().meta().getSchemas()));
|
||||
expectedSchemas.addAll(history.lookup(from().meta().getSchemas()));
|
||||
expectedSchemas.addAll(history.lookup(to().meta().getSchemas()));
|
||||
expectedSchemas.retainAll(includedSchemas);
|
||||
|
||||
schemaLoop:
|
||||
@ -258,8 +194,8 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
execute(ctx, listener, ctx.revertUntrackedQueries);
|
||||
}
|
||||
|
||||
private final DefaultMigrationContext migrationContext() {
|
||||
Set<Schema> schemas = schemas();
|
||||
final DefaultMigrationContext migrationContext() {
|
||||
Set<Schema> schemas = history.schemas();
|
||||
|
||||
return new DefaultMigrationContext(
|
||||
configuration(),
|
||||
@ -271,25 +207,6 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
);
|
||||
}
|
||||
|
||||
private final Set<Schema> schemas() {
|
||||
Set<Schema> set = new LinkedHashSet<>();
|
||||
|
||||
for (MigrationSchema schema : configuration.settings().getMigrationSchemata())
|
||||
addSchema(set, schema);
|
||||
|
||||
if (configuration.settings().getMigrationDefaultSchema() != null) {
|
||||
addSchema(set, configuration.settings().getMigrationDefaultSchema());
|
||||
set.add(DSL.schema(""));
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private final void addSchema(Set<Schema> set, MigrationSchema schema) {
|
||||
if (schema != null)
|
||||
set.addAll(lookup(asList(schema(name(schema.getCatalog(), schema.getSchema())))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void execute() {
|
||||
|
||||
@ -355,7 +272,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
}
|
||||
|
||||
private final HistoryRecord createRecord(Status status) {
|
||||
HistoryRecord record = historyCtx.newRecord(HISTORY);
|
||||
HistoryRecord record = history.historyCtx.newRecord(HISTORY);
|
||||
|
||||
record
|
||||
.setJooqVersion(Constants.VERSION)
|
||||
@ -401,22 +318,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
* History.
|
||||
*/
|
||||
final void init() {
|
||||
|
||||
// TODO: What to do when initialising jOOQ-migrations on an existing database?
|
||||
// - Should there be init() commands that can be run explicitly by the user?
|
||||
// - Will we reverse engineer the production Meta snapshot first?
|
||||
if (!existsHistory()) {
|
||||
|
||||
// TODO: [#9506] Make this schema creation vendor agnostic
|
||||
// TODO: [#15225] This CREATE SCHEMA statement should never be necessary.
|
||||
if (TRUE.equals(historyCtx.settings().isMigrationHistorySchemaCreateSchemaIfNotExists())
|
||||
&& historyCtx.settings().getMigrationHistorySchema() != null
|
||||
|| TRUE.equals(historyCtx.settings().isMigrationSchemataCreateSchemaIfNotExists())
|
||||
&& historyCtx.settings().getMigrationDefaultSchema() != null)
|
||||
historyCtx.createSchemaIfNotExists("").execute();
|
||||
|
||||
historyCtx.meta(HISTORY).ddl().executeBatch();
|
||||
}
|
||||
history.init();
|
||||
|
||||
MigrationContext ctx = migrationContext();
|
||||
if (TRUE.equals(ctx.settings().isMigrationSchemataCreateSchemaIfNotExists()))
|
||||
@ -424,36 +326,8 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
dsl().createSchemaIfNotExists(schema).execute();
|
||||
}
|
||||
|
||||
private final boolean existsHistory() {
|
||||
|
||||
// [#8301] Find a better way to test if our table already exists
|
||||
try {
|
||||
Configuration c = historyCtx
|
||||
.configuration()
|
||||
.derive();
|
||||
c.data("org.jooq.tools.LoggerListener.exception.mute", true);
|
||||
c.dsl().fetchExists(HISTORY);
|
||||
return true;
|
||||
}
|
||||
catch (DataAccessException ignore) {}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private final HistoryRecord currentHistoryRecord() {
|
||||
return existsHistory()
|
||||
? historyCtx.selectFrom(HISTORY)
|
||||
|
||||
// TODO: How to recover from failure?
|
||||
.where(HISTORY.STATUS.eq(inline(SUCCESS)))
|
||||
.orderBy(HISTORY.MIGRATED_AT.desc(), HISTORY.ID.desc())
|
||||
.limit(1)
|
||||
.fetchOne()
|
||||
: null;
|
||||
}
|
||||
|
||||
final Commit currentCommit() {
|
||||
HistoryRecord currentRecord = currentHistoryRecord();
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord();
|
||||
|
||||
if (currentRecord == null) {
|
||||
Commit result = TRUE.equals(settings().isMigrationAutoBaseline()) ? to() : to().root();
|
||||
@ -486,6 +360,18 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
}
|
||||
}
|
||||
|
||||
enum Status {
|
||||
STARTING,
|
||||
REVERTING,
|
||||
MIGRATING,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
@ -496,12 +382,4 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
enum Status {
|
||||
STARTING,
|
||||
REVERTING,
|
||||
MIGRATING,
|
||||
SUCCESS,
|
||||
FAILURE
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,9 +41,10 @@ import static java.util.Collections.emptyList;
|
||||
|
||||
import org.jooq.Commit;
|
||||
import org.jooq.Commits;
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.ContentType;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.File;
|
||||
import org.jooq.History;
|
||||
import org.jooq.Migration;
|
||||
import org.jooq.Migrations;
|
||||
import org.jooq.Version;
|
||||
@ -51,12 +52,10 @@ import org.jooq.Version;
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class MigrationsImpl implements Migrations {
|
||||
final class MigrationsImpl extends AbstractScope implements Migrations {
|
||||
|
||||
final DSLContext ctx;
|
||||
|
||||
MigrationsImpl(DSLContext ctx) {
|
||||
this.ctx = ctx;
|
||||
MigrationsImpl(Configuration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -64,18 +63,23 @@ final class MigrationsImpl implements Migrations {
|
||||
return new FileImpl(path, content, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final History history() {
|
||||
return new HistoryImpl(configuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Version version(String id) {
|
||||
return new VersionImpl(ctx, id);
|
||||
return new VersionImpl(configuration(), id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Commits commits() {
|
||||
return new CommitsImpl(ctx.configuration(), new CommitImpl(ctx.configuration(), "init", "init", null, emptyList(), emptyList()));
|
||||
return new CommitsImpl(configuration(), new CommitImpl(configuration(), "init", "init", null, emptyList(), emptyList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Migration migrateTo(Commit to) {
|
||||
return new MigrationImpl(ctx.configuration(), to);
|
||||
return new MigrationImpl(configuration(), to);
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,6 @@ package org.jooq.impl;
|
||||
import static java.lang.Boolean.TRUE;
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.unmodifiableList;
|
||||
import static org.jooq.impl.DSL.createSchema;
|
||||
import static org.jooq.impl.DSL.name;
|
||||
import static org.jooq.impl.DSL.schema;
|
||||
@ -51,12 +50,9 @@ import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
import org.jooq.DSLContext;
|
||||
import org.jooq.Meta;
|
||||
import org.jooq.Queries;
|
||||
@ -75,10 +71,10 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
final Meta meta;
|
||||
final List<Parent> parents;
|
||||
|
||||
private VersionImpl(DSLContext ctx, String id, Meta meta, Version root, List<Parent> parents) {
|
||||
private VersionImpl(Configuration configuration, String id, Meta meta, Version root, List<Parent> parents) {
|
||||
super(id, null, root);
|
||||
|
||||
this.ctx = ctx;
|
||||
this.ctx = configuration.dsl();
|
||||
this.meta = meta != null ? meta : init(ctx);
|
||||
this.parents = parents;
|
||||
}
|
||||
@ -96,19 +92,19 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
return result;
|
||||
}
|
||||
|
||||
VersionImpl(DSLContext ctx, String id, Meta meta, Version root, Version parent, Queries queries) {
|
||||
this(ctx, id, meta, root, Arrays.asList(new Parent((VersionImpl) parent, queries)));
|
||||
VersionImpl(Configuration configuration, String id, Meta meta, Version root, Version parent, Queries queries) {
|
||||
this(configuration, id, meta, root, Arrays.asList(new Parent((VersionImpl) parent, queries)));
|
||||
}
|
||||
|
||||
VersionImpl(DSLContext ctx, String id, Meta meta, Version root, Version[] parents) {
|
||||
this(ctx, id, meta, root, wrap(parents));
|
||||
VersionImpl(Configuration configuration, String id, Meta meta, Version root, Version[] parents) {
|
||||
this(configuration, id, meta, root, wrap(parents));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the root {@link Version}.
|
||||
*/
|
||||
VersionImpl(DSLContext ctx, String id) {
|
||||
this(ctx, id, null, null, asList());
|
||||
VersionImpl(Configuration configuration, String id) {
|
||||
this(configuration, id, null, null, asList());
|
||||
}
|
||||
|
||||
private static List<Parent> wrap(Version[] parents) {
|
||||
@ -152,7 +148,7 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
|
||||
@Override
|
||||
public final Version apply(String newId, Queries migration) {
|
||||
return new VersionImpl(ctx, newId, meta().apply(migration), root, this, migration);
|
||||
return new VersionImpl(ctx.configuration(), newId, meta().apply(migration), root, this, migration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -181,7 +177,7 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
if (list == null)
|
||||
list = new ArrayList<>();
|
||||
|
||||
list.add(new Parent(new VersionImpl(ctx, parent.version.id(), parent.version.meta, root, emptyList()), parent.queries));
|
||||
list.add(new Parent(new VersionImpl(ctx.configuration(), parent.version.id(), parent.version.meta, root, emptyList()), parent.queries));
|
||||
}
|
||||
else {
|
||||
VersionImpl p = parent.version.subgraphTo(ancestor);
|
||||
@ -195,7 +191,7 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
}
|
||||
}
|
||||
|
||||
return list == null ? null : new VersionImpl(ctx, id(), meta, root, list);
|
||||
return list == null ? null : new VersionImpl(ctx.configuration(), id(), meta, root, list);
|
||||
}
|
||||
|
||||
private final Queries migrateTo(VersionImpl target, Queries result) {
|
||||
@ -230,13 +226,13 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
|
||||
|
||||
@Override
|
||||
public final Version commit(String newId, Meta newMeta) {
|
||||
return new VersionImpl(ctx, newId, newMeta, root, new Version[] { this });
|
||||
return new VersionImpl(ctx.configuration(), newId, newMeta, root, new Version[] { this });
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Version merge(String newId, Version with) {
|
||||
Meta m = commonAncestor(with).meta();
|
||||
return new VersionImpl(ctx, newId, m.apply(m.migrateTo(meta()).concat(m.migrateTo(with.meta()))), root, new Version[] { this, with });
|
||||
return new VersionImpl(ctx.configuration(), newId, m.apply(m.migrateTo(meta()).concat(m.migrateTo(with.meta()))), root, new Version[] { this, with });
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Loading…
Reference in New Issue
Block a user