From 83da9e2c230091d2b7fa42d14024f535148bc025 Mon Sep 17 00:00:00 2001 From: Lukas Eder Date: Thu, 21 Nov 2019 18:08:09 +0100 Subject: [PATCH] [jOOQ/jOOQ#9506] [jOOQ/jOOQ#9583] - [jOOQ/jOOQ#9506] Add org.jooq.Migration to describe a migration between two org.jooq.Version - [jOOQ/jOOQ#9583] Add a VersionProvider SPI --- jOOQ-codegen-maven/pom.xml | 4 - .../src/main/java/org/jooq/Configuration.java | 26 ++ jOOQ/src/main/java/org/jooq/DSLContext.java | 5 + jOOQ/src/main/java/org/jooq/Migration.java | 71 +++ .../main/java/org/jooq/MigrationResult.java | 47 ++ .../main/java/org/jooq/VersionProvider.java | 60 +++ .../org/jooq/impl/DefaultConfiguration.java | 74 +++ .../java/org/jooq/impl/DefaultDSLContext.java | 6 + .../org/jooq/impl/DefaultVersionProvider.java | 81 ++++ .../java/org/jooq/impl/MigrationImpl.java | 439 ++++++++++++++++++ .../main/java/org/jooq/impl/VersionImpl.java | 1 + .../jooq/tools/jdbc/MockConfiguration.java | 16 + .../resources/migrations/jooq-migrations.sql | 19 + pom.xml | 31 ++ 14 files changed, 876 insertions(+), 4 deletions(-) create mode 100644 jOOQ/src/main/java/org/jooq/Migration.java create mode 100644 jOOQ/src/main/java/org/jooq/MigrationResult.java create mode 100644 jOOQ/src/main/java/org/jooq/VersionProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/DefaultVersionProvider.java create mode 100644 jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java create mode 100644 jOOQ/src/main/resources/migrations/jooq-migrations.sql diff --git a/jOOQ-codegen-maven/pom.xml b/jOOQ-codegen-maven/pom.xml index a7a91cb8d6..04c95513e0 100644 --- a/jOOQ-codegen-maven/pom.xml +++ b/jOOQ-codegen-maven/pom.xml @@ -84,13 +84,10 @@ org.apache.maven maven-plugin-api - 3.6.0 org.apache.maven maven-core - 3.6.0 - jar @@ -103,7 +100,6 @@ org.apache.maven.plugin-tools maven-plugin-annotations - 3.5.2 provided diff --git a/jOOQ/src/main/java/org/jooq/Configuration.java b/jOOQ/src/main/java/org/jooq/Configuration.java index 3a0847faa7..e8a9015803 100644 --- a/jOOQ/src/main/java/org/jooq/Configuration.java +++ b/jOOQ/src/main/java/org/jooq/Configuration.java @@ -284,6 +284,11 @@ public interface Configuration extends Serializable { */ MetaProvider metaProvider(); + /** + * Get this configuration's underlying meta provider. + */ + VersionProvider versionProvider(); + /** * Get this configuration's underlying executor provider. *

@@ -499,6 +504,18 @@ public interface Configuration extends Serializable { */ Configuration set(MetaProvider newMetaProvider); + /** + * Change this configuration to hold a new version provider. + *

+ * This method is not thread-safe and should not be used in globally + * available Configuration objects. + * + * @param newVersionProvider The new version provider to be contained in the + * changed configuration. + * @return The changed configuration. + */ + Configuration set(VersionProvider newVersionProvider); + /** * Change this configuration to hold a new executor provider. *

@@ -877,6 +894,15 @@ public interface Configuration extends Serializable { */ Configuration derive(MetaProvider newMetaProvider); + /** + * Create a derived configuration from this one, with a new version provider. + * + * @param newVersionProvider The new version provider to be contained in the + * derived configuration. + * @return The derived configuration. + */ + Configuration derive(VersionProvider newVersionProvider); + /** * Create a derived configuration from this one, with a new executor. *

diff --git a/jOOQ/src/main/java/org/jooq/DSLContext.java b/jOOQ/src/main/java/org/jooq/DSLContext.java index 9dbfedc9a5..5839be0fc0 100644 --- a/jOOQ/src/main/java/org/jooq/DSLContext.java +++ b/jOOQ/src/main/java/org/jooq/DSLContext.java @@ -253,6 +253,11 @@ public interface DSLContext extends Scope , AutoCloseable { */ Version version(String id); + /** + * Create a migration to a new version. + */ + Migration migration(Version to); + /** * Access the database meta data. *

diff --git a/jOOQ/src/main/java/org/jooq/Migration.java b/jOOQ/src/main/java/org/jooq/Migration.java new file mode 100644 index 0000000000..19d3c00def --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/Migration.java @@ -0,0 +1,71 @@ +/* + * 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 + * + * http://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: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import org.jooq.exception.DataDefinitionException; + +/** + * An executable migration between two {@link Version} instances. + * + * @author Lukas Eder + */ +public interface Migration extends Scope { + + /** + * The version that is being migrated from. + */ + Version from(); + + /** + * The version that is being migrated to. + */ + Version to(); + + /** + * The queries that are executed by the migration. + */ + Queries queries(); + + /** + * Apply the migration. + * + * @throws DataDefinitionException When something went wrong during the + * application of the migration. + */ + MigrationResult migrate() throws DataDefinitionException; +} diff --git a/jOOQ/src/main/java/org/jooq/MigrationResult.java b/jOOQ/src/main/java/org/jooq/MigrationResult.java new file mode 100644 index 0000000000..bda6d6797a --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/MigrationResult.java @@ -0,0 +1,47 @@ +/* + * 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 + * + * http://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: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +/** + * The result of a {@link Migration}. + * + * @author Lukas Eder + */ +public interface MigrationResult { + +} diff --git a/jOOQ/src/main/java/org/jooq/VersionProvider.java b/jOOQ/src/main/java/org/jooq/VersionProvider.java new file mode 100644 index 0000000000..7b55c4e807 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/VersionProvider.java @@ -0,0 +1,60 @@ +/* + * 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 + * + * http://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: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq; + +import java.util.Set; + +/** + * An SPI that allows for providing a graph of versions. + * + * @author Lukas Eder + */ +@Internal // TODO This SPI is still being worked on and might change incompatibly +public interface VersionProvider { + + /** + * Provide a set of versions relevant to a migration. + *

+ * This can include the entire set of known versions, or a subset thereof. + * There is no requirement to provide a fully connected graph, although + * {@link Version#migrateFrom(Version)} and other operations are undefined + * if two versions do not have a common ancestor. + */ + @Internal // TODO Produce a better type than Set. Possibly, #current() can be obtained from the new result type. + Set provide(); +} diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java index 83c5c1c1de..52020b14a9 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultConfiguration.java @@ -77,6 +77,7 @@ import org.jooq.TransactionListenerProvider; import org.jooq.TransactionProvider; import org.jooq.Unwrapper; import org.jooq.UnwrapperProvider; +import org.jooq.VersionProvider; import org.jooq.VisitListener; import org.jooq.VisitListenerProvider; import org.jooq.conf.Settings; @@ -112,6 +113,7 @@ public class DefaultConfiguration implements Configuration { private transient ConnectionProvider interpreterConnectionProvider; private transient ConnectionProvider systemConnectionProvider; private transient MetaProvider metaProvider; + private transient VersionProvider versionProvider; private transient ExecutorProvider executorProvider; private transient TransactionProvider transactionProvider; private transient RecordMapperProvider recordMapperProvider; @@ -174,6 +176,7 @@ public class DefaultConfiguration implements Configuration { null, null, null, + null, null, @@ -197,6 +200,7 @@ public class DefaultConfiguration implements Configuration { configuration.interpreterConnectionProvider, configuration.systemConnectionProvider, configuration.metaProvider, + configuration.versionProvider, configuration.executorProvider, configuration.transactionProvider, configuration.recordMapperProvider, @@ -230,6 +234,7 @@ public class DefaultConfiguration implements Configuration { ConnectionProvider interpreterConnectionProvider, ConnectionProvider systemConnectionProvider, MetaProvider metaProvider, + VersionProvider versionProvider, ExecutorProvider executorProvider, TransactionProvider transactionProvider, RecordMapperProvider recordMapperProvider, @@ -252,6 +257,7 @@ public class DefaultConfiguration implements Configuration { setInterpreterConnectionProvider(interpreterConnectionProvider); setSystemConnectionProvider(systemConnectionProvider); set(metaProvider); + set(versionProvider); set(executorProvider); set(transactionProvider); set(recordMapperProvider); @@ -309,6 +315,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -336,6 +343,35 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, newMetaProvider, + versionProvider, + executorProvider, + transactionProvider, + recordMapperProvider, + recordUnmapperProvider, + recordListenerProviders, + executeListenerProviders, + visitListenerProviders, + transactionListenerProviders, + diagnosticsListenerProviders, + unwrapperProvider, + converterProvider, + + clock, + + dialect, + settings, + data + ); + } + + @Override + public final Configuration derive(VersionProvider newVersionProvider) { + return new DefaultConfiguration( + connectionProvider, + interpreterConnectionProvider, + systemConnectionProvider, + metaProvider, + newVersionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -368,6 +404,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, newExecutorProvider, transactionProvider, recordMapperProvider, @@ -395,6 +432,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, newTransactionProvider, recordMapperProvider, @@ -427,6 +465,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, newRecordMapperProvider, @@ -459,6 +498,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -491,6 +531,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -523,6 +564,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -555,6 +597,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -587,6 +630,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -619,6 +663,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -651,6 +696,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -678,6 +724,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -706,6 +753,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -732,6 +780,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -759,6 +808,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider, systemConnectionProvider, metaProvider, + versionProvider, executorProvider, transactionProvider, recordMapperProvider, @@ -817,6 +867,12 @@ public class DefaultConfiguration implements Configuration { return this; } + @Override + public final Configuration set(VersionProvider newVersionProvider) { + this.versionProvider = newVersionProvider; + return this; + } + @Override public final Configuration set(Executor newExecutor) { return set(new ExecutorWrapper(newExecutor)); @@ -1035,6 +1091,13 @@ public class DefaultConfiguration implements Configuration { set(newMetaProvider); } + /** + * @see #set(MetaProvider) + */ + public final void setVersionProvider(VersionProvider newVersionProvider) { + set(newVersionProvider); + } + /** * @see #set(Executor) */ @@ -1229,6 +1292,13 @@ public class DefaultConfiguration implements Configuration { : new DefaultMetaProvider(this); } + @Override + public final VersionProvider versionProvider() { + return versionProvider != null + ? versionProvider + : new DefaultVersionProvider(this); + } + @Override public final ExecutorProvider executorProvider() { return executorProvider != null @@ -1379,6 +1449,9 @@ public class DefaultConfiguration implements Configuration { oos.writeObject(metaProvider instanceof Serializable ? metaProvider : null); + oos.writeObject(versionProvider instanceof Serializable + ? versionProvider + : null); oos.writeObject(transactionProvider instanceof Serializable ? transactionProvider : null); @@ -1437,6 +1510,7 @@ public class DefaultConfiguration implements Configuration { interpreterConnectionProvider = (ConnectionProvider) ois.readObject(); systemConnectionProvider = (ConnectionProvider) ois.readObject(); metaProvider = (MetaProvider) ois.readObject(); + versionProvider = (VersionProvider) ois.readObject(); transactionProvider = (TransactionProvider) ois.readObject(); recordMapperProvider = (RecordMapperProvider) ois.readObject(); recordUnmapperProvider = (RecordUnmapperProvider) ois.readObject(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java index 83a0d48f96..1f5868f0ae 100644 --- a/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultDSLContext.java @@ -191,6 +191,7 @@ import org.jooq.MergeKeyStep9; import org.jooq.MergeKeyStepN; import org.jooq.MergeUsingStep; import org.jooq.Meta; +import org.jooq.Migration; import org.jooq.Name; import org.jooq.Param; import org.jooq.Parser; @@ -415,6 +416,11 @@ public class DefaultDSLContext extends AbstractScope implements DSLContext, Seri return new VersionImpl(this, id, null, new Version[0]); } + @Override + public Migration migration(Version to) { + return new MigrationImpl(configuration, to); + } + @Override public Meta meta() { return configuration().metaProvider().provide(); diff --git a/jOOQ/src/main/java/org/jooq/impl/DefaultVersionProvider.java b/jOOQ/src/main/java/org/jooq/impl/DefaultVersionProvider.java new file mode 100644 index 0000000000..4dcfc684a3 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/DefaultVersionProvider.java @@ -0,0 +1,81 @@ +/* + * 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 + * + * http://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: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.util.LinkedHashSet; +import java.util.Set; + +import org.jooq.Configuration; +import org.jooq.DSLContext; +import org.jooq.Parser; +import org.jooq.Source; +import org.jooq.Version; +import org.jooq.VersionProvider; + +/** + * A default implementation of the {@link VersionProvider} SPI, which provides + * a materialisation of the currently available database version graph. + *

+ * It is based + * + * @author Lukas Eder + */ +@org.jooq.Internal // TODO This is work in progress. The current implementation is not useful yet. +public class DefaultVersionProvider implements VersionProvider { + + private final DSLContext ctx; + private final Source[] sources; + + public DefaultVersionProvider(Configuration configuration, Source... sources) { + this.ctx = configuration.dsl(); + this.sources = sources; + } + + @Override + public Set provide() { + Set result = new LinkedHashSet<>(); + Version parent; + result.add(parent = ctx.version("initial")); + + Parser parser = ctx.parser(); + for (int i = 0; i < sources.length; i++) + parent = parent.apply("version" + (i + 1), parser.parse(sources[i].readString())); + + return result; + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java b/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java new file mode 100644 index 0000000000..f88691fea2 --- /dev/null +++ b/jOOQ/src/main/java/org/jooq/impl/MigrationImpl.java @@ -0,0 +1,439 @@ +/* + * 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 + * + * http://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: http://www.jooq.org/licenses + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + */ +package org.jooq.impl; + +import java.sql.Timestamp; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.jooq.Configuration; +import org.jooq.Constants; +import org.jooq.ContextTransactionalCallable; +import org.jooq.Field; +import org.jooq.Identity; +import org.jooq.Migration; +import org.jooq.MigrationResult; +import org.jooq.Name; +import org.jooq.Queries; +import org.jooq.Query; +import org.jooq.Record1; +import org.jooq.Schema; +import org.jooq.Table; +import org.jooq.TableField; +import org.jooq.UniqueKey; +import org.jooq.Version; +import org.jooq.exception.DataAccessException; +import org.jooq.exception.DataDefinitionException; +import org.jooq.tools.JooqLogger; +import org.jooq.tools.StopWatch; + +/** + * @author Lukas Eder + */ +final class MigrationImpl extends AbstractScope implements Migration { + + private static final JooqLogger log = JooqLogger.getLogger(Migration.class); + + // TODO: Make this table and its schema configurable + private static final JooqMigrationsChangelog CHANGELOG = JooqMigrationsChangelog.JOOQ_MIGRATIONS_CHANGELOG; + private final Version to; + private Version from; + private Queries queries; + private Map versions; + + MigrationImpl(Configuration configuration, Version to) { + super(configuration.derive(new ThreadLocalTransactionProvider(configuration.systemConnectionProvider()))); + + this.to = to; + } + + @Override + public final Version from() { + if (from == null) { + + // TODO: Use pessimistic locking so no one else can migrate in between + JooqMigrationsChangelogRecord currentRecord = + dsl().selectFrom(CHANGELOG) + .orderBy(CHANGELOG.MIGRATED_AT.desc(), CHANGELOG.ID.desc()) + .limit(1) + .fetchOne(); + + from = currentRecord == null ? to().root() : versions().get(currentRecord.getMigratedTo()); + } + + return from; + } + + @Override + public final Version to() { + return to; + } + + @Override + public final Queries queries() { + if (queries == null) + queries = to().migrateFrom(from()); + + return queries; + } + + private final Map versions() { + if (versions == null) { + versions = new HashMap<>(); + + for (Version version : configuration().versionProvider().provide()) + versions.put(version.id(), version); + } + + return versions; + } + + private static final MigrationResult MIGRATION_RESULT = new MigrationResult() {}; + + @Override + public final MigrationResult migrate() throws DataDefinitionException { + return run(new ContextTransactionalCallable() { + @Override + public MigrationResult run() { + if (from().equals(to())) { + log.info("jOOQ Migrations", "Version " + to().id() + " is already installed as the current version."); + return MIGRATION_RESULT; + } + + log.info("jOOQ Migrations", "Version " + from().id() + " is migrated to " + to().id()); + + StopWatch watch = new StopWatch(); + + // TODO: Make logging configurable + if (log.isDebugEnabled()) + for (Query query : queries()) + log.debug("jOOQ Migrations", dsl().renderInlined(query)); + + // TODO: Make batching an option + queries().executeBatch(); + + JooqMigrationsChangelogRecord newRecord = dsl().newRecord(CHANGELOG); + newRecord + .setJooqVersion(Constants.VERSION) + .setMigratedAt(new Timestamp(System.currentTimeMillis())) + .setMigratedFrom(from().id()) + .setMigratedTo(to().id()) + .setMigrationTime(watch.split() / 1000000L) + .store(); + + return MIGRATION_RESULT; + } + }); + } + + /** + * Initialise the underlying {@link Configuration} with the jOOQ Migrations + * Changelog. + */ + 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 explicitl by the user? + // - Will we reverse engineer the production Meta snapshot first? + if (!existsChangelog()) + dsl().meta(CHANGELOG).ddl().executeBatch(); + } + + private final boolean existsChangelog() { + + // [#8301] Find a better way to test if our table already exists + try { + dsl().fetchExists(CHANGELOG); + return true; + } + catch (DataAccessException ignore) {} + + return false; + } + + private final T run(final ContextTransactionalCallable runnable) { + init(); + return dsl().transactionResult(runnable); + } + + // ------------------------------------------------------------------------- + // XXX: Generated code + // ------------------------------------------------------------------------- + + // These classes have been generated and copied here. It would be desirable: + // - [#6948] To be able to generate package private classes directly inside of other classes + // - [#7444] Alternatively, have a simple public API replacing TableImpl + + /** + * The migration log of jOOQ Migrations. + */ + @SuppressWarnings({ "all", "unchecked", "rawtypes" }) + static class JooqMigrationsChangelog extends TableImpl { + + private static final long serialVersionUID = 1147896779; + + /** + * The reference instance of JOOQ_MIGRATIONS_CHANGELOG + */ + public static final JooqMigrationsChangelog JOOQ_MIGRATIONS_CHANGELOG = new JooqMigrationsChangelog(); + + /** + * The class holding records for this type + */ + @Override + public Class getRecordType() { + return JooqMigrationsChangelogRecord.class; + } + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.ID. The database version ID. + */ + public final TableField ID = createField(DSL.name("ID"), org.jooq.impl.SQLDataType.BIGINT.nullable(false).identity(true), this, "The database version ID."); + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM. The previous database version ID. + */ + public final TableField MIGRATED_FROM = createField(DSL.name("MIGRATED_FROM"), org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, "The previous database version ID."); + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO. + */ + public final TableField MIGRATED_TO = createField(DSL.name("MIGRATED_TO"), org.jooq.impl.SQLDataType.VARCHAR(255).nullable(false), this, ""); + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT. The date/time when the database version was migrated to. + */ + public final TableField MIGRATED_AT = createField(DSL.name("MIGRATED_AT"), org.jooq.impl.SQLDataType.TIMESTAMP.precision(6).nullable(false), this, "The date/time when the database version was migrated to."); + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME. The time in milliseconds it took to migrate to this database version. + */ + public final TableField MIGRATION_TIME = createField(DSL.name("MIGRATION_TIME"), org.jooq.impl.SQLDataType.BIGINT, this, "The time in milliseconds it took to migrate to this database version."); + + /** + * The column JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION. The jOOQ version used to migrate to this database version. + */ + public final TableField JOOQ_VERSION = createField(DSL.name("JOOQ_VERSION"), org.jooq.impl.SQLDataType.VARCHAR(50).nullable(false), this, "The jOOQ version used to migrate to this database version."); + + /** + * Create a JOOQ_MIGRATIONS_CHANGELOG table reference + */ + public JooqMigrationsChangelog() { + this(DSL.name("JOOQ_MIGRATIONS_CHANGELOG"), null); + } + + /** + * Create an aliased JOOQ_MIGRATIONS_CHANGELOG table reference + */ + public JooqMigrationsChangelog(String alias) { + this(DSL.name(alias), JOOQ_MIGRATIONS_CHANGELOG); + } + + /** + * Create an aliased JOOQ_MIGRATIONS_CHANGELOG table reference + */ + public JooqMigrationsChangelog(Name alias) { + this(alias, JOOQ_MIGRATIONS_CHANGELOG); + } + + private JooqMigrationsChangelog(Name alias, Table aliased) { + this(alias, aliased, null); + } + + private JooqMigrationsChangelog(Name alias, Table aliased, Field[] parameters) { + super(alias, null, aliased, parameters, DSL.comment("The migration log of jOOQ Migrations.")); + } + + @Override + public Schema getSchema() { + return new SchemaImpl(""); + } + + @Override + public Identity getIdentity() { + return Internal.createIdentity(JOOQ_MIGRATIONS_CHANGELOG, JOOQ_MIGRATIONS_CHANGELOG.ID); + } + + @Override + public UniqueKey getPrimaryKey() { + return Internal.createUniqueKey(JOOQ_MIGRATIONS_CHANGELOG, "JOOQ_MIGRATIONS_CHANGELOG_PK", JOOQ_MIGRATIONS_CHANGELOG.ID); + } + + @Override + public List> getKeys() { + return Arrays.>asList( + Internal.createUniqueKey(JOOQ_MIGRATIONS_CHANGELOG, "JOOQ_MIGRATIONS_CHANGELOG_PK", JOOQ_MIGRATIONS_CHANGELOG.ID) + ); + } + } + + /** + * The migration log of jOOQ Migrations. + */ + @SuppressWarnings({ "all", "unchecked", "rawtypes" }) + static class JooqMigrationsChangelogRecord extends UpdatableRecordImpl { + + private static final long serialVersionUID = 2016380678; + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.ID. The database version ID. + */ + public JooqMigrationsChangelogRecord setId(Long value) { + set(0, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.ID. The database version ID. + */ + public Long getId() { + return (Long) get(0); + } + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM. The previous database version ID. + */ + public JooqMigrationsChangelogRecord setMigratedFrom(String value) { + set(1, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_FROM. The previous database version ID. + */ + public String getMigratedFrom() { + return (String) get(1); + } + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO. + */ + public JooqMigrationsChangelogRecord setMigratedTo(String value) { + set(2, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_TO. + */ + public String getMigratedTo() { + return (String) get(2); + } + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT. The date/time when the database version was migrated to. + */ + public JooqMigrationsChangelogRecord setMigratedAt(Timestamp value) { + set(3, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATED_AT. The date/time when the database version was migrated to. + */ + public Timestamp getMigratedAt() { + return (Timestamp) get(3); + } + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME. The time in milliseconds it took to migrate to this database version. + */ + public JooqMigrationsChangelogRecord setMigrationTime(Long value) { + set(4, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.MIGRATION_TIME. The time in milliseconds it took to migrate to this database version. + */ + public Long getMigrationTime() { + return (Long) get(4); + } + + /** + * Setter for JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION. The jOOQ version used to migrate to this database version. + */ + public JooqMigrationsChangelogRecord setJooqVersion(String value) { + set(5, value); + return this; + } + + /** + * Getter for JOOQ_MIGRATIONS_CHANGELOG.JOOQ_VERSION. The jOOQ version used to migrate to this database version. + */ + public String getJooqVersion() { + return (String) get(5); + } + + // ------------------------------------------------------------------------- + // Primary key information + // ------------------------------------------------------------------------- + + @Override + public Record1 key() { + return (Record1) super.key(); + } + + // ------------------------------------------------------------------------- + // Constructors + // ------------------------------------------------------------------------- + + /** + * Create a detached JooqMigrationsChangelogRecord + */ + public JooqMigrationsChangelogRecord() { + super(JooqMigrationsChangelog.JOOQ_MIGRATIONS_CHANGELOG); + } + + /** + * Create a detached, initialised JooqMigrationsChangelogRecord + */ + public JooqMigrationsChangelogRecord(Long id, String migratedFrom, String migratedTo, Timestamp migratedAt, Long migrationTime, String jooqVersion) { + super(JooqMigrationsChangelog.JOOQ_MIGRATIONS_CHANGELOG); + + set(0, id); + set(1, migratedFrom); + set(2, migratedTo); + set(3, migratedAt); + set(4, migrationTime); + set(5, jooqVersion); + } + } +} diff --git a/jOOQ/src/main/java/org/jooq/impl/VersionImpl.java b/jOOQ/src/main/java/org/jooq/impl/VersionImpl.java index 4c48c543eb..93bc0b00c6 100644 --- a/jOOQ/src/main/java/org/jooq/impl/VersionImpl.java +++ b/jOOQ/src/main/java/org/jooq/impl/VersionImpl.java @@ -142,6 +142,7 @@ final class VersionImpl implements Version { + return subgraph.migrateFrom((VersionImpl) version, ctx.queries()); } diff --git a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java index 39fc112f21..86044f6630 100644 --- a/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java +++ b/jOOQ/src/main/java/org/jooq/tools/jdbc/MockConfiguration.java @@ -66,6 +66,7 @@ import org.jooq.TransactionListenerProvider; import org.jooq.TransactionProvider; import org.jooq.Unwrapper; import org.jooq.UnwrapperProvider; +import org.jooq.VersionProvider; import org.jooq.VisitListener; import org.jooq.VisitListenerProvider; import org.jooq.conf.Settings; @@ -142,6 +143,11 @@ public class MockConfiguration implements Configuration { return delegate.metaProvider(); } + @Override + public VersionProvider versionProvider() { + return delegate.versionProvider(); + } + @Override public ExecutorProvider executorProvider() { return delegate.executorProvider(); @@ -234,6 +240,11 @@ public class MockConfiguration implements Configuration { return delegate.set(newMetaProvider); } + @Override + public Configuration set(VersionProvider newVersionProvider) { + return delegate.set(newVersionProvider); + } + @Override public Configuration set(Connection newConnection) { return delegate.set(newConnection); @@ -386,6 +397,11 @@ public class MockConfiguration implements Configuration { return delegate.derive(newMetaProvider); } + @Override + public Configuration derive(VersionProvider newVersionProvider) { + return delegate.derive(newVersionProvider); + } + @Override public Configuration derive(Executor newExecutor) { return delegate.derive(newExecutor); diff --git a/jOOQ/src/main/resources/migrations/jooq-migrations.sql b/jOOQ/src/main/resources/migrations/jooq-migrations.sql new file mode 100644 index 0000000000..a6aa2362d4 --- /dev/null +++ b/jOOQ/src/main/resources/migrations/jooq-migrations.sql @@ -0,0 +1,19 @@ +CREATE TABLE jooq_migrations_changelog ( + id BIGINT NOT NULL IDENTITY, + migrated_from VARCHAR(255) NOT NULL, + migrated_to VARCHAR(255) NOT NULL, + migrated_at TIMESTAMP NOT NULL, + migration_time BIGINT NULL, + jooq_version VARCHAR(50) NOT NULL, + + CONSTRAINT jooq_migrations_changelog_pk PRIMARY KEY (id) +); + +CREATE INDEX jooq_migrations_changelog_i1 ON jooq_migrations_changelog (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.' \ No newline at end of file diff --git a/pom.xml b/pom.xml index 6ff5a3595c..77338ae43e 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,31 @@ 1.0-rc6 true + + + + org.apache.maven + maven-plugin-api + 3.6.2 + + + org.apache.maven + maven-core + 3.6.2 + + + + + junit + junit + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + @@ -406,6 +431,12 @@ + + + org.jooq + jooq-codegen-maven + ${project.version} +