[jOOQ/jOOQ#9506] Various improvements

This includes:

- Commits::add should return this
- Add Settings.migrationHistorySchema
- Add some DEBUG logging
- Add a jOOQ-migrations-maven module draft
- Cache AbstractNode.root to avoid excessive recursive root() lookups
- [jOOQ/jOOQ#15201] Rename changelog to history
- [jOOQ/jOOQ#15201] Removal of Commits.commit(String)
This commit is contained in:
Lukas Eder 2023-06-14 15:52:43 +02:00
parent 4d5147ec52
commit 25f023ebe5
24 changed files with 927 additions and 314 deletions

3
jOOQ-migrations-maven/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
/target
/.idea
/*.iml

View File

@ -0,0 +1,19 @@
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

View File

@ -0,0 +1,10 @@
Third party NOTICE.txt contents
===============================
Contents of https://github.com/apache/commons-lang/blob/master/NOTICE.txt
-------------------------------------------------------------------------
Apache Commons Lang
Copyright 2001-2019 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

View File

@ -0,0 +1,109 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.jooq</groupId>
<artifactId>jooq-parent</artifactId>
<version>3.19.0-SNAPSHOT</version>
</parent>
<artifactId>jooq-migrations-maven</artifactId>
<name>jOOQ Migrations Maven</name>
<packaging>maven-plugin</packaging>
<licenses>
<license>
<name>Apache License, Version 2.0</name>
<url>http://www.jooq.org/inc/LICENSE.txt</url>
<distribution>repo</distribution>
</license>
</licenses>
<build>
<plugins>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<configuration>
<!-- [#12434] These two dependencies are outdated transitive dependencies from
maven-core:3.8.2, which we can't update easily without risking to break
maven stuff -->
<excludes>
<exclude>commons-io:commons-io:jar:2.6</exclude>
<exclude>com.google.guava:guava:jar:25.1-android</exclude>
</excludes>
</configuration>
</plugin>
<!-- More details about this plugin can be found here:PatternExcludesArtifactFilter
http://maven.apache.org/plugin-tools/maven-plugin-plugin/examples/using-annotations.html
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<executions>
<execution>
<id>mojo-descriptor</id>
<goals>
<goal>descriptor</goal>
</goals>
</execution>
<!-- if you want to generate help goal -->
<execution>
<id>help-goal</id>
<goals>
<goal>helpmojo</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>org.jooq.migrations.maven</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-core</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,219 @@
/*
* 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.jooq.tools.StringUtils.defaultIfNull;
import static org.jooq.tools.StringUtils.isBlank;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
import org.jooq.CloseableDSLContext;
import org.jooq.Configuration;
import org.jooq.conf.MigrationSchema;
import org.jooq.impl.DSL;
import org.jooq.tools.StringUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
/**
* A base class for migration mojos.
*
* @author Lukas Eder
*/
abstract class AbstractMigrationsMojo extends AbstractMojo {
/**
* The Maven project.
*/
@Parameter(
property = "project",
required = true,
readonly = true
)
MavenProject project;
/**
* Whether to skip the execution of the Maven Plugin for this module.
*/
@Parameter(property = "jooq.migrate.skip")
boolean skip;
/**
* The JDBC URL to connect to.
*/
@Parameter(property = "jooq.migrate.jdbc")
Jdbc jdbc;
/**
* The migration script directory.
*/
@Parameter(property = "jooq.migrate.directory")
String directory;
/**
* The script to run before the migration.
*/
@Parameter(property = "jooq.migrate.setupScript")
String setupScript;
/**
* The script to run after the migration.
*/
@Parameter(property = "jooq.migrate.cleanupScript")
String cleanupScript;
/**
* The schemata that are migrated.
*/
@Parameter(property = "jooq.migrate.schemata")
List<MigrationSchema> schemata;
/**
* The default catalog among the migrated schemata.
*/
@Parameter(property = "jooq.migrate.defaultCatalog")
String defaultCatalog;
/**
* The default schema among the migrated schemata.
*/
@Parameter(property = "jooq.migrate.defaultSchema")
String defaultSchema;
/**
* The catalog where the history tables are located.
*/
@Parameter(property = "jooq.migrate.historyCatalog")
String historyCatalog;
/**
* The schema where the history tables are located.
*/
@Parameter(property = "jooq.migrate.historySchema")
String historySchema;
@Override
public void execute() throws MojoExecutionException {
if (skip) {
getLog().info("Skipping jOOQ migrations");
return;
}
ClassLoader oldCL = Thread.currentThread().getContextClassLoader();
URLClassLoader pluginClassLoader = getClassLoader();
try {
// [#2886] Add the surrounding project's dependencies to the current classloader
Thread.currentThread().setContextClassLoader(pluginClassLoader);
if (jdbc == null || jdbc.url == null)
throw new MojoExecutionException("JDBC URL is required");
try (CloseableDSLContext ctx = DSL.using(jdbc.url, defaultIfNull(jdbc.user, jdbc.username), jdbc.password)) {
// Initialise Settings
List<MigrationSchema> s = ctx.settings().getMigrationSchemata();
s.addAll(schemata);
if (defaultCatalog != null || defaultSchema != null)
s.add(new MigrationSchema().withCatalog(defaultIfNull(defaultCatalog, "")).withSchema(defaultIfNull(defaultSchema, "")));
if (historyCatalog != null || historySchema != null)
ctx.settings().setMigrationHistorySchema(new MigrationSchema().withCatalog(defaultIfNull(historyCatalog, "")).withSchema(defaultIfNull(historySchema, "")));
// Initialise connection
if (!isBlank(defaultCatalog))
ctx.setCatalog(defaultCatalog).execute();
if (!isBlank(defaultSchema))
ctx.setSchema(defaultSchema).execute();
// Run migration
if (setupScript != null)
ctx.execute(setupScript);
execute0(ctx.configuration());
if (cleanupScript != null)
ctx.execute(cleanupScript);
}
}
catch (Exception ex) {
throw new MojoExecutionException("Error running jOOQ code generation tool", ex);
}
finally {
// [#2886] Restore old class loader
Thread.currentThread().setContextClassLoader(oldCL);
// [#7630] Close URLClassLoader to help free resources
try {
pluginClassLoader.close();
}
// Catch all possible errors to avoid suppressing the original exception
catch (Throwable e) {
getLog().error("Couldn't close the classloader.", e);
}
}
}
abstract void execute0(Configuration configuration) throws Exception;
private URLClassLoader getClassLoader() throws MojoExecutionException {
try {
List<String> classpathElements = project.getRuntimeClasspathElements();
URL urls[] = new URL[classpathElements.size()];
for (int i = 0; i < urls.length; i++)
urls[i] = new File(classpathElements.get(i)).toURI().toURL();
return new URLClassLoader(urls, getClass().getClassLoader());
}
catch (Exception e) {
throw new MojoExecutionException("Couldn't create a classloader.", e);
}
}
}

View File

@ -0,0 +1,46 @@
/*
* 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;
public class Jdbc {
public String url;
public String user;
public String username;
public String password;
}

View File

@ -0,0 +1,98 @@
/*
* 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 java.io.File;
import org.jooq.Commits;
import org.jooq.Configuration;
import org.jooq.Migration;
import org.jooq.Migrations;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.Mojo;
/**
* The jOOQ Migrations migrate mojo
*
* @author Lukas Eder
*/
@Mojo(
name = "migrate",
defaultPhase = GENERATE_SOURCES,
requiresDependencyResolution = TEST,
threadSafe = true
)
public class MigrateMojo extends AbstractMigrationsMojo {
@Override
public void execute0(Configuration configuration) throws Exception {
if (directory == null)
throw new MojoExecutionException("Directory was not provided");
Migrations migrations = configuration.dsl().migrations();
Commits commits = migrations.commits();
// [#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));
Migration migration = migrations.migrateTo(commits.latest());
if (getLog().isInfoEnabled()) {
getLog().info("Migration from version: " + migration.from());
getLog().info("Migration to version : " + migration.to());
getLog().info("Migration queries : " + migration.queries().queries().length);
}
migration.execute();
}
private File file(String file) {
getLog().info("Reading migrations directory: " + file);
File f = new File(file);
if (!f.isAbsolute())
f = new File(project.getBasedir(), file);
return f;
}
}

View File

@ -0,0 +1,19 @@
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

View File

@ -0,0 +1,2 @@
Thanks for downloading jOOQ.
Please visit http://www.jooq.org for more information.

View File

@ -102,22 +102,29 @@ public interface Commits extends Iterable<Commit> {
/**
* Add a commit to the graph.
*
* @return The same instance.
*/
void add(Commit commit);
Commits add(Commit commit);
/**
* Add all commits to the graph.
*
* @return The same instance.
*/
void addAll(Commit... commits);
Commits addAll(Commit... commits);
/**
* Add all commits to the graph.
*
* @return The same instance.
*/
void addAll(Collection<? extends Commit> commits);
Commits addAll(Collection<? extends Commit> commits);
/**
* Load directory content into this commits graph.
*
* @return The same instance.
* @throws IOException If anything goes wrong reading the directory
* contents.
* @throws DataMigrationValidationException If the migration doesn't
@ -129,6 +136,7 @@ public interface Commits extends Iterable<Commit> {
/**
* Load XML content into this commits graph.
*
* @return The same instance.
* @throws DataMigrationValidationException If the migration doesn't
* validate.
*/

View File

@ -80,16 +80,6 @@ public interface Migrations {
@NotNull
Versions versions();
/**
* Initialise a {@link Version}.
* <p>
* This is EXPERIMENTAL functionality and subject to change in future jOOQ
* versions.
*/
@Experimental
@NotNull
Commit commit(String id);
/**
* Initialise an empty {@link Commits} graph.
* <p>

View File

@ -451,6 +451,7 @@ public class Settings
protected Boolean metaIncludeSystemIndexes = false;
@XmlElement(defaultValue = "false")
protected Boolean metaIncludeSystemSequences = false;
protected MigrationSchema migrationHistorySchema;
@XmlElement(defaultValue = "false")
protected Boolean migrationAllowsUndo = false;
@XmlElement(defaultValue = "false")
@ -5268,6 +5269,22 @@ public class Settings
this.metaIncludeSystemSequences = value;
}
/**
* The database schema where the migration history is located.
*
*/
public MigrationSchema getMigrationHistorySchema() {
return migrationHistorySchema;
}
/**
* The database schema where the migration history is located.
*
*/
public void setMigrationHistorySchema(MigrationSchema value) {
this.migrationHistorySchema = value;
}
/**
* Whether migrations are allowed to be executed in inverse order.<p><strong>This is a potentially destructive feature, which should not be turned on in production</strong>. It is useful mostly to quickly switch between branches in a development environment. This feature is available only in commercial distributions.
*
@ -7151,6 +7168,15 @@ public class Settings
return this;
}
/**
* The database schema where the migration history is located.
*
*/
public Settings withMigrationHistorySchema(MigrationSchema value) {
setMigrationHistorySchema(value);
return this;
}
public Settings withMigrationAllowsUndo(Boolean value) {
setMigrationAllowsUndo(value);
return this;
@ -7624,6 +7650,7 @@ public class Settings
builder.append("interpreterDelayForeignKeyDeclarations", interpreterDelayForeignKeyDeclarations);
builder.append("metaIncludeSystemIndexes", metaIncludeSystemIndexes);
builder.append("metaIncludeSystemSequences", metaIncludeSystemSequences);
builder.append("migrationHistorySchema", migrationHistorySchema);
builder.append("migrationAllowsUndo", migrationAllowsUndo);
builder.append("migrationRevertUntracked", migrationRevertUntracked);
builder.append("migrationAutoBaseline", migrationAutoBaseline);
@ -9341,6 +9368,15 @@ public class Settings
return false;
}
}
if (migrationHistorySchema == null) {
if (other.migrationHistorySchema!= null) {
return false;
}
} else {
if (!migrationHistorySchema.equals(other.migrationHistorySchema)) {
return false;
}
}
if (migrationAllowsUndo == null) {
if (other.migrationAllowsUndo!= null) {
return false;
@ -9812,6 +9848,7 @@ public class Settings
result = ((prime*result)+((interpreterDelayForeignKeyDeclarations == null)? 0 :interpreterDelayForeignKeyDeclarations.hashCode()));
result = ((prime*result)+((metaIncludeSystemIndexes == null)? 0 :metaIncludeSystemIndexes.hashCode()));
result = ((prime*result)+((metaIncludeSystemSequences == null)? 0 :metaIncludeSystemSequences.hashCode()));
result = ((prime*result)+((migrationHistorySchema == null)? 0 :migrationHistorySchema.hashCode()));
result = ((prime*result)+((migrationAllowsUndo == null)? 0 :migrationAllowsUndo.hashCode()));
result = ((prime*result)+((migrationRevertUntracked == null)? 0 :migrationRevertUntracked.hashCode()));
result = ((prime*result)+((migrationAutoBaseline == null)? 0 :migrationAutoBaseline.hashCode()));

View File

@ -51,10 +51,13 @@ import org.jooq.exception.DataDefinitionException;
*/
abstract class AbstractNode<N extends Node<N>> implements Node<N> {
private final String id;
private final String message;
final N root;
final String id;
final String message;
AbstractNode(String id, String message) {
@SuppressWarnings("unchecked")
AbstractNode(String id, String message, N root) {
this.root = root != null ? root : (N) this;
this.id = id;
this.message = defaultIfNull(message, "");
}
@ -70,14 +73,8 @@ abstract class AbstractNode<N extends Node<N>> implements Node<N> {
}
@Override
@SuppressWarnings("unchecked")
public final N root() {
N node = (N) this;
while (!node.parents().isEmpty())
node = node.parents().get(0);
return node;
return root;
}
@SuppressWarnings("unchecked")

View File

@ -1,157 +0,0 @@
/*
* This file is generated by jOOQ.
*/
package org.jooq.impl;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
import org.jooq.Check;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Identity;
import org.jooq.Name;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableOptions;
import org.jooq.UniqueKey;
import org.jooq.impl.MigrationImpl.Status;
/**
* The migration log of jOOQ Migrations.
*/
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
class Changelog extends TableImpl<ChangelogRecord> {
private static final long serialVersionUID = 1L;
/**
* The reference instance of <code>JOOQ_MIGRATIONS_CHANGELOG</code>
*/
static final Changelog CHANGELOG = new Changelog();
/**
* The class holding records for this type
*/
@Override
public Class<ChangelogRecord> getRecordType() {
return ChangelogRecord.class;
}
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.ID</code>. The database
* version ID.
*/
final TableField<ChangelogRecord, Integer> ID = createField(DSL.name("ID"), SQLDataType.INTEGER.nullable(false).identity(true), this, "The database version ID.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM</code>. The
* previous database version ID.
*/
final TableField<ChangelogRecord, String> MIGRATED_FROM = createField(DSL.name("MIGRATED_FROM"), SQLDataType.VARCHAR(255).nullable(false), this, "The previous database version ID.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO</code>.
*/
final TableField<ChangelogRecord, String> MIGRATED_TO = createField(DSL.name("MIGRATED_TO"), SQLDataType.VARCHAR(255).nullable(false), this, "");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT</code>. The
* date/time when the database version was migrated to.
*/
final TableField<ChangelogRecord, Timestamp> MIGRATED_AT = createField(DSL.name("MIGRATED_AT"), SQLDataType.TIMESTAMP(6).nullable(false), this, "The date/time when the database version was migrated to.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME</code>. The
* time in milliseconds it took to migrate to this database version.
*/
final TableField<ChangelogRecord, Long> MIGRATION_TIME = createField(DSL.name("MIGRATION_TIME"), SQLDataType.BIGINT, this, "The time in milliseconds it took to migrate to this database version.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION</code>. The jOOQ
* version used to migrate to this database version.
*/
final TableField<ChangelogRecord, String> JOOQ_VERSION = createField(DSL.name("JOOQ_VERSION"), SQLDataType.VARCHAR(50).nullable(false), this, "The jOOQ version used to migrate to this database version.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.SQL</code>. The SQL statements
* that were run to install this database version.
*/
final TableField<ChangelogRecord, String> SQL = createField(DSL.name("SQL"), SQLDataType.CLOB, this, "The SQL statements that were run to install this database version.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.SQL_COUNT</code>. The number
* of SQL statements that were run to install this database version.
*/
final TableField<ChangelogRecord, Integer> SQL_COUNT = createField(DSL.name("SQL_COUNT"), SQLDataType.INTEGER.nullable(false), this, "The number of SQL statements that were run to install this database version.");
/**
* The column <code>JOOQ_MIGRATIONS_CHANGELOG.STATUS</code>. The database
* version installation status.
*/
final TableField<ChangelogRecord, 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));
private Changelog(Name alias, Table<ChangelogRecord> aliased) {
this(alias, aliased, (Field<?>[]) null, null);
}
private Changelog(Name alias, Table<ChangelogRecord> aliased, Field<?>[] parameters, Condition where) {
super(alias, null, aliased, parameters, DSL.comment("The migration log of jOOQ Migrations."), TableOptions.table(), where);
}
/**
* Create an aliased <code>JOOQ_MIGRATIONS_CHANGELOG</code> table reference
*/
Changelog(String alias) {
this(DSL.name(alias), CHANGELOG);
}
/**
* Create an aliased <code>JOOQ_MIGRATIONS_CHANGELOG</code> table reference
*/
Changelog(Name alias) {
this(alias, CHANGELOG);
}
/**
* Create a <code>JOOQ_MIGRATIONS_CHANGELOG</code> table reference
*/
Changelog() {
this(DSL.name("JOOQ_MIGRATIONS_CHANGELOG"), null);
}
@Override
public Identity<ChangelogRecord, Integer> getIdentity() {
return (Identity<ChangelogRecord, Integer>) super.getIdentity();
}
@Override
public UniqueKey<ChangelogRecord> getPrimaryKey() {
return Internal.createUniqueKey(Changelog.CHANGELOG, DSL.name("JOOQ_MIGR_PK"), new TableField[] { Changelog.CHANGELOG.ID }, true);
}
@Override
public List<Check<ChangelogRecord>> getChecks() {
return Arrays.asList(
Internal.createCheck(this, DSL.name("JOOQ_MIGR_CHK1"), "\"STATUS\" IN('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE')", true)
);
}
@Override
public Changelog as(String alias) {
return new Changelog(DSL.name(alias), this);
}
@Override
public Changelog as(Name alias) {
return new Changelog(alias, this);
}
@Override
public Changelog as(Table<?> alias) {
return new Changelog(alias.getQualifiedName(), this);
}
}

View File

@ -75,15 +75,15 @@ import org.jooq.tools.StringUtils;
*/
final class CommitImpl extends AbstractNode<Commit> implements Commit {
private final Configuration configuration;
private final DSLContext ctx;
private final List<Commit> parents;
private final List<Tag> tags;
private final Map<String, File> delta;
private final Map<String, File> files;
final Configuration configuration;
final DSLContext ctx;
final List<Commit> parents;
final List<Tag> tags;
final Map<String, File> delta;
final Map<String, File> files;
CommitImpl(Configuration configuration, String id, String message, List<Commit> parents, Collection<? extends File> delta) {
super(id, message);
CommitImpl(Configuration configuration, String id, String message, Commit root, List<Commit> parents, Collection<? extends File> delta) {
super(id, message, root);
this.configuration = configuration;
this.ctx = configuration.dsl();
@ -94,7 +94,7 @@ final class CommitImpl extends AbstractNode<Commit> implements Commit {
}
private CommitImpl(CommitImpl copy) {
super(copy.id(), copy.message());
super(copy.id(), copy.message(), copy.root);
this.configuration = copy.configuration;
this.ctx = copy.ctx;
@ -192,7 +192,7 @@ final class CommitImpl extends AbstractNode<Commit> implements Commit {
@Override
public final Commit commit(String newId, String newMessage, Collection<? extends File> newFiles) {
return new CommitImpl(configuration, newId, newMessage, Arrays.asList(this), newFiles);
return new CommitImpl(configuration, newId, newMessage, root, Arrays.asList(this), newFiles);
}
@Override
@ -212,7 +212,7 @@ final class CommitImpl extends AbstractNode<Commit> implements Commit {
@Override
public final Commit merge(String newId, String newMessage, Commit with, Collection<? extends File> newFiles) {
return new CommitImpl(configuration, newId, newMessage, Arrays.asList(this, with), newFiles);
return new CommitImpl(configuration, newId, newMessage, root, Arrays.asList(this, with), newFiles);
}
@Override

View File

@ -69,17 +69,19 @@ import org.jooq.migrations.xml.jaxb.FileType;
import org.jooq.migrations.xml.jaxb.MigrationsType;
import org.jooq.migrations.xml.jaxb.ParentType;
import org.jooq.migrations.xml.jaxb.TagType;
import org.jooq.tools.JooqLogger;
/**
* @author Lukas Eder
*/
final class CommitsImpl implements Commits {
final Configuration configuration;
final Migrations migrations;
final Commit root;
final Map<String, Commit> commitsById;
final Map<String, Commit> commitsByTag;
private static final JooqLogger log = JooqLogger.getLogger(CommitsImpl.class);
final Configuration configuration;
final Migrations migrations;
final Commit root;
final Map<String, Commit> commitsById;
final Map<String, Commit> commitsByTag;
CommitsImpl(Configuration configuration, Commit root) {
this.configuration = configuration;
@ -92,7 +94,7 @@ final class CommitsImpl implements Commits {
}
@Override
public void add(Commit commit) {
public final Commits add(Commit commit) {
if (root != commit.root())
throw new DataMigrationValidationException("A Commits graph must contain a single graph whose commits all share the same root.");
@ -109,17 +111,24 @@ final class CommitsImpl implements Commits {
for (Tag tag : commit.tags())
commitsByTag.put(tag.id(), commit);
if (log.isDebugEnabled())
log.debug("Commit added", commit);
return this;
}
@Override
public void addAll(Commit... c) {
addAll(asList(c));
public final Commits addAll(Commit... c) {
return addAll(asList(c));
}
@Override
public void addAll(Collection<? extends Commit> c) {
public final Commits addAll(Collection<? extends Commit> c) {
for (Commit commit : c)
add(commit);
return this;
}
@Override
@ -206,9 +215,10 @@ final class CommitsImpl implements Commits {
java.io.File[] files = directory.listFiles(f -> f.getName().endsWith(".sql"));
if (files != null) {
List<FileData> list = Stream.of(files).map(x -> {
return new FileData(x);
}).collect(toList());
List<FileData> list = Stream.of(files).map(FileData::new).collect(toList());
if (log.isDebugEnabled())
list.forEach(f -> log.debug("Reading file", f.basename));
/*
* An example:

View File

@ -0,0 +1,157 @@
/*
* This file is generated by jOOQ.
*/
package org.jooq.impl;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.List;
import org.jooq.Check;
import org.jooq.Condition;
import org.jooq.Field;
import org.jooq.Identity;
import org.jooq.Name;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableOptions;
import org.jooq.UniqueKey;
import org.jooq.impl.MigrationImpl.Status;
/**
* The migration history of jOOQ Migrations.
*/
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
class History extends TableImpl<HistoryRecord> {
private static final long serialVersionUID = 1L;
/**
* The reference instance of <code>JOOQ_MIGRATION_HISTORY</code>
*/
static final History HISTORY = new History();
/**
* The class holding records for this type
*/
@Override
public Class<HistoryRecord> getRecordType() {
return HistoryRecord.class;
}
/**
* The column <code>JOOQ_MIGRATION_HISTORY.ID</code>. The database version
* ID.
*/
final TableField<HistoryRecord, Integer> ID = createField(DSL.name("ID"), SQLDataType.INTEGER.nullable(false).identity(true), this, "The database version ID.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_FROM</code>. The
* previous database version ID.
*/
final TableField<HistoryRecord, String> MIGRATED_FROM = createField(DSL.name("MIGRATED_FROM"), SQLDataType.VARCHAR(255).nullable(false), this, "The previous database version ID.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO</code>.
*/
final TableField<HistoryRecord, String> MIGRATED_TO = createField(DSL.name("MIGRATED_TO"), SQLDataType.VARCHAR(255).nullable(false), this, "");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATED_AT</code>. The date/time
* when the database version was migrated to.
*/
final TableField<HistoryRecord, Timestamp> MIGRATED_AT = createField(DSL.name("MIGRATED_AT"), SQLDataType.TIMESTAMP(6).nullable(false), this, "The date/time when the database version was migrated to.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.MIGRATION_TIME</code>. The time
* in milliseconds it took to migrate to this database version.
*/
final TableField<HistoryRecord, Long> MIGRATION_TIME = createField(DSL.name("MIGRATION_TIME"), SQLDataType.BIGINT, this, "The time in milliseconds it took to migrate to this database version.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.JOOQ_VERSION</code>. The jOOQ
* version used to migrate to this database version.
*/
final TableField<HistoryRecord, String> JOOQ_VERSION = createField(DSL.name("JOOQ_VERSION"), SQLDataType.VARCHAR(50).nullable(false), this, "The jOOQ version used to migrate to this database version.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.SQL</code>. The SQL statements
* that were run to install this database version.
*/
final TableField<HistoryRecord, String> SQL = createField(DSL.name("SQL"), SQLDataType.CLOB, this, "The SQL statements that were run to install this database version.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.SQL_COUNT</code>. The number of
* SQL statements that were run to install this database version.
*/
final TableField<HistoryRecord, Integer> SQL_COUNT = createField(DSL.name("SQL_COUNT"), SQLDataType.INTEGER.nullable(false), this, "The number of SQL statements that were run to install this database version.");
/**
* The column <code>JOOQ_MIGRATION_HISTORY.STATUS</code>. The database
* version installation status.
*/
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));
private History(Name alias, Table<HistoryRecord> aliased) {
this(alias, aliased, (Field<?>[]) null, null);
}
private History(Name alias, Table<HistoryRecord> aliased, Field<?>[] parameters, Condition where) {
super(alias, null, aliased, parameters, DSL.comment("The migration history of jOOQ Migrations."), TableOptions.table(), where);
}
/**
* Create an aliased <code>JOOQ_MIGRATION_HISTORY</code> table reference
*/
History(String alias) {
this(DSL.name(alias), HISTORY);
}
/**
* Create an aliased <code>JOOQ_MIGRATION_HISTORY</code> table reference
*/
History(Name alias) {
this(alias, HISTORY);
}
/**
* Create a <code>JOOQ_MIGRATION_HISTORY</code> table reference
*/
History() {
this(DSL.name("JOOQ_MIGRATION_HISTORY"), null);
}
@Override
public Identity<HistoryRecord, Integer> getIdentity() {
return (Identity<HistoryRecord, Integer>) super.getIdentity();
}
@Override
public UniqueKey<HistoryRecord> getPrimaryKey() {
return Internal.createUniqueKey(History.HISTORY, DSL.name("JOOQ_MIGR_HIST_PK"), new TableField[] { History.HISTORY.ID }, true);
}
@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)
);
}
@Override
public History as(String alias) {
return new History(DSL.name(alias), this);
}
@Override
public History as(Name alias) {
return new History(alias, this);
}
@Override
public History as(Table<?> alias) {
return new History(alias.getQualifiedName(), this);
}
}

View File

@ -11,41 +11,41 @@ import org.jooq.impl.MigrationImpl.Status;
/**
* The migration log of jOOQ Migrations.
* The migration history of jOOQ Migrations.
*/
@SuppressWarnings({ "all", "unchecked", "rawtypes" })
class ChangelogRecord extends UpdatableRecordImpl<ChangelogRecord> {
class HistoryRecord extends UpdatableRecordImpl<HistoryRecord> {
private static final long serialVersionUID = 1L;
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.ID</code>. The database
* version ID.
* Setter for <code>JOOQ_MIGRATION_HISTORY.ID</code>. The database version
* ID.
*/
ChangelogRecord setId(Integer value) {
HistoryRecord setId(Integer value) {
set(0, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.ID</code>. The database
* version ID.
* Getter for <code>JOOQ_MIGRATION_HISTORY.ID</code>. The database version
* ID.
*/
Integer getId() {
return (Integer) get(0);
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM</code>. The
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_FROM</code>. The
* previous database version ID.
*/
ChangelogRecord setMigratedFrom(String value) {
HistoryRecord setMigratedFrom(String value) {
set(1, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM</code>. The
* Getter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_FROM</code>. The
* previous database version ID.
*/
String getMigratedFrom() {
@ -53,65 +53,65 @@ class ChangelogRecord extends UpdatableRecordImpl<ChangelogRecord> {
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO</code>.
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO</code>.
*/
ChangelogRecord setMigratedTo(String value) {
HistoryRecord setMigratedTo(String value) {
set(2, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO</code>.
* Getter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_TO</code>.
*/
String getMigratedTo() {
return (String) get(2);
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT</code>. The
* date/time when the database version was migrated to.
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_AT</code>. The date/time
* when the database version was migrated to.
*/
ChangelogRecord setMigratedAt(Timestamp value) {
HistoryRecord setMigratedAt(Timestamp value) {
set(3, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT</code>. The
* date/time when the database version was migrated to.
* Getter for <code>JOOQ_MIGRATION_HISTORY.MIGRATED_AT</code>. The date/time
* when the database version was migrated to.
*/
Timestamp getMigratedAt() {
return (Timestamp) get(3);
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME</code>. The
* time in milliseconds it took to migrate to this database version.
* Setter for <code>JOOQ_MIGRATION_HISTORY.MIGRATION_TIME</code>. The time
* in milliseconds it took to migrate to this database version.
*/
ChangelogRecord setMigrationTime(Long value) {
HistoryRecord setMigrationTime(Long value) {
set(4, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME</code>. The
* time in milliseconds it took to migrate to this database version.
* Getter for <code>JOOQ_MIGRATION_HISTORY.MIGRATION_TIME</code>. The time
* in milliseconds it took to migrate to this database version.
*/
Long getMigrationTime() {
return (Long) get(4);
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION</code>. The jOOQ
* Setter for <code>JOOQ_MIGRATION_HISTORY.JOOQ_VERSION</code>. The jOOQ
* version used to migrate to this database version.
*/
ChangelogRecord setJooqVersion(String value) {
HistoryRecord setJooqVersion(String value) {
set(5, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION</code>. The jOOQ
* Getter for <code>JOOQ_MIGRATION_HISTORY.JOOQ_VERSION</code>. The jOOQ
* version used to migrate to this database version.
*/
String getJooqVersion() {
@ -119,16 +119,16 @@ class ChangelogRecord extends UpdatableRecordImpl<ChangelogRecord> {
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.SQL</code>. The SQL statements
* Setter for <code>JOOQ_MIGRATION_HISTORY.SQL</code>. The SQL statements
* that were run to install this database version.
*/
ChangelogRecord setSql(String value) {
HistoryRecord setSql(String value) {
set(6, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.SQL</code>. The SQL statements
* Getter for <code>JOOQ_MIGRATION_HISTORY.SQL</code>. The SQL statements
* that were run to install this database version.
*/
String getSql() {
@ -136,33 +136,33 @@ class ChangelogRecord extends UpdatableRecordImpl<ChangelogRecord> {
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.SQL_COUNT</code>. The number
* of SQL statements that were run to install this database version.
* Setter for <code>JOOQ_MIGRATION_HISTORY.SQL_COUNT</code>. The number of
* SQL statements that were run to install this database version.
*/
ChangelogRecord setSqlCount(Integer value) {
HistoryRecord setSqlCount(Integer value) {
set(7, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.SQL_COUNT</code>. The number
* of SQL statements that were run to install this database version.
* Getter for <code>JOOQ_MIGRATION_HISTORY.SQL_COUNT</code>. The number of
* SQL statements that were run to install this database version.
*/
Integer getSqlCount() {
return (Integer) get(7);
}
/**
* Setter for <code>JOOQ_MIGRATIONS_CHANGELOG.STATUS</code>. The database
* Setter for <code>JOOQ_MIGRATION_HISTORY.STATUS</code>. The database
* version installation status.
*/
ChangelogRecord setStatus(Status value) {
HistoryRecord setStatus(Status value) {
set(8, value);
return this;
}
/**
* Getter for <code>JOOQ_MIGRATIONS_CHANGELOG.STATUS</code>. The database
* Getter for <code>JOOQ_MIGRATION_HISTORY.STATUS</code>. The database
* version installation status.
*/
Status getStatus() {
@ -183,17 +183,17 @@ class ChangelogRecord extends UpdatableRecordImpl<ChangelogRecord> {
// -------------------------------------------------------------------------
/**
* Create a detached ChangelogRecord
* Create a detached HistoryRecord
*/
ChangelogRecord() {
super(Changelog.CHANGELOG);
HistoryRecord() {
super(History.HISTORY);
}
/**
* Create a detached, initialised ChangelogRecord
* Create a detached, initialised HistoryRecord
*/
ChangelogRecord(Integer id, String migratedFrom, String migratedTo, Timestamp migratedAt, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, Status status) {
super(Changelog.CHANGELOG);
HistoryRecord(Integer id, String migratedFrom, String migratedTo, Timestamp migratedAt, Long migrationTime, String jooqVersion, String sql, Integer sqlCount, Status status) {
super(History.HISTORY);
setId(id);
setMigratedFrom(migratedFrom);

View File

@ -40,18 +40,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.Changelog.CHANGELOG;
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.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;
@ -65,6 +66,7 @@ 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;
@ -73,7 +75,10 @@ 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.DataMigrationValidationException;
@ -85,16 +90,51 @@ import org.jooq.tools.StopWatch;
*/
final class MigrationImpl extends AbstractScope implements Migration {
private static final JooqLogger log = JooqLogger.getLogger(Migration.class);
private final Commit to;
private Commit from;
private Queries queries;
private Commits commits;
static final JooqLogger log = JooqLogger.getLogger(Migration.class);
final DSLContext historyCtx;
final Commit to;
Commit from;
Queries queries;
Commits commits;
MigrationImpl(Configuration configuration, Commit to) {
super(configuration.derive(new ThreadLocalTransactionProvider(configuration.systemConnectionProvider())));
this.to = to;
this.historyCtx = initHistoryCtx(configuration());
}
private static final DSLContext initHistoryCtx(Configuration configuration) {
MigrationSchema m = configuration.settings().getMigrationHistorySchema();
if (m != null) {
DSLContext result = configuration.derive().dsl();
if (!isBlank(m.getCatalog())) {
result.settings().withRenderMapping(new RenderMapping()
.withCatalogs(new MappedCatalog()
.withInput("")
.withOutput(m.getCatalog())
.withSchemata(new MappedSchema()
.withInput("")
.withOutput(m.getSchema())
)
)
);
}
else if (!isBlank(m.getSchema())) {
result.settings().withRenderMapping(new RenderMapping()
.withSchemata(new MappedSchema()
.withInput("")
.withOutput(m.getSchema())
)
);
}
return result;
}
else
return configuration.dsl();
}
@Override
@ -135,7 +175,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
}
private final void validate0(DefaultMigrationContext ctx) {
ChangelogRecord currentRecord = currentChangelogRecord();
HistoryRecord currentRecord = currentHistoryRecord();
if (currentRecord != null) {
Commit currentCommit = commits().get(currentRecord.getMigratedTo());
@ -194,7 +234,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
continue schemaLoop;
// TODO Why is this qualification necessary?
existingMeta = existingMeta.apply(dropTableIfExists(schema.getQualifiedName().append(CHANGELOG.getUnqualifiedName())).cascade());
existingMeta = existingMeta.apply(dropTableIfExists(schema.getQualifiedName().append(HISTORY.getUnqualifiedName())).cascade());
if (!expectedSchemas.contains(schema))
existingMeta = existingMeta.apply(dropSchemaIfExists(schema).cascade());
@ -205,7 +245,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
return existingMeta.migrateTo(currentMeta);
}
private final void revertUntracked(DefaultMigrationContext ctx, MigrationListener listener, ChangelogRecord currentRecord) {
private final void revertUntracked(DefaultMigrationContext ctx, MigrationListener listener, HistoryRecord currentRecord) {
if (ctx.revertUntrackedQueries.queries().length > 0)
if (!TRUE.equals(dsl().settings().isMigrationRevertUntracked()))
throw new DataMigrationValidationException(
@ -264,11 +304,11 @@ final class MigrationImpl extends AbstractScope implements Migration {
// 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 CHANGELOG.SQL should be filled
// TODO: Migrate the CHANGELOG table with the Migration API
// TODO: Create an Enum for CHANGELOG.STATUS
// TODO: Add CHANGELOG.USERNAME and HOSTNAME columns
// TODO: Add CHANGELOG.COMMENTS column
// 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());
@ -280,7 +320,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
for (Query query : queries())
log.debug("jOOQ Migrations", dsl().renderInlined(query));
ChangelogRecord record = createRecord(STARTING);
HistoryRecord record = createRecord(STARTING);
try {
log(watch, record, REVERTING);
@ -302,8 +342,8 @@ final class MigrationImpl extends AbstractScope implements Migration {
}
}
private final ChangelogRecord createRecord(Status status) {
ChangelogRecord record = dsl().newRecord(CHANGELOG);
private final HistoryRecord createRecord(Status status) {
HistoryRecord record = historyCtx.newRecord(HISTORY);
record
.setJooqVersion(Constants.VERSION)
@ -319,7 +359,7 @@ final class MigrationImpl extends AbstractScope implements Migration {
return record;
}
private final void log(StopWatch watch, ChangelogRecord record, Status status) {
private final void log(StopWatch watch, HistoryRecord record, Status status) {
record.setMigrationTime(watch.split() / 1000000L)
.setStatus(status)
.update();
@ -346,22 +386,27 @@ final class MigrationImpl extends AbstractScope implements Migration {
/**
* Initialise the underlying {@link Configuration} with the jOOQ Migrations
* Changelog.
* History.
*/
public 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 (!existsChangelog())
dsl().meta(CHANGELOG).ddl().executeBatch();
if (!existsHistory()) {
// TODO: [#15225] This CREATE SCHEMA statement should never be necessary.
if (historyCtx.settings().getMigrationHistorySchema() != null)
historyCtx.createSchemaIfNotExists("").execute();
historyCtx.meta(HISTORY).ddl().executeBatch();
}
}
private final boolean existsChangelog() {
private final boolean existsHistory() {
// [#8301] Find a better way to test if our table already exists
try {
dsl().fetchExists(CHANGELOG);
historyCtx.fetchExists(HISTORY);
return true;
}
catch (DataAccessException ignore) {}
@ -369,20 +414,20 @@ final class MigrationImpl extends AbstractScope implements Migration {
return false;
}
private final ChangelogRecord currentChangelogRecord() {
return existsChangelog()
? dsl().selectFrom(CHANGELOG)
private final HistoryRecord currentHistoryRecord() {
return existsHistory()
? historyCtx.selectFrom(HISTORY)
// TODO: How to recover from failure?
.where(CHANGELOG.STATUS.eq(inline(SUCCESS)))
.orderBy(CHANGELOG.MIGRATED_AT.desc(), CHANGELOG.ID.desc())
.where(HISTORY.STATUS.eq(inline(SUCCESS)))
.orderBy(HISTORY.MIGRATED_AT.desc(), HISTORY.ID.desc())
.limit(1)
.fetchOne()
: null;
}
final Commit currentCommit() {
ChangelogRecord currentRecord = currentChangelogRecord();
HistoryRecord currentRecord = currentHistoryRecord();
if (currentRecord == null) {
Commit result = TRUE.equals(settings().isMigrationAutoBaseline()) ? to() : to().root();

View File

@ -67,7 +67,7 @@ final class MigrationsImpl implements Migrations {
@Override
public final Version version(String id) {
return new VersionImpl(ctx, id, null, new Version[0]);
return new VersionImpl(ctx, id, null, null, new Version[0]);
}
@Override
@ -75,14 +75,9 @@ final class MigrationsImpl implements Migrations {
return new VersionsImpl(version("init"));
}
@Override
public final Commit commit(String id) {
return new CommitImpl(ctx.configuration(), id, null, emptyList(), emptyList());
}
@Override
public final Commits commits() {
return new CommitsImpl(ctx.configuration(), commit("init"));
return new CommitsImpl(ctx.configuration(), new CommitImpl(ctx.configuration(), "init", null, null, emptyList(), emptyList()));
}
@Override

View File

@ -70,12 +70,12 @@ import org.jooq.exception.DataDefinitionException;
*/
final class VersionImpl extends AbstractNode<Version> implements Version {
private final DSLContext ctx;
private final Meta meta;
private final List<Parent> parents;
final DSLContext ctx;
final Meta meta;
final List<Parent> parents;
private VersionImpl(DSLContext ctx, String id, Meta meta, List<Parent> parents) {
super(id, null);
private VersionImpl(DSLContext ctx, String id, Meta meta, Version root, List<Parent> parents) {
super(id, null, root);
this.ctx = ctx;
this.meta = meta != null ? meta : init(ctx);
@ -95,12 +95,12 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
return result;
}
VersionImpl(DSLContext ctx, String id, Meta meta, Version parent, Queries queries) {
this(ctx, id, meta, Arrays.asList(new Parent((VersionImpl) parent, queries)));
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(DSLContext ctx, String id, Meta meta, Version[] parents) {
this(ctx, id, meta, wrap(parents));
VersionImpl(DSLContext ctx, String id, Meta meta, Version root, Version[] parents) {
this(ctx, id, meta, root, wrap(parents));
}
private static List<Parent> wrap(Version[] parents) {
@ -144,7 +144,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), this, migration);
return new VersionImpl(ctx, newId, meta().apply(migration), root, this, migration);
}
@Override
@ -173,7 +173,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, emptyList()), parent.queries));
list.add(new Parent(new VersionImpl(ctx, parent.version.id(), parent.version.meta, root, emptyList()), parent.queries));
}
else {
VersionImpl p = parent.version.subgraphTo(ancestor);
@ -187,7 +187,7 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
}
}
return list == null ? null : new VersionImpl(ctx, id(), meta, list);
return list == null ? null : new VersionImpl(ctx, id(), meta, root, list);
}
private final Queries migrateTo(VersionImpl target, Queries result) {
@ -222,13 +222,13 @@ final class VersionImpl extends AbstractNode<Version> implements Version {
@Override
public final Version commit(String newId, Meta newMeta) {
return new VersionImpl(ctx, newId, newMeta, new Version[] { this });
return new VersionImpl(ctx, 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()))), new Version[] { this, with });
return new VersionImpl(ctx, newId, m.apply(m.migrateTo(meta()).concat(m.migrateTo(with.meta()))), root, new Version[] { this, with });
}
@Override

View File

@ -1,4 +1,4 @@
CREATE TABLE jooq_migrations_changelog (
CREATE TABLE jooq_migration_history (
id BIGINT NOT NULL IDENTITY,
migrated_from VARCHAR(255) NOT NULL,
migrated_to VARCHAR(255) NOT NULL,
@ -9,18 +9,18 @@ CREATE TABLE jooq_migrations_changelog (
sql_count INT NOT NULL,
status VARCHAR(10) NOT NULL,
CONSTRAINT jooq_migr_pk PRIMARY KEY (id),
CONSTRAINT jooq_migr_chk1 CHECK (status IN ('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE'))
CONSTRAINT jooq_migr_hist_pk PRIMARY KEY (id),
CONSTRAINT jooq_migr_hist_chk1 CHECK (status IN ('STARTING', 'REVERTING', 'MIGRATING', 'SUCCESS', 'FAILURE'))
);
CREATE INDEX jooq_migr_i1 ON jooq_migrations_changelog (migrated_at);
CREATE INDEX jooq_migr_hist_i1 ON jooq_migration_history (migrated_at);
COMMENT ON TABLE jooq_migrations_changelog IS 'The migration log of jOOQ Migrations.';
COMMENT ON COLUMN jooq_migrations_changelog.id IS 'The database version ID.';
COMMENT ON COLUMN jooq_migrations_changelog.migrated_from IS 'The previous database version ID.';
COMMENT ON COLUMN jooq_migrations_changelog.migrated_at IS 'The date/time when the database version was migrated to.';
COMMENT ON COLUMN jooq_migrations_changelog.migration_time IS 'The time in milliseconds it took to migrate to this database version.';
COMMENT ON COLUMN jooq_migrations_changelog.jooq_version IS 'The jOOQ version used to migrate to this database version.';
COMMENT ON COLUMN jooq_migrations_changelog.sql_count IS 'The number of SQL statements that were run to install this database version.';
COMMENT ON COLUMN jooq_migrations_changelog.sql IS 'The SQL statements that were run to install this database version.';
COMMENT ON COLUMN jooq_migrations_changelog.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.';

View File

@ -1445,6 +1445,10 @@ deployed on an RDBMS that does not.]]></jxb:javadoc></jxb:property></appinfo></a
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The {@link org.jooq.Meta} implementation that is backed by {@link java.sql.DatabaseMetaData} does not produce system generated sequences, by default.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="migrationHistorySchema" type="jooq-runtime:MigrationSchema" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The database schema where the migration history is located.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>
<element name="migrationSchemata" type="jooq-runtime:MigrationSchemata" minOccurs="0" maxOccurs="1">
<annotation><appinfo><jxb:property><jxb:javadoc><![CDATA[The database objects that are included in the migration.]]></jxb:javadoc></jxb:property></appinfo></annotation>
</element>

12
pom.xml
View File

@ -131,6 +131,11 @@
<artifactId>jooq-codegen-maven</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-migrations-maven</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-meta</artifactId>
@ -882,10 +887,7 @@
<module>jOOQ-checker</module>
<module>jOOQ-jackson-extensions</module>
<module>jOOQ-postgres-extensions</module>
@ -895,10 +897,9 @@
<module>jOOQ-meta-extensions-liquibase</module>
<module>jOOQ-codegen</module>
<module>jOOQ-codegen-maven</module>
<module>jOOQ-migrations-maven</module>
<module>jOOQ-migrations</module>
<module>jOOQ-kotlin</module>
<module>jOOQ-kotlin-coroutines</module>
<module>jOOQ-scala_2.13</module>
@ -921,6 +922,7 @@
</modules>
<profiles>