[jOOQ/jOOQ#9506] Add a way to resolve() a FAILURE
This commit is contained in:
parent
80ac10e1c8
commit
9e04de7cf8
@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.codegen.maven;
|
||||
|
||||
import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_SOURCES;
|
||||
import static org.apache.maven.plugins.annotations.ResolutionScope.TEST;
|
||||
|
||||
import org.jooq.Configuration;
|
||||
|
||||
import org.apache.maven.plugins.annotations.Mojo;
|
||||
|
||||
/**
|
||||
* The jOOQ Migrations resolve mojo
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
@Mojo(
|
||||
name = "resolve",
|
||||
defaultPhase = GENERATE_SOURCES,
|
||||
requiresDependencyResolution = TEST,
|
||||
threadSafe = true
|
||||
)
|
||||
public class ResolveMojo extends AbstractMigrationsMojo {
|
||||
|
||||
@Override
|
||||
final void execute0(Configuration configuration) throws Exception {
|
||||
configuration.dsl().migrations().history().resolve("Resolved");
|
||||
}
|
||||
}
|
||||
@ -83,4 +83,14 @@ public interface History extends Iterable<Version> {
|
||||
@NotNull
|
||||
@Experimental
|
||||
Version current();
|
||||
|
||||
/**
|
||||
* Resolve any previous failures in the {@link History}.
|
||||
* <p>
|
||||
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
|
||||
* versions.
|
||||
*/
|
||||
@Experimental
|
||||
void resolve(String message);
|
||||
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import org.jooq.Table;
|
||||
import org.jooq.TableField;
|
||||
import org.jooq.TableOptions;
|
||||
import org.jooq.UniqueKey;
|
||||
import org.jooq.impl.MigrationImpl.Resolution;
|
||||
import org.jooq.impl.MigrationImpl.Status;
|
||||
|
||||
|
||||
@ -94,6 +95,24 @@ class History extends TableImpl<HistoryRecord> {
|
||||
*/
|
||||
final TableField<HistoryRecord, Status> STATUS = createField(DSL.name("STATUS"), SQLDataType.VARCHAR(10).nullable(false), this, "The database version installation status.", new EnumConverter<String, Status>(String.class, Status.class));
|
||||
|
||||
/**
|
||||
* The column <code>JOOQ_MIGRATION_HISTORY.STATUS_MESSAGE</code>. Any info
|
||||
* or error message explaining the status.
|
||||
*/
|
||||
final TableField<HistoryRecord, String> STATUS_MESSAGE = createField(DSL.name("STATUS_MESSAGE"), SQLDataType.CLOB, this, "Any info or error message explaining the status.");
|
||||
|
||||
/**
|
||||
* The column <code>JOOQ_MIGRATION_HISTORY.RESOLUTION</code>. The error
|
||||
* resolution, if any.
|
||||
*/
|
||||
final TableField<HistoryRecord, Resolution> RESOLUTION = createField(DSL.name("RESOLUTION"), SQLDataType.VARCHAR(10), this, "The error resolution, if any.", new EnumConverter<String, Resolution>(String.class, Resolution.class));
|
||||
|
||||
/**
|
||||
* The column <code>JOOQ_MIGRATION_HISTORY.RESOLUTION_MESSAGE</code>. Any
|
||||
* info or error message explaining the resolution.
|
||||
*/
|
||||
final TableField<HistoryRecord, String> RESOLUTION_MESSAGE = createField(DSL.name("RESOLUTION_MESSAGE"), SQLDataType.CLOB, this, "Any info or error message explaining the resolution.");
|
||||
|
||||
private History(Name alias, Table<HistoryRecord> aliased) {
|
||||
this(alias, aliased, (Field<?>[]) null, null);
|
||||
}
|
||||
@ -136,7 +155,8 @@ class History extends TableImpl<HistoryRecord> {
|
||||
@Override
|
||||
public List<Check<HistoryRecord>> getChecks() {
|
||||
return Arrays.asList(
|
||||
Internal.createCheck(this, DSL.name("JOOQ_MIGR_HIST_CHK1"), "\"STATUS\" IN('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE')", true)
|
||||
Internal.createCheck(this, DSL.name("JOOQ_MIGR_HIST_CHK1"), "\"STATUS\" IN('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE')", true),
|
||||
Internal.createCheck(this, DSL.name("JOOQ_MIGR_HIST_CHK2"), "\"RESOLUTION\" IN('OPEN', 'RESOLVED', 'IGNORED')", true)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -42,8 +42,11 @@ 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.noCondition;
|
||||
import static org.jooq.impl.DSL.schema;
|
||||
import static org.jooq.impl.History.HISTORY;
|
||||
import static org.jooq.impl.MigrationImpl.Resolution.OPEN;
|
||||
import static org.jooq.impl.MigrationImpl.Resolution.RESOLVED;
|
||||
import static org.jooq.impl.MigrationImpl.Status.SUCCESS;
|
||||
import static org.jooq.impl.Tools.isEmpty;
|
||||
import static org.jooq.tools.StringUtils.isBlank;
|
||||
@ -70,8 +73,10 @@ import org.jooq.conf.MigrationSchema;
|
||||
import org.jooq.conf.RenderMapping;
|
||||
import org.jooq.exception.DataAccessException;
|
||||
import org.jooq.exception.DataMigrationVerificationException;
|
||||
import org.jooq.impl.MigrationImpl.Resolution;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.ApiStatus.Experimental;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
@ -86,6 +91,8 @@ class HistoryImpl extends AbstractScope implements History {
|
||||
HistoryImpl(Configuration configuration) {
|
||||
super(configuration);
|
||||
|
||||
// [#9506] TODO: What's the best way to spawn a new JDBC connection from an existing one?
|
||||
// The history interactions should run in an autonomous transaction of the system connection provider
|
||||
this.ctx = configuration.dsl();
|
||||
this.historyCtx = initCtx(configuration, configuration.settings().getMigrationHistorySchema()).dsl();
|
||||
this.commits = configuration.commitProvider().provide();
|
||||
@ -184,12 +191,14 @@ class HistoryImpl extends AbstractScope implements History {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
final HistoryRecord currentHistoryRecord() {
|
||||
final HistoryRecord currentHistoryRecord(boolean successOnly) {
|
||||
return existsHistory()
|
||||
? historyCtx.selectFrom(HISTORY)
|
||||
|
||||
// TODO: How to recover from failure?
|
||||
.where(HISTORY.STATUS.eq(inline(SUCCESS)))
|
||||
.where(successOnly
|
||||
? HISTORY.STATUS.eq(inline(SUCCESS))
|
||||
: HISTORY.STATUS.eq(inline(SUCCESS)).or(HISTORY.RESOLUTION.eq(OPEN)))
|
||||
.orderBy(HISTORY.MIGRATED_AT.desc(), HISTORY.ID.desc())
|
||||
.limit(1)
|
||||
.fetchOne()
|
||||
@ -254,6 +263,18 @@ class HistoryImpl extends AbstractScope implements History {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void resolve(String message) {
|
||||
HistoryRecord h = currentHistoryRecord(false);
|
||||
|
||||
if (h != null)
|
||||
h.setResolution(RESOLVED)
|
||||
.setResolutionMessage(message)
|
||||
.update();
|
||||
else
|
||||
throw new DataMigrationVerificationException("No current history record found to resolve");
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -7,6 +7,7 @@ package org.jooq.impl;
|
||||
import java.sql.Timestamp;
|
||||
|
||||
import org.jooq.Record1;
|
||||
import org.jooq.impl.MigrationImpl.Resolution;
|
||||
import org.jooq.impl.MigrationImpl.Status;
|
||||
|
||||
|
||||
@ -169,6 +170,57 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
|
||||
return (Status) get(8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for <code>JOOQ_MIGRATION_HISTORY.STATUS_MESSAGE</code>. Any info
|
||||
* or error message explaining the status.
|
||||
*/
|
||||
HistoryRecord setStatusMessage(String value) {
|
||||
set(9, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for <code>JOOQ_MIGRATION_HISTORY.STATUS_MESSAGE</code>. Any info
|
||||
* or error message explaining the status.
|
||||
*/
|
||||
String getStatusMessage() {
|
||||
return (String) get(9);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for <code>JOOQ_MIGRATION_HISTORY.RESOLUTION</code>. The error
|
||||
* resolution, if any.
|
||||
*/
|
||||
HistoryRecord setResolution(Resolution value) {
|
||||
set(10, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for <code>JOOQ_MIGRATION_HISTORY.RESOLUTION</code>. The error
|
||||
* resolution, if any.
|
||||
*/
|
||||
Resolution getResolution() {
|
||||
return (Resolution) get(10);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for <code>JOOQ_MIGRATION_HISTORY.RESOLUTION_MESSAGE</code>. Any
|
||||
* info or error message explaining the resolution.
|
||||
*/
|
||||
HistoryRecord setResolutionMessage(String value) {
|
||||
set(11, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for <code>JOOQ_MIGRATION_HISTORY.RESOLUTION_MESSAGE</code>. Any
|
||||
* info or error message explaining the resolution.
|
||||
*/
|
||||
String getResolutionMessage() {
|
||||
return (String) get(11);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Primary key information
|
||||
// -------------------------------------------------------------------------
|
||||
@ -192,7 +244,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
|
||||
/**
|
||||
* Create a detached, initialised HistoryRecord
|
||||
*/
|
||||
HistoryRecord(Integer id, String migratedFrom, String migratedTo, Timestamp migratedAt, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, Status status) {
|
||||
HistoryRecord(Integer id, String migratedFrom, String migratedTo, Timestamp migratedAt, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, Status status, String statusMessage, Resolution resolution, String resolutionMessage) {
|
||||
super(History.HISTORY);
|
||||
|
||||
setId(id);
|
||||
@ -204,6 +256,9 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
|
||||
setSql(sql);
|
||||
setSqlCount(sqlCount);
|
||||
setStatus(status);
|
||||
setStatusMessage(statusMessage);
|
||||
setResolution(resolution);
|
||||
setResolutionMessage(resolutionMessage);
|
||||
resetChangedOnNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,12 +44,15 @@ import static org.jooq.impl.DSL.dropSchemaIfExists;
|
||||
import static org.jooq.impl.DSL.dropTableIfExists;
|
||||
import static org.jooq.impl.History.HISTORY;
|
||||
import static org.jooq.impl.HistoryImpl.initCtx;
|
||||
import static org.jooq.impl.MigrationImpl.Resolution.OPEN;
|
||||
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 java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@ -72,13 +75,14 @@ import org.jooq.exception.DataMigrationException;
|
||||
import org.jooq.exception.DataMigrationVerificationException;
|
||||
import org.jooq.tools.JooqLogger;
|
||||
import org.jooq.tools.StopWatch;
|
||||
import org.jooq.tools.StringUtils;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
final class MigrationImpl extends AbstractScope implements Migration {
|
||||
|
||||
static final JooqLogger log = JooqLogger.getLogger(Migration.class);
|
||||
static final JooqLogger log = JooqLogger.getLogger(Migration.class);
|
||||
final HistoryImpl history;
|
||||
final Commit to;
|
||||
Commit from;
|
||||
@ -133,9 +137,19 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
}
|
||||
|
||||
private final void verify0(DefaultMigrationContext ctx) {
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord();
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord(false);
|
||||
|
||||
if (currentRecord != null) {
|
||||
switch (currentRecord.getStatus()) {
|
||||
case FAILURE:
|
||||
throw new DataMigrationVerificationException("Previous migration attempt from " + currentRecord.getMigratedFrom() + " to " + currentRecord.getMigratedTo() + " has failed. Please resolve before migrating.");
|
||||
|
||||
case STARTING:
|
||||
case REVERTING:
|
||||
case MIGRATING:
|
||||
throw new DataMigrationVerificationException("Ongoing migration from " + currentRecord.getMigratedFrom() + " to " + currentRecord.getMigratedTo() + ". Please wait until it has finished.");
|
||||
}
|
||||
|
||||
Commit currentCommit = commits().get(currentRecord.getMigratedTo());
|
||||
|
||||
if (currentCommit == null)
|
||||
@ -212,90 +226,109 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
|
||||
// TODO: Transactions don't really make sense in most dialects. In some, they do
|
||||
// e.g. PostgreSQL supports transactional DDL. Check if we're getting this right.
|
||||
run(new ContextTransactionalRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DefaultMigrationContext ctx = migrationContext();
|
||||
MigrationListener listener = new MigrationListeners(configuration);
|
||||
run(() -> {
|
||||
DefaultMigrationContext ctx = migrationContext();
|
||||
MigrationListener listener = new MigrationListeners(configuration);
|
||||
|
||||
if (!FALSE.equals(dsl().settings().isMigrationAutoVerification()))
|
||||
verify0(ctx);
|
||||
if (!FALSE.equals(dsl().settings().isMigrationAutoVerification()))
|
||||
verify0(ctx);
|
||||
|
||||
try {
|
||||
listener.migrationStart(ctx);
|
||||
|
||||
if (from().equals(to())) {
|
||||
log.info("jOOQ Migrations", "Version " + to().id() + " is already installed as the current version.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement preconditions
|
||||
// TODO: Implement a listener with a variety of pro / oss features
|
||||
// TODO: Implement additional out-of-the-box sanity checks
|
||||
// TODO: Allow undo migrations only if enabled explicitly
|
||||
// TODO: Add some migration settings, e.g. whether HISTORY.SQL should be filled
|
||||
// TODO: Migrate the HISTORY table with the Migration API
|
||||
// TODO: Create an Enum for HISTORY.STATUS
|
||||
// TODO: Add HISTORY.USERNAME and HOSTNAME columns
|
||||
// TODO: Add HISTORY.COMMENTS column
|
||||
// TODO: Replace (MIGRATED_AT, MIGRATION_TIME) by (MIGRATION_START, MIGRATION_END)
|
||||
|
||||
log.info("jOOQ Migrations", "Version " + from().id() + " is being migrated to " + to().id());
|
||||
|
||||
StopWatch watch = new StopWatch();
|
||||
|
||||
// TODO: Make logging configurable
|
||||
if (log.isDebugEnabled())
|
||||
for (Query query : queries())
|
||||
log.debug("jOOQ Migrations", dsl().renderInlined(query));
|
||||
|
||||
HistoryRecord record = createRecord(STARTING);
|
||||
|
||||
try {
|
||||
listener.migrationStart(ctx);
|
||||
|
||||
if (from().equals(to())) {
|
||||
log.info("jOOQ Migrations", "Version " + to().id() + " is already installed as the current version.");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Implement preconditions
|
||||
// TODO: Implement a listener with a variety of pro / oss features
|
||||
// TODO: Implement additional out-of-the-box sanity checks
|
||||
// TODO: Allow undo migrations only if enabled explicitly
|
||||
// TODO: Add some migration settings, e.g. whether HISTORY.SQL should be filled
|
||||
// TODO: Migrate the HISTORY table with the Migration API
|
||||
// TODO: Create an Enum for HISTORY.STATUS
|
||||
// TODO: Add HISTORY.USERNAME and HOSTNAME columns
|
||||
// TODO: Add HISTORY.COMMENTS column
|
||||
// TODO: Replace (MIGRATED_AT, MIGRATION_TIME) by (MIGRATION_START, MIGRATION_END)
|
||||
|
||||
log.info("jOOQ Migrations", "Version " + from().id() + " is migrated to " + to().id());
|
||||
|
||||
StopWatch watch = new StopWatch();
|
||||
|
||||
// TODO: Make logging configurable
|
||||
if (log.isDebugEnabled())
|
||||
for (Query query : queries())
|
||||
log.debug("jOOQ Migrations", dsl().renderInlined(query));
|
||||
|
||||
HistoryRecord record = createRecord(STARTING);
|
||||
|
||||
try {
|
||||
log(watch, record, REVERTING);
|
||||
revertUntracked(ctx, listener, record);
|
||||
log(watch, record, MIGRATING);
|
||||
execute(ctx, listener, queries());
|
||||
log(watch, record, SUCCESS);
|
||||
}
|
||||
catch (DataAccessException e) {
|
||||
|
||||
// TODO: Make sure this is committed, given that we're re-throwing the exception.
|
||||
// TODO: How can we recover from failure?
|
||||
log(watch, record, FAILURE);
|
||||
throw e;
|
||||
}
|
||||
log(watch, record, REVERTING);
|
||||
revertUntracked(ctx, listener, record);
|
||||
log(watch, record, MIGRATING);
|
||||
execute(ctx, listener, queries());
|
||||
log(watch, record, SUCCESS);
|
||||
}
|
||||
finally {
|
||||
listener.migrationEnd(ctx);
|
||||
catch (Exception e) {
|
||||
StringWriter s = new StringWriter();
|
||||
e.printStackTrace(new PrintWriter(s));
|
||||
|
||||
log.error("jOOQ Migrations", "Version " + from().id() + " migration to " + to().id() + " failed: " + e.getMessage());
|
||||
log(watch, record, FAILURE, OPEN, s.toString());
|
||||
throw new DataMigrationRedoLogException(record, e);
|
||||
}
|
||||
}
|
||||
|
||||
private final HistoryRecord createRecord(Status status) {
|
||||
HistoryRecord record = history.historyCtx.newRecord(HISTORY);
|
||||
|
||||
record
|
||||
.setJooqVersion(Constants.VERSION)
|
||||
.setMigratedAt(new Timestamp(dsl().configuration().clock().instant().toEpochMilli()))
|
||||
.setMigratedFrom(from().id())
|
||||
.setMigratedTo(to().id())
|
||||
.setMigrationTime(0L)
|
||||
.setSql(queries().toString())
|
||||
.setSqlCount(queries().queries().length)
|
||||
.setStatus(status)
|
||||
.insert();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
private final void log(StopWatch watch, HistoryRecord record, Status status) {
|
||||
record.setMigrationTime(watch.split() / 1000000L)
|
||||
.setStatus(status)
|
||||
.update();
|
||||
finally {
|
||||
listener.migrationEnd(ctx);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal wrapper class for exceptions that allows for re-creating the
|
||||
* {@link HistoryRecord} in case it was rolled back.
|
||||
*/
|
||||
static final class DataMigrationRedoLogException extends DataMigrationException {
|
||||
|
||||
final HistoryRecord record;
|
||||
|
||||
public DataMigrationRedoLogException(HistoryRecord record, Exception cause) {
|
||||
super("Redo log", cause);
|
||||
|
||||
this.record = record;
|
||||
}
|
||||
}
|
||||
|
||||
private final HistoryRecord createRecord(Status status) {
|
||||
HistoryRecord record = history.historyCtx.newRecord(HISTORY);
|
||||
|
||||
record
|
||||
.setJooqVersion(Constants.VERSION)
|
||||
.setMigratedAt(new Timestamp(dsl().configuration().clock().instant().toEpochMilli()))
|
||||
.setMigratedFrom(from().id())
|
||||
.setMigratedTo(to().id())
|
||||
.setMigrationTime(0L)
|
||||
.setSql(queries().toString())
|
||||
.setSqlCount(queries().queries().length)
|
||||
.setStatus(status)
|
||||
.insert();
|
||||
|
||||
return record;
|
||||
}
|
||||
|
||||
private final void log(StopWatch watch, HistoryRecord record, Status status) {
|
||||
log(watch, record, status, null, null);
|
||||
}
|
||||
|
||||
private final void log(StopWatch watch, HistoryRecord record, Status status, Resolution resolution, String message) {
|
||||
record.setMigrationTime(watch.split() / 1000000L)
|
||||
.setStatus(status)
|
||||
.setStatusMessage(message)
|
||||
.setResolution(resolution)
|
||||
.update();
|
||||
}
|
||||
|
||||
private final void execute(DefaultMigrationContext ctx, MigrationListener listener, Queries q) {
|
||||
// TODO: Can we access the individual Queries from Version, if applicable?
|
||||
// TODO: Set the ctx.queriesFrom(), ctx.queriesTo(), and ctx.queries() values
|
||||
@ -327,7 +360,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
}
|
||||
|
||||
final Commit currentCommit() {
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord();
|
||||
HistoryRecord currentRecord = history.currentHistoryRecord(true);
|
||||
|
||||
if (currentRecord == null) {
|
||||
Commit result = TRUE.equals(settings().isMigrationAutoBaseline()) ? to() : to().root();
|
||||
@ -352,6 +385,21 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
init();
|
||||
dsl().transaction(runnable);
|
||||
}
|
||||
catch (DataMigrationRedoLogException e) {
|
||||
|
||||
// [#9506] Make sure history record is re-created in case it was rolled back.
|
||||
HistoryRecord record = history.currentHistoryRecord(false);
|
||||
|
||||
if (record == null || !StringUtils.equals(e.record.getId(), record.getId())) {
|
||||
e.record.changed(true);
|
||||
e.record.insert();
|
||||
}
|
||||
|
||||
if (e.getCause() instanceof DataMigrationException r)
|
||||
throw r;
|
||||
else
|
||||
throw new DataMigrationException("Exception during migration", e);
|
||||
}
|
||||
catch (DataMigrationException e) {
|
||||
throw e;
|
||||
}
|
||||
@ -368,6 +416,12 @@ final class MigrationImpl extends AbstractScope implements Migration {
|
||||
FAILURE
|
||||
}
|
||||
|
||||
enum Resolution {
|
||||
OPEN,
|
||||
RESOLVED,
|
||||
IGNORED
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// The Object API
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@ -1,26 +1,33 @@
|
||||
CREATE TABLE jooq_migration_history (
|
||||
id BIGINT NOT NULL IDENTITY,
|
||||
migrated_from VARCHAR(255) NOT NULL,
|
||||
migrated_to VARCHAR(255) NOT NULL,
|
||||
migrated_at TIMESTAMP NOT NULL,
|
||||
migration_time BIGINT NULL,
|
||||
jooq_version VARCHAR(50) NOT NULL,
|
||||
sql CLOB NULL,
|
||||
sql_count INT NOT NULL,
|
||||
status VARCHAR(10) NOT NULL,
|
||||
id BIGINT NOT NULL IDENTITY,
|
||||
migrated_from VARCHAR(255) NOT NULL,
|
||||
migrated_to VARCHAR(255) NOT NULL,
|
||||
migrated_at TIMESTAMP NOT NULL,
|
||||
migration_time BIGINT NULL,
|
||||
jooq_version VARCHAR(50) NOT NULL,
|
||||
sql CLOB NULL,
|
||||
sql_count INT NOT NULL,
|
||||
status VARCHAR(10) NOT NULL,
|
||||
status_message CLOB NULL,
|
||||
resolution VARCHAR(10) NULL,
|
||||
resolution_message CLOB NULL,
|
||||
|
||||
CONSTRAINT jooq_migr_hist_pk PRIMARY KEY (id),
|
||||
CONSTRAINT jooq_migr_hist_chk1 CHECK (status IN ('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE'))
|
||||
CONSTRAINT jooq_migr_hist_chk1 CHECK (status IN ('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE')),
|
||||
CONSTRAINT jooq_migr_hist_chk2 CHECK (resolution IN ('OPEN', 'RESOLVED', 'IGNORED'))
|
||||
);
|
||||
|
||||
CREATE INDEX jooq_migr_hist_i1 ON jooq_migration_history (migrated_at);
|
||||
|
||||
COMMENT ON TABLE jooq_migration_history IS 'The migration history of jOOQ Migrations.';
|
||||
COMMENT ON COLUMN jooq_migration_history.id IS 'The database version ID.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migrated_from IS 'The previous database version ID.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migrated_at IS 'The date/time when the database version was migrated to.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migration_time IS 'The time in milliseconds it took to migrate to this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.jooq_version IS 'The jOOQ version used to migrate to this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.sql_count IS 'The number of SQL statements that were run to install this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.sql IS 'The SQL statements that were run to install this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.status IS 'The database version installation status.';
|
||||
COMMENT ON TABLE jooq_migration_history IS 'The migration history of jOOQ Migrations.';
|
||||
COMMENT ON COLUMN jooq_migration_history.id IS 'The database version ID.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migrated_from IS 'The previous database version ID.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migrated_at IS 'The date/time when the database version was migrated to.';
|
||||
COMMENT ON COLUMN jooq_migration_history.migration_time IS 'The time in milliseconds it took to migrate to this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.jooq_version IS 'The jOOQ version used to migrate to this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.sql_count IS 'The number of SQL statements that were run to install this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.sql IS 'The SQL statements that were run to install this database version.';
|
||||
COMMENT ON COLUMN jooq_migration_history.status IS 'The database version installation status.';
|
||||
COMMENT ON COLUMN jooq_migration_history.status_message IS 'Any info or error message explaining the status.';
|
||||
COMMENT ON COLUMN jooq_migration_history.resolution IS 'The error resolution, if any.';
|
||||
COMMENT ON COLUMN jooq_migration_history.resolution_message IS 'Any info or error message explaining the resolution.';
|
||||
Loading…
Reference in New Issue
Block a user