diff --git a/jOOQ-migrations-maven/pom.xml b/jOOQ-migrations-maven/pom.xml
index d96a997ee0..83a8b0f3ed 100644
--- a/jOOQ-migrations-maven/pom.xml
+++ b/jOOQ-migrations-maven/pom.xml
@@ -90,6 +90,10 @@
org.jooq
jooq
+
+ jakarta.xml.bind
+ jakarta.xml.bind-api
+
org.apache.maven
diff --git a/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/HistoryMojo.java b/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/HistoryMojo.java
index da4a6ef6ac..fd6341e656 100644
--- a/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/HistoryMojo.java
+++ b/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/HistoryMojo.java
@@ -48,6 +48,7 @@ import org.jooq.Migration;
import org.jooq.Version;
import org.jooq.tools.StringUtils;
+import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Mojo;
/**
@@ -65,28 +66,30 @@ public class HistoryMojo extends AbstractMigrateMojo {
@Override
final void execute1(Migration migration) throws Exception {
- if (getLog().isInfoEnabled()) {
- for (HistoryVersion version : migration.dsl().migrations().history()) {
- getLog().info(string(version.migratedAt()) + " - Version: " + string(version.version()));
+ if (getLog().isInfoEnabled())
+ for (HistoryVersion version : migration.dsl().migrations().history())
+ log(getLog(), version);
+ }
- if (version.version().parents().size() > 1) {
- getLog().info(" Merged parents: ");
+ static final void log(Log log, HistoryVersion version) {
+ log.info(" " + string(version.migratedAt()) + " - Version: " + string(version.version()));
- for (Version p : version.version().parents())
- getLog().info(" - " + string(p));
- }
- }
+ if (version.version().parents().size() > 1) {
+ log.info(" Merged parents: ");
+
+ for (Version p : version.version().parents())
+ log.info(" - " + string(p));
}
}
- private final String string(Instant instant) {
+ private static final String string(Instant instant) {
if (instant == null)
return "0000-00-00T00:00:00.000Z";
else
return StringUtils.rightPad(instant.toString(), 24);
}
- private final String string(Version version) {
+ private static final String string(Version version) {
return version.id() + (!isEmpty(version.message()) ? " (" + version.message() + ")" : "");
}
}
diff --git a/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/LogMojo.java b/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/LogMojo.java
index 01c75d7d47..6b9972dd73 100644
--- a/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/LogMojo.java
+++ b/jOOQ-migrations-maven/src/main/java/org/jooq/migrations/maven/LogMojo.java
@@ -37,9 +37,16 @@
*/
package org.jooq.migrations.maven;
+import static java.util.stream.Collectors.toList;
import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_SOURCES;
import static org.apache.maven.plugins.annotations.ResolutionScope.TEST;
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+import org.jooq.History;
+import org.jooq.HistoryVersion;
import org.jooq.Migration;
import org.jooq.Query;
import org.jooq.tools.StringUtils;
@@ -63,7 +70,31 @@ public class LogMojo extends AbstractMigrateMojo {
@Override
final void execute1(Migration migration) throws Exception {
if (getLog().isInfoEnabled()) {
+ History history = migration.dsl().migrations().history();
+ List versions = StreamSupport
+ .stream(history.spliterator(), false)
+ .collect(toList());
+
+ if (versions.isEmpty()) {
+ getLog().info("No migration history available yet");
+ }
+ else {
+ getLog().info("Migration history");
+
+ for (HistoryVersion version : versions.subList(
+ Math.max(0, versions.size() - 5),
+ versions.size()
+ )) {
+ HistoryMojo.log(getLog(), version);
+ }
+ }
+
Query[] queries = migration.queries().queries();
+
+ getLog().info("Outstanding queries from " + migration.from() + " to " + migration.to() + ": "
+ + (queries.length == 0 ? "none" : "")
+ );
+
log(getLog(), queries);
}
}
diff --git a/jOOQ/src/main/java/org/jooq/FilePattern.java b/jOOQ/src/main/java/org/jooq/FilePattern.java
index 07bf140495..503df706e3 100644
--- a/jOOQ/src/main/java/org/jooq/FilePattern.java
+++ b/jOOQ/src/main/java/org/jooq/FilePattern.java
@@ -168,6 +168,10 @@ public final class FilePattern {
);
}
+ public final Comparator fileComparator() {
+ return fileComparator(sort());
+ }
+
public static final Comparator fileComparator(Sort sort) {
if (sort == null)
sort = SEMANTIC;
diff --git a/jOOQ/src/main/java/org/jooq/impl/CommitsImpl.java b/jOOQ/src/main/java/org/jooq/impl/CommitsImpl.java
index 5854d5d369..6ff8291df8 100644
--- a/jOOQ/src/main/java/org/jooq/impl/CommitsImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/CommitsImpl.java
@@ -39,6 +39,7 @@ package org.jooq.impl;
import static java.util.Arrays.asList;
import static java.util.Collections.unmodifiableCollection;
+import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.toList;
import static org.jooq.impl.Tools.filter;
import static org.jooq.impl.Tools.isEmpty;
@@ -48,6 +49,7 @@ import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -291,7 +293,10 @@ final class CommitsImpl implements Commits {
// [#9506] TODO: Turning a directory into a MigrationsType (and various other conversion)
// could be made reusable. This is certainly very useful for testing and interop,
// e.g. also to support other formats (Flyway, Liquibase) as source
- TreeMap idToCommit = new TreeMap<>();
+ TreeMap idToCommit = new TreeMap<>(comparing(
+ java.io.File::new,
+ pattern.fileComparator()
+ ));
List list = files.stream().map(s -> new FileData(pattern, s)).collect(toList());
@@ -303,6 +308,7 @@ final class CommitsImpl implements Commits {
.withId(f.id)
.withAuthor(f.author)
.withMessage(f.message)
+ .withTags(f.tags)
);
for (FileData f : list) {
@@ -334,6 +340,9 @@ final class CommitsImpl implements Commits {
);
}
+ if (log.isDebugEnabled())
+ log.debug("Loading files into: " + new MigrationsType().withCommits(idToCommit.values()));
+
return load(new MigrationsType().withCommits(idToCommit.values()));
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java b/jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java
index 9aaec0b582..a862451fc6 100644
--- a/jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/HistoryImpl.java
@@ -339,6 +339,9 @@ class HistoryImpl extends AbstractScope implements History {
@Override
public String toString() {
- return "History [" + current() + "]";
+ if (!isEmpty(versions))
+ return "History [" + current() + "]";
+ else
+ return "History []";
}
}
diff --git a/jOOQ/src/main/java/org/jooq/impl/Interpreter.java b/jOOQ/src/main/java/org/jooq/impl/Interpreter.java
index 1b87659670..2fa67e4b2f 100644
--- a/jOOQ/src/main/java/org/jooq/impl/Interpreter.java
+++ b/jOOQ/src/main/java/org/jooq/impl/Interpreter.java
@@ -1379,11 +1379,32 @@ final class Interpreter {
}
private static final DataDefinitionException notExists(Named named) {
- return notExists(named.getClass().getSimpleName(), named);
+ return notExists(typeName(named), named);
}
private static final DataDefinitionException alreadyExists(Named named) {
- return alreadyExists(named.getClass().getSimpleName(), named);
+ return alreadyExists(typeName(named), named);
+ }
+
+ private static final String typeName(Named named) {
+
+ // TODO: Move this to the Named interface
+ if (named instanceof Catalog)
+ return "Catalog";
+ else if (named instanceof Schema)
+ return "Schema";
+ else if (named instanceof Table)
+ return "Table";
+ else if (named instanceof Field)
+ return "Field";
+ else if (named instanceof Sequence)
+ return "Sequence";
+ else if (named instanceof Domain)
+ return "Domain";
+ else if (named instanceof Synonym)
+ return "Synonym";
+ else
+ return named.getClass().getSimpleName();
}
private static final DataDefinitionException notExists(String type, Named named) {
diff --git a/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java b/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java
index 8158c4e669..83703f9cdc 100644
--- a/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java
+++ b/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java
@@ -212,11 +212,37 @@ final class MigrationImpl extends AbstractScope implements Migration {
private final void revertUntracked(DefaultMigrationContext ctx, MigrationListener listener, HistoryRecord currentRecord) {
if (ctx.revertUntrackedQueries.queries().length > 0)
- if (!TRUE.equals(dsl().settings().isMigrationRevertUntracked()))
- throw new DataMigrationVerificationException(
- "Non-empty difference between actual schema and migration from schema: " + ctx.revertUntrackedQueries +
- (currentRecord == null ? ("\n\nUse Settings.migrationAutoBaseline to automatically set a baseline") : "")
- );
+ if (!TRUE.equals(dsl().settings().isMigrationRevertUntracked())) {
+ if (currentRecord == null) {
+ throw new DataMigrationVerificationException(
+ """
+ Non-empty difference between actual schema and migration from schema: {queries}.
+
+ Possible remedies:
+ - Use Settings.migrationAutoBaseline to automatically set a baseline.
+ """.replace("{queries}", "" + ctx.revertUntrackedQueries)
+ );
+ }
+ else {
+ throw new DataMigrationVerificationException(
+ """
+ Non-empty difference between actual schema and migration from schema: {queries}.
+
+ This can happen for two reasons:
+ 1) The migration specification of a version that has already been installed has been modified.
+ 2) The database schemas contain untracked objects.
+
+ Possible remedies if 1):
+ - Revert changes to the migration specification and move those changes to a new version.
+
+ Possible remedies if 2):
+ - Use Settings.migrationRevertUntracked to automatically drop unknown objects (at your own risk!)
+ - Manually drop or move unknown objects outside of managed schemas.
+ - Update migration scripts to track missing objects.
+ """.replace("{queries}", "" + ctx.revertUntrackedQueries)
+ );
+ }
+ }
else if (listener != null)
execute(ctx, listener, ctx.revertUntrackedQueries);
}
diff --git a/jOOQ/src/main/java/org/jooq/migrations/xml/jaxb/CommitType.java b/jOOQ/src/main/java/org/jooq/migrations/xml/jaxb/CommitType.java
index 6f1a1cd2a1..568091690b 100644
--- a/jOOQ/src/main/java/org/jooq/migrations/xml/jaxb/CommitType.java
+++ b/jOOQ/src/main/java/org/jooq/migrations/xml/jaxb/CommitType.java
@@ -25,7 +25,7 @@ import org.jooq.util.jaxb.tools.XMLBuilder;
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <all>
* <element name="parents" type="{http://www.jooq.org/xsd/jooq-migrations-3.20.0.xsd}ParentsType" minOccurs="0"/>
- * <element name="id" type="{http://www.w3.org/2001/XMLSchema}string"/>
+ * <element name="id" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="message" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="author" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
* <element name="tags" type="{http://www.jooq.org/xsd/jooq-migrations-3.20.0.xsd}TagsType" minOccurs="0"/>
@@ -49,7 +49,6 @@ public class CommitType implements Serializable, XMLAppendable
{
private final static long serialVersionUID = 32000L;
- @XmlElement(required = true)
protected String id;
protected String message;
protected String author;
diff --git a/jOOQ/src/main/resources/org/jooq/xsd/jooq-migrations-3.20.0.xsd b/jOOQ/src/main/resources/org/jooq/xsd/jooq-migrations-3.20.0.xsd
index 2539203149..e13d7c38d2 100644
--- a/jOOQ/src/main/resources/org/jooq/xsd/jooq-migrations-3.20.0.xsd
+++ b/jOOQ/src/main/resources/org/jooq/xsd/jooq-migrations-3.20.0.xsd
@@ -22,7 +22,7 @@
-
+