[jOOQ/jOOQ#9506] More work on Migrations API:

- Handling of default schema in MigrationImpl::revertUntrackedQueries
- Use Settings.migrationHistorySchema in revertUntrackedQueries
- Improve logic to filter out history table from revertUntrackedQueries
- Set migration default schema as interpreter search path in History
- Suppress exceptions caused by history record re-insertion in case of
DataMigrationRedoLogException
- VersionImpl should apply CREATE SCHEMA IF NOT EXISTS to on init
- Remove weird initialisation of VersionImpl.meta with interpreter
search path
- Implement DefaultMigrationContext::toString
- Automatically set interpreter search path in Maven plugin
- GitCommitProvider should use same content type subdirs (in plural)
- Add commitProvider property to Maven plugin
- Upgrade jgit
- Allow for configuring a basedir within the GitConfiguration#repository
This commit is contained in:
Lukas Eder 2024-11-21 15:21:54 +01:00
parent f086212738
commit 1dd1205673
14 changed files with 236 additions and 92 deletions

View File

@ -37,9 +37,6 @@
*/ */
package org.jooq.migrations.maven; package org.jooq.migrations.maven;
import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_SOURCES;
import static org.apache.maven.plugins.annotations.ResolutionScope.TEST;
import java.io.File; import java.io.File;
import org.jooq.CommitProvider; import org.jooq.CommitProvider;
@ -47,9 +44,9 @@ import org.jooq.Commits;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.Migration; import org.jooq.Migration;
import org.jooq.Migrations; import org.jooq.Migrations;
import org.jooq.impl.DefaultCommitProvider;
import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
/** /**
* A base class for {@link MigrateMojo}, * A base class for {@link MigrateMojo},
@ -63,21 +60,18 @@ public abstract class AbstractMigrateMojo extends AbstractMigrationsMojo {
if (directory == null) if (directory == null)
throw new MojoExecutionException("Directory was not provided"); throw new MojoExecutionException("Directory was not provided");
Migrations migrations = configuration.dsl().migrations(); CommitProvider cp = configuration.commitProvider();
Commits commits = migrations.commits(); if (cp instanceof DefaultCommitProvider) {
Migrations migrations = configuration.dsl().migrations();
Commits commits = migrations.commits();
commits.load(file(directory));
}
// [#9506] TODO: Support loading directories recursively
// [#9506] TODO: Support loading **/*.sql style paths
// [#9506] TODO: Support relative paths, absolute paths, etc.
commits.load(file(directory));
// [#9506] TODO: Having to use this CommitsProvider "trick" isn't really
// user friendly. There must be a better way
Migration migration = configuration Migration migration = configuration
.derive((CommitProvider) () -> commits) .derive(cp)
.dsl() .dsl()
.migrations() .migrations()
.migrateTo(commits.latest()); .migrateTo(cp.provide().latest());
if (getLog().isInfoEnabled()) if (getLog().isInfoEnabled())
getLog().info( getLog().info(

View File

@ -46,9 +46,12 @@ import java.net.URLClassLoader;
import java.util.List; import java.util.List;
import org.jooq.CloseableDSLContext; import org.jooq.CloseableDSLContext;
import org.jooq.CommitProvider;
import org.jooq.Configuration; import org.jooq.Configuration;
import org.jooq.conf.InterpreterSearchSchema;
import org.jooq.conf.MigrationSchema; import org.jooq.conf.MigrationSchema;
import org.jooq.impl.DSL; import org.jooq.impl.DSL;
import org.jooq.impl.DefaultCommitProvider;
import org.jooq.tools.ClassUtils; import org.jooq.tools.ClassUtils;
import org.jooq.tools.jdbc.JDBCUtils; import org.jooq.tools.jdbc.JDBCUtils;
@ -146,6 +149,12 @@ abstract class AbstractMigrationsMojo extends AbstractMojo {
@Parameter(property = "jooq.migrate.historySchemaCreateSchemaIfNotExists") @Parameter(property = "jooq.migrate.historySchemaCreateSchemaIfNotExists")
boolean historySchemaCreateSchemaIfNotExists; boolean historySchemaCreateSchemaIfNotExists;
/**
* The {@link CommitProvider} implementation, defaulting to {@link DefaultCommitProvider}.
*/
@Parameter(property = "jooq.migrate.commitProvider")
String commitProvider;
@Override @Override
public final void execute() throws MojoExecutionException { public final void execute() throws MojoExecutionException {
if (skip) { if (skip) {
@ -174,13 +183,29 @@ abstract class AbstractMigrationsMojo extends AbstractMojo {
// Initialise Settings // Initialise Settings
// --------------------------------------------------------------------- // ---------------------------------------------------------------------
// TODO [#9506]: What are accepted constructor signatures?
if (commitProvider != null)
ctx.configuration().set((CommitProvider) ClassUtils
.loadClass(commitProvider)
.getConstructor(Configuration.class)
.newInstance(ctx.configuration())
);
ctx.settings().getMigrationSchemata().addAll(schemata); ctx.settings().getMigrationSchemata().addAll(schemata);
if (defaultCatalog != null || defaultSchema != null) if (defaultCatalog != null || defaultSchema != null) {
ctx.settings().setMigrationDefaultSchema(new MigrationSchema() ctx.settings()
.withCatalog(defaultIfNull(defaultCatalog, "")) .withMigrationDefaultSchema(new MigrationSchema()
.withSchema(defaultIfNull(defaultSchema, "")) .withCatalog(defaultIfNull(defaultCatalog, ""))
); .withSchema(defaultIfNull(defaultSchema, ""))
)
// [#9506] TODO: This should be automatic, even for programmatic usage
.withInterpreterSearchPath(new InterpreterSearchSchema()
.withCatalog(defaultIfNull(defaultCatalog, ""))
.withSchema(defaultIfNull(defaultSchema, ""))
);
}
if (historyCatalog != null || historySchema != null) if (historyCatalog != null || historySchema != null)
ctx.settings().setMigrationHistorySchema(new MigrationSchema() ctx.settings().setMigrationHistorySchema(new MigrationSchema()

View File

@ -83,7 +83,7 @@
<dependency> <dependency>
<groupId>org.eclipse.jgit</groupId> <groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId> <artifactId>org.eclipse.jgit</artifactId>
<version>6.6.1.202309021850-r</version> <version>7.0.0.202409031743-r</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -65,6 +65,7 @@ import org.jooq.File;
import org.jooq.FilePattern; import org.jooq.FilePattern;
import org.jooq.Migrations; import org.jooq.Migrations;
import org.jooq.tools.JooqLogger; import org.jooq.tools.JooqLogger;
import org.jooq.tools.StringUtils;
import org.eclipse.jgit.api.Git; import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.Status; import org.eclipse.jgit.api.Status;
@ -81,7 +82,6 @@ import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTag; import org.eclipse.jgit.revwalk.RevTag;
import org.eclipse.jgit.treewalk.TreeWalk; import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.io.DisabledOutputStream; import org.eclipse.jgit.util.io.DisabledOutputStream;
import org.jetbrains.annotations.NotNull;
/** /**
* A {@link CommitProvider} that produces versions from a git repository. * A {@link CommitProvider} that produces versions from a git repository.
@ -98,12 +98,25 @@ public final class GitCommitProvider implements CommitProvider {
private final FilePattern incrementFilePattern; private final FilePattern incrementFilePattern;
private final FilePattern schemaFilePattern; private final FilePattern schemaFilePattern;
public GitCommitProvider(Configuration configuration) {
this(configuration, new GitConfiguration());
}
public GitCommitProvider(Configuration configuration, GitConfiguration git) { public GitCommitProvider(Configuration configuration, GitConfiguration git) {
this.dsl = configuration.dsl(); this.dsl = configuration.dsl();
this.migrations = dsl.migrations(); this.migrations = dsl.migrations();
this.git = git; this.git = git;
this.incrementFilePattern = new FilePattern().pattern(git.incrementFilePattern()); this.incrementFilePattern = new FilePattern().pattern(combine(git.basedir(), git.incrementFilePattern()));
this.schemaFilePattern = new FilePattern().pattern(git.schemaFilePattern()); this.schemaFilePattern = new FilePattern().pattern(combine(git.basedir(), git.schemaFilePattern()));
}
private static final String combine(String basedir, String pattern) {
if (StringUtils.isEmpty(basedir))
return pattern;
else if (basedir.endsWith("/"))
return basedir + pattern;
else
return basedir + "/" + pattern;
} }
@Override @Override
@ -115,6 +128,9 @@ public final class GitCommitProvider implements CommitProvider {
Repository r = g.getRepository(); Repository r = g.getRepository();
ObjectReader reader = r.newObjectReader() ObjectReader reader = r.newObjectReader()
) { ) {
// Prevent a "close() called when useCnt is already zero" warning
r.incrementOpen();
List<RevCommit> revCommits = new ArrayList<>(); List<RevCommit> revCommits = new ArrayList<>();
Map<String, List<RevTag>> tags = new HashMap<>(); Map<String, List<RevTag>> tags = new HashMap<>();
RevCommit last = null; RevCommit last = null;
@ -311,24 +327,34 @@ public final class GitCommitProvider implements CommitProvider {
treeWalk.setRecursive(false); treeWalk.setRecursive(false);
while (treeWalk.next()) { while (treeWalk.next()) {
if (treeWalk.isSubtree()) { String path = treeWalk.getPathString();
if (treeWalk.isSubtree() && include(path)) {
treeWalk.enterSubtree(); treeWalk.enterSubtree();
} }
else { else {
ContentType contentType = contentType(treeWalk.getPathString()); ContentType contentType = contentType(path);
if (contentType != null) if (contentType != null) {
files.add(migrations.file( files.add(migrations.file(
treeWalk.getPathString(), path,
read(repository, revCommit, treeWalk.getPathString()), read(repository, revCommit, path),
contentType contentType
)); ));
}
} }
} }
return files; return files;
} }
private final boolean include(String path) {
// [#9506] TODO: resolve . and ..
return git.basedir().startsWith(path)
|| path.startsWith(git.basedir());
}
private final String read(Repository repository, RevCommit commit, String path) throws IOException { private final String read(Repository repository, RevCommit commit, String path) throws IOException {
try (TreeWalk treeWalk = TreeWalk.forPath(repository, path, commit.getTree())) { try (TreeWalk treeWalk = TreeWalk.forPath(repository, path, commit.getTree())) {
ObjectId blobId = treeWalk.getObjectId(0); ObjectId blobId = treeWalk.getObjectId(0);

View File

@ -40,11 +40,6 @@ package org.jooq.migrations.jgit;
import static org.jooq.tools.StringUtils.defaultIfNull; import static org.jooq.tools.StringUtils.defaultIfNull;
import java.io.File; import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import org.jooq.Commit; import org.jooq.Commit;
import org.jooq.tools.StringUtils; import org.jooq.tools.StringUtils;
@ -58,6 +53,7 @@ import org.jetbrains.annotations.NotNull;
public class GitConfiguration { public class GitConfiguration {
private final File repository; private final File repository;
private final String basedir;
private final String schemaFilePattern; private final String schemaFilePattern;
private final String incrementFilePattern; private final String incrementFilePattern;
private final String scriptFilePattern; private final String scriptFilePattern;
@ -71,12 +67,14 @@ public class GitConfiguration {
null, null,
null, null,
null, null,
null,
true true
); );
} }
private GitConfiguration( private GitConfiguration(
File repository, File repository,
String basedir,
String schemaFilePattern, String schemaFilePattern,
String incrementFilePattern, String incrementFilePattern,
String scriptFilePattern, String scriptFilePattern,
@ -84,10 +82,11 @@ public class GitConfiguration {
boolean includeUncommitted boolean includeUncommitted
) { ) {
this.repository = repository != null ? repository : new File("."); this.repository = repository != null ? repository : new File(".");
this.schemaFilePattern = defaultIfNull(schemaFilePattern, "migrations/schema/**"); this.basedir = basedir != null ? basedir : "src/main/resources";
this.incrementFilePattern = defaultIfNull(incrementFilePattern, "migrations/increment/**"); this.schemaFilePattern = defaultIfNull(schemaFilePattern, "migrations/schemas/**");
this.scriptFilePattern = defaultIfNull(scriptFilePattern, "migrations/script/**"); this.incrementFilePattern = defaultIfNull(incrementFilePattern, "migrations/increments/**");
this.snapshotFilePattern = defaultIfNull(snapshotFilePattern, "migrations/snapshot/**"); this.scriptFilePattern = defaultIfNull(scriptFilePattern, "migrations/scripts/**");
this.snapshotFilePattern = defaultIfNull(snapshotFilePattern, "migrations/snapshots/**");
this.includeUncommitted = includeUncommitted; this.includeUncommitted = includeUncommitted;
} }
@ -98,6 +97,7 @@ public class GitConfiguration {
public final GitConfiguration repository(File newRepository) { public final GitConfiguration repository(File newRepository) {
return new GitConfiguration( return new GitConfiguration(
newRepository, newRepository,
basedir,
schemaFilePattern, schemaFilePattern,
incrementFilePattern, incrementFilePattern,
scriptFilePattern, scriptFilePattern,
@ -114,6 +114,30 @@ public class GitConfiguration {
return repository; return repository;
} }
/**
* The base directory of the migration scripts within the {@link #repository()}.
*/
@NotNull
public final GitConfiguration basedir(String newBasedir) {
return new GitConfiguration(
repository,
newBasedir,
schemaFilePattern,
incrementFilePattern,
scriptFilePattern,
snapshotFilePattern,
includeUncommitted
);
}
/**
* The base directory of the migration scripts within the {@link #repository()}.
*/
@NotNull
public final String basedir() {
return basedir;
}
/** /**
* The patterns of files in the repository to be searched for schema * The patterns of files in the repository to be searched for schema
* definition files. * definition files.
@ -122,6 +146,7 @@ public class GitConfiguration {
public final GitConfiguration schemaFilePattern(String newSchemaFilePattern) { public final GitConfiguration schemaFilePattern(String newSchemaFilePattern) {
return new GitConfiguration( return new GitConfiguration(
repository, repository,
basedir,
newSchemaFilePattern, newSchemaFilePattern,
incrementFilePattern, incrementFilePattern,
scriptFilePattern, scriptFilePattern,
@ -147,6 +172,7 @@ public class GitConfiguration {
public final GitConfiguration incrementFilePattern(String newIncrementFilePattern) { public final GitConfiguration incrementFilePattern(String newIncrementFilePattern) {
return new GitConfiguration( return new GitConfiguration(
repository, repository,
basedir,
schemaFilePattern, schemaFilePattern,
newIncrementFilePattern, newIncrementFilePattern,
scriptFilePattern, scriptFilePattern,
@ -172,6 +198,7 @@ public class GitConfiguration {
public final GitConfiguration includeUncommitted(boolean newIncludeUncommitted) { public final GitConfiguration includeUncommitted(boolean newIncludeUncommitted) {
return new GitConfiguration( return new GitConfiguration(
repository, repository,
basedir,
schemaFilePattern, schemaFilePattern,
incrementFilePattern, incrementFilePattern,
scriptFilePattern, scriptFilePattern,

View File

@ -122,4 +122,17 @@ final class DefaultMigrationContext extends AbstractScope implements MigrationCo
final void query(Query q) { final void query(Query q) {
this.query = q; this.query = q;
} }
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(
"MigrationContext for migration from: " + migrationFrom + " to " + migrationTo + "\n"
+ "Migration queries:\n" + migrationQueries
+ "Revert untracked queries:\n" + revertUntrackedQueries
);
return sb.toString();
}
} }

View File

@ -22,7 +22,7 @@ import org.jooq.UniqueKey;
/** /**
* The migration history of jOOQ Migrations. * The migration history of jOOQ Migrations.
*/ */
@SuppressWarnings({ "all", "unchecked", "rawtypes" }) @SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
class History extends TableImpl<HistoryRecord> { class History extends TableImpl<HistoryRecord> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -64,6 +64,11 @@ class History extends TableImpl<HistoryRecord> {
*/ */
final TableField<HistoryRecord, String> MIGRATED_TO = createField(DSL.name("MIGRATED_TO"), SQLDataType.VARCHAR(255).nullable(false), this, "The current database version ID."); final TableField<HistoryRecord, String> MIGRATED_TO = createField(DSL.name("MIGRATED_TO"), SQLDataType.VARCHAR(255).nullable(false), this, "The current database version ID.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_MESSAGE</code>.
*/
final TableField<HistoryRecord, String> MIGRATED_TO_MESSAGE = createField(DSL.name("MIGRATED_TO_MESSAGE"), SQLDataType.CLOB.nullable(false), this, "");
/** /**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_TAGS</code>. The * The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_TAGS</code>. The
* current database version tags, if any, in JSON array format. * current database version tags, if any, in JSON array format.

View File

@ -139,7 +139,7 @@ class HistoryImpl extends AbstractScope implements History {
private final void addSchema(Set<Schema> set, MigrationSchema schema) { private final void addSchema(Set<Schema> set, MigrationSchema schema) {
if (schema != null) if (schema != null)
set.addAll(lookup(asList(schema(name(schema.getCatalog(), schema.getSchema()))))); set.addAll(lookup(asList(MigrationImpl.schema(schema))));
} }
final Collection<Schema> lookup(List<Schema> schemas) { final Collection<Schema> lookup(List<Schema> schemas) {
@ -187,6 +187,11 @@ class HistoryImpl extends AbstractScope implements History {
); );
} }
result.settings().withInterpreterSearchPath(new InterpreterSearchSchema()
.withCatalog(defaultSchema.getCatalog())
.withSchema(defaultSchema.getSchema())
);
return result; return result;
} }
else else

View File

@ -12,7 +12,7 @@ import org.jooq.Record1;
/** /**
* The migration history of jOOQ Migrations. * The migration history of jOOQ Migrations.
*/ */
@SuppressWarnings({ "all", "unchecked", "rawtypes" }) @SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> { class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
@ -85,12 +85,27 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
return (String) get(3); return (String) get(3);
} }
/**
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_MESSAGE</code>.
*/
HistoryRecord setMigratedToMessage(String value) {
set(4, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_MESSAGE</code>.
*/
String getMigratedToMessage() {
return (String) get(4);
}
/** /**
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_TAGS</code>. The * Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO_TAGS</code>. The
* current database version tags, if any, in JSON array format. * current database version tags, if any, in JSON array format.
*/ */
HistoryRecord setMigratedToTags(String value) { HistoryRecord setMigratedToTags(String value) {
set(4, value); set(5, value);
return this; return this;
} }
@ -99,7 +114,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* current database version tags, if any, in JSON array format. * current database version tags, if any, in JSON array format.
*/ */
String getMigratedToTags() { String getMigratedToTags() {
return (String) get(4); return (String) get(5);
} }
/** /**
@ -107,7 +122,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* in milliseconds it took to migrate to this database version. * in milliseconds it took to migrate to this database version.
*/ */
HistoryRecord setMigrationTime(Long value) { HistoryRecord setMigrationTime(Long value) {
set(5, value); set(6, value);
return this; return this;
} }
@ -116,7 +131,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* in milliseconds it took to migrate to this database version. * in milliseconds it took to migrate to this database version.
*/ */
Long getMigrationTime() { Long getMigrationTime() {
return (Long) get(5); return (Long) get(6);
} }
/** /**
@ -124,7 +139,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* version used to migrate to this database version. * version used to migrate to this database version.
*/ */
HistoryRecord setJooqVersion(String value) { HistoryRecord setJooqVersion(String value) {
set(6, value); set(7, value);
return this; return this;
} }
@ -133,7 +148,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* version used to migrate to this database version. * version used to migrate to this database version.
*/ */
String getJooqVersion() { String getJooqVersion() {
return (String) get(6); return (String) get(7);
} }
/** /**
@ -141,7 +156,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* that were run to install this database version. * that were run to install this database version.
*/ */
HistoryRecord setSql(String value) { HistoryRecord setSql(String value) {
set(7, value); set(8, value);
return this; return this;
} }
@ -150,7 +165,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* that were run to install this database version. * that were run to install this database version.
*/ */
String getSql() { String getSql() {
return (String) get(7); return (String) get(8);
} }
/** /**
@ -158,7 +173,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* SQL statements that were run to install this database version. * SQL statements that were run to install this database version.
*/ */
HistoryRecord setSqlCount(Integer value) { HistoryRecord setSqlCount(Integer value) {
set(8, value); set(9, value);
return this; return this;
} }
@ -167,7 +182,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* SQL statements that were run to install this database version. * SQL statements that were run to install this database version.
*/ */
Integer getSqlCount() { Integer getSqlCount() {
return (Integer) get(8); return (Integer) get(9);
} }
/** /**
@ -175,7 +190,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* version installation status. * version installation status.
*/ */
HistoryRecord setStatus(HistoryStatus value) { HistoryRecord setStatus(HistoryStatus value) {
set(9, value); set(10, value);
return this; return this;
} }
@ -184,7 +199,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* version installation status. * version installation status.
*/ */
HistoryStatus getStatus() { HistoryStatus getStatus() {
return (HistoryStatus) get(9); return (HistoryStatus) get(10);
} }
/** /**
@ -192,7 +207,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* or error message explaining the status. * or error message explaining the status.
*/ */
HistoryRecord setStatusMessage(String value) { HistoryRecord setStatusMessage(String value) {
set(10, value); set(11, value);
return this; return this;
} }
@ -201,7 +216,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* or error message explaining the status. * or error message explaining the status.
*/ */
String getStatusMessage() { String getStatusMessage() {
return (String) get(10); return (String) get(11);
} }
/** /**
@ -209,7 +224,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* resolution, if any. * resolution, if any.
*/ */
HistoryRecord setResolution(HistoryResolution value) { HistoryRecord setResolution(HistoryResolution value) {
set(11, value); set(12, value);
return this; return this;
} }
@ -218,7 +233,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* resolution, if any. * resolution, if any.
*/ */
HistoryResolution getResolution() { HistoryResolution getResolution() {
return (HistoryResolution) get(11); return (HistoryResolution) get(12);
} }
/** /**
@ -226,7 +241,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* info or error message explaining the resolution. * info or error message explaining the resolution.
*/ */
HistoryRecord setResolutionMessage(String value) { HistoryRecord setResolutionMessage(String value) {
set(12, value); set(13, value);
return this; return this;
} }
@ -235,7 +250,7 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
* info or error message explaining the resolution. * info or error message explaining the resolution.
*/ */
String getResolutionMessage() { String getResolutionMessage() {
return (String) get(12); return (String) get(13);
} }
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
@ -261,13 +276,14 @@ class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
/** /**
* Create a detached, initialised HistoryRecord * Create a detached, initialised HistoryRecord
*/ */
HistoryRecord(Integer id, Timestamp migratedAt, String migratedFrom, String migratedTo, String migratedToTags, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, HistoryStatus status, String statusMessage, HistoryResolution resolution, String resolutionMessage) { HistoryRecord(Integer id, Timestamp migratedAt, String migratedFrom, String migratedTo, String migratedToMessage, String migratedToTags, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, HistoryStatus status, String statusMessage, HistoryResolution resolution, String resolutionMessage) {
super(History.HISTORY); super(History.HISTORY);
setId(id); setId(id);
setMigratedAt(migratedAt); setMigratedAt(migratedAt);
setMigratedFrom(migratedFrom); setMigratedFrom(migratedFrom);
setMigratedTo(migratedTo); setMigratedTo(migratedTo);
setMigratedToMessage(migratedToMessage);
setMigratedToTags(migratedToTags); setMigratedToTags(migratedToTags);
setMigrationTime(migrationTime); setMigrationTime(migrationTime);
setJooqVersion(jooqVersion); setJooqVersion(jooqVersion);

View File

@ -12,7 +12,7 @@ import org.jooq.Schema;
/** /**
* This class is generated by jOOQ. * This class is generated by jOOQ.
*/ */
@SuppressWarnings({ "all", "unchecked", "rawtypes" }) @SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
public enum HistoryResolution implements EnumType { public enum HistoryResolution implements EnumType {
OPEN("OPEN"), OPEN("OPEN"),

View File

@ -12,7 +12,7 @@ import org.jooq.Schema;
/** /**
* This class is generated by jOOQ. * This class is generated by jOOQ.
*/ */
@SuppressWarnings({ "all", "unchecked", "rawtypes" }) @SuppressWarnings({ "all", "unchecked", "rawtypes", "this-escape" })
public enum HistoryStatus implements EnumType { public enum HistoryStatus implements EnumType {
STARTING("STARTING"), STARTING("STARTING"),

View File

@ -39,9 +39,12 @@ package org.jooq.impl;
import static java.lang.Boolean.FALSE; import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE; import static java.lang.Boolean.TRUE;
import static java.util.function.Predicate.not;
import static org.jooq.impl.DSL.createSchemaIfNotExists; import static org.jooq.impl.DSL.createSchemaIfNotExists;
import static org.jooq.impl.DSL.dropSchemaIfExists; import static org.jooq.impl.DSL.dropSchemaIfExists;
import static org.jooq.impl.DSL.dropTableIfExists; import static org.jooq.impl.DSL.dropTableIfExists;
import static org.jooq.impl.DSL.name;
import static org.jooq.impl.DSL.table;
import static org.jooq.impl.History.HISTORY; import static org.jooq.impl.History.HISTORY;
import static org.jooq.impl.HistoryImpl.initCtx; import static org.jooq.impl.HistoryImpl.initCtx;
import static org.jooq.impl.HistoryResolution.OPEN; import static org.jooq.impl.HistoryResolution.OPEN;
@ -50,6 +53,7 @@ import static org.jooq.impl.HistoryStatus.MIGRATING;
import static org.jooq.impl.HistoryStatus.REVERTING; import static org.jooq.impl.HistoryStatus.REVERTING;
import static org.jooq.impl.HistoryStatus.STARTING; import static org.jooq.impl.HistoryStatus.STARTING;
import static org.jooq.impl.HistoryStatus.SUCCESS; import static org.jooq.impl.HistoryStatus.SUCCESS;
import static org.jooq.impl.SchemaImpl.DEFAULT_SCHEMA;
import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.map;
import java.io.PrintWriter; import java.io.PrintWriter;
@ -57,6 +61,7 @@ import java.io.StringWriter;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.function.Predicate;
import org.jooq.Commit; import org.jooq.Commit;
import org.jooq.Commits; import org.jooq.Commits;
@ -71,7 +76,11 @@ import org.jooq.MigrationListener;
import org.jooq.Queries; import org.jooq.Queries;
import org.jooq.Query; import org.jooq.Query;
import org.jooq.Schema; import org.jooq.Schema;
import org.jooq.Table;
import org.jooq.Tag; import org.jooq.Tag;
import org.jooq.conf.InterpreterSearchSchema;
import org.jooq.conf.MigrationSchema;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataMigrationException; import org.jooq.exception.DataMigrationException;
import org.jooq.exception.DataMigrationVerificationException; import org.jooq.exception.DataMigrationVerificationException;
import org.jooq.tools.JooqLogger; import org.jooq.tools.JooqLogger;
@ -101,6 +110,10 @@ final class MigrationImpl extends AbstractScope implements Migration {
this.history = new HistoryImpl(configuration()); this.history = new HistoryImpl(configuration());
} }
static final Schema schema(MigrationSchema schema) {
return new SchemaImpl(name(schema.getCatalog(), schema.getSchema()));
}
@Override @Override
public final Commit from() { public final Commit from() {
if (from == null) if (from == null)
@ -184,23 +197,39 @@ final class MigrationImpl extends AbstractScope implements Migration {
} }
private final Queries revertUntrackedQueries(Set<Schema> includedSchemas) { private final Queries revertUntrackedQueries(Set<Schema> includedSchemas) {
MigrationSchema hs = settings().getMigrationHistorySchema();
MigrationSchema ds = settings().getMigrationDefaultSchema();
Set<Table<?>> historyTables = new HashSet<>();
if (hs != null || ds != null)
historyTables.add(table(schema(hs != null ? hs : ds).getQualifiedName().append(HISTORY.getUnqualifiedName())));
else
historyTables.addAll(map(includedSchemas, s -> table(s.getQualifiedName().append(HISTORY.getUnqualifiedName()))));
Commit currentCommit = currentCommit(); Commit currentCommit = currentCommit();
Meta currentMeta = currentCommit.meta(); Meta currentMeta = currentCommit.meta();
Meta existingMeta = dsl().meta().filterSchemas(includedSchemas::contains); Meta existingMeta = dsl().meta()
.filterSchemas(includedSchemas::contains)
.filterTables(not(historyTables::contains));
Set<Schema> expectedSchemas = new HashSet<>(); Set<Schema> expectedSchemas = new HashSet<>();
expectedSchemas.addAll(history.lookup(from().meta().getSchemas())); expectedSchemas.addAll(history.lookup(from().meta().getSchemas()));
expectedSchemas.addAll(history.lookup(to().meta().getSchemas())); expectedSchemas.addAll(history.lookup(to().meta().getSchemas()));
expectedSchemas.retainAll(includedSchemas); expectedSchemas.retainAll(includedSchemas);
if (ds != null) {
Schema d = DEFAULT_SCHEMA.get();
if (expectedSchemas.contains(d) && includedSchemas.contains(d))
expectedSchemas.add(schema(ds));
}
schemaLoop: schemaLoop:
for (Schema schema : existingMeta.getSchemas()) { for (Schema schema : existingMeta.getSchemas()) {
if (!includedSchemas.contains(schema)) if (!includedSchemas.contains(schema))
continue schemaLoop; continue schemaLoop;
// TODO Why is this qualification necessary?
existingMeta = existingMeta.apply(dropTableIfExists(schema.getQualifiedName().append(HISTORY.getUnqualifiedName())).cascade());
if (!expectedSchemas.contains(schema)) if (!expectedSchemas.contains(schema))
existingMeta = existingMeta.apply(dropSchemaIfExists(schema).cascade()); existingMeta = existingMeta.apply(dropSchemaIfExists(schema).cascade());
else else
@ -347,6 +376,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
.setMigratedAt(new Timestamp(dsl().configuration().clock().instant().toEpochMilli())) .setMigratedAt(new Timestamp(dsl().configuration().clock().instant().toEpochMilli()))
.setMigratedFrom(from().id()) .setMigratedFrom(from().id())
.setMigratedTo(to().id()) .setMigratedTo(to().id())
.setMigratedToMessage(to().message())
.setMigratedToTags(new JSONArray(map(to().tags(), Tag::id)).toString()) .setMigratedToTags(new JSONArray(map(to().tags(), Tag::id)).toString())
.setMigrationTime(0L) .setMigrationTime(0L)
.setSql(queries().toString()) .setSql(queries().toString())
@ -426,13 +456,18 @@ final class MigrationImpl extends AbstractScope implements Migration {
dsl().transaction(runnable); dsl().transaction(runnable);
} }
catch (DataMigrationRedoLogException e) { catch (DataMigrationRedoLogException e) {
try {
// [#9506] Make sure history record is re-created in case it was rolled back. // [#9506] Make sure history record is re-created in case it was rolled back.
HistoryRecord record = history.currentHistoryRecord(false); HistoryRecord record = history.currentHistoryRecord(false);
if (record == null || !StringUtils.equals(e.record.getId(), record.getId())) { if (record == null || !StringUtils.equals(e.record.getId(), record.getId())) {
e.record.touched(true); e.record.touched(true);
e.record.insert(); e.record.insert();
}
}
catch (DataAccessException s) {
e.addSuppressed(s);
} }
if (e.getCause() instanceof DataMigrationException r) if (e.getCause() instanceof DataMigrationException r)

View File

@ -41,8 +41,7 @@ import static java.lang.Boolean.TRUE;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.jooq.impl.DSL.createSchema; import static org.jooq.impl.DSL.createSchema;
import static org.jooq.impl.DSL.name; import static org.jooq.impl.DSL.createSchemaIfNotExists;
import static org.jooq.impl.DSL.schema;
import static org.jooq.impl.Tools.anyMatch; import static org.jooq.impl.Tools.anyMatch;
import static org.jooq.impl.Tools.map; import static org.jooq.impl.Tools.map;
@ -60,6 +59,7 @@ import org.jooq.Query;
import org.jooq.Source; import org.jooq.Source;
import org.jooq.Version; import org.jooq.Version;
import org.jooq.conf.InterpreterSearchSchema; import org.jooq.conf.InterpreterSearchSchema;
import org.jooq.conf.MigrationSchema;
import org.jooq.exception.DataDefinitionException; import org.jooq.exception.DataDefinitionException;
/** /**
@ -82,12 +82,9 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
private static final Meta init(DSLContext ctx) { private static final Meta init(DSLContext ctx) {
Meta result = ctx.meta(""); Meta result = ctx.meta("");
// TODO: Instead of reusing interpreter search path, we should have some dedicated MigrationSchema ds = ctx.settings().getMigrationDefaultSchema();
// configuration for this. if (ds != null)
// TODO: Should this be moved in DSLContext.meta()? result = result.apply(createSchemaIfNotExists(MigrationImpl.schema(ds)));
List<InterpreterSearchSchema> searchPath = ctx.settings().getInterpreterSearchPath();
for (InterpreterSearchSchema schema : searchPath)
result = result.apply(createSchema(schema(name(schema.getCatalog(), schema.getSchema()))));
return result; return result;
} }

View File

@ -1,17 +1,18 @@
CREATE TABLE jooq_migration_history ( CREATE TABLE jooq_migration_history (
id BIGINT NOT NULL IDENTITY, id BIGINT NOT NULL IDENTITY,
migrated_at TIMESTAMP NOT NULL, migrated_at TIMESTAMP NOT NULL,
migrated_from VARCHAR(255) NOT NULL, migrated_from VARCHAR(255) NOT NULL,
migrated_to VARCHAR(255) NOT NULL, migrated_to VARCHAR(255) NOT NULL,
migrated_to_tags CLOB NOT NULL, migrated_to_message CLOB NOT NULL,
migration_time BIGINT NULL, migrated_to_tags CLOB NOT NULL,
jooq_version VARCHAR(50) NOT NULL, migration_time BIGINT NULL,
sql CLOB NULL, jooq_version VARCHAR(50) NOT NULL,
sql_count INT NOT NULL, sql CLOB NULL,
status VARCHAR(10) NOT NULL, sql_count INT NOT NULL,
status_message CLOB NULL, status VARCHAR(10) NOT NULL,
resolution VARCHAR(10) NULL, status_message CLOB NULL,
resolution_message CLOB NULL, resolution VARCHAR(10) NULL,
resolution_message CLOB NULL,
CONSTRAINT jooq_migr_hist_pk PRIMARY KEY (id), 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')),