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 @@ - +