From 8767b8014d1beaee87f4e90ed5ce3f4ce10c8525 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20Y=C3=BCcel?= Date: Tue, 23 Jul 2019 01:34:25 +0200 Subject: [PATCH] [jOOQ/jOOQ#8939] Support Flyway file ordering in DDLDatabase --- .../jOOQ-flyway-ddl-example/.gitignore | 2 + .../jOOQ-flyway-ddl-example/LICENSE.txt | 19 +++ .../jOOQ-flyway-ddl-example/README.md | 12 ++ jOOQ-examples/jOOQ-flyway-ddl-example/pom.xml | 111 ++++++++++++++ .../migration/V1.2__create_author_table.sql | 12 ++ ...1_3__create_book_table_and_add_records.sql | 17 +++ .../db/migration/V1__initialise_database.sql | 1 + .../java/org/jooq/meta/tools/FilePattern.java | 2 + .../jooq/meta/tools/FlywayFileComparator.java | 57 +++++++ .../org/jooq/meta/tools/FlywayVersion.java | 141 ++++++++++++++++++ 10 files changed, 374 insertions(+) create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/.gitignore create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/LICENSE.txt create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/README.md create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/pom.xml create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1.2__create_author_table.sql create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1_3__create_book_table_and_add_records.sql create mode 100644 jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1__initialise_database.sql create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayFileComparator.java create mode 100644 jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayVersion.java diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/.gitignore b/jOOQ-examples/jOOQ-flyway-ddl-example/.gitignore new file mode 100644 index 0000000000..0fd7a02083 --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/.gitignore @@ -0,0 +1,2 @@ +/target +/jooq-flyway-example.iml diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/LICENSE.txt b/jOOQ-examples/jOOQ-flyway-ddl-example/LICENSE.txt new file mode 100644 index 0000000000..d090694f44 --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/LICENSE.txt @@ -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 + + 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 \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/README.md b/jOOQ-examples/jOOQ-flyway-ddl-example/README.md new file mode 100644 index 0000000000..eb04f7bd73 --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/README.md @@ -0,0 +1,12 @@ +Thanks for downloading jOOQ. +Please visit http://www.jooq.org for more information. + +To install and run this example, simply check it out and run the following Maven command + +``` +$ pwd +/path/to/checkout/dir +$ cd jOOQ-examples/jOOQ-flyway-ddl-example +... +$ mvn clean install +``` diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/pom.xml b/jOOQ-examples/jOOQ-flyway-ddl-example/pom.xml new file mode 100644 index 0000000000..346d71418e --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + org.jooq + jooq-flyway-ddl-example + 1.0 + jOOQ Flyway/DDL Example + + + + Apache License, Version 2.0 + http://www.jooq.org/inc/LICENSE.txt + repo + + + + + UTF-8 + 3.2.6.RELEASE + 3.12.0-SNAPSHOT + + + + + + + org.jooq + jooq + ${org.jooq.version} + + + + + + + + + org.jooq + jooq-codegen-maven + ${org.jooq.version} + + + + java-generator + generate-sources + + generate + + + + + + org.jooq.meta.extensions.ddl.DDLDatabase + FLYWAY_TEST + + + sort + flyway + + + scripts + src/main/resources/db/migration/* + + + + + org.jooq.example.flyway.j.db.h2 + ${project.build.directory}/generated-sources/jooq-h2-java + + + + + + + + + org.jooq + jooq-meta-extensions + ${org.jooq.version} + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.0 + + true + 1024m + 256m + UTF-8 + + 11 + + + + 11 + 11 + + true + lines,vars,source + + + + + + diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1.2__create_author_table.sql b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1.2__create_author_table.sql new file mode 100644 index 0000000000..a9cf6c027f --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1.2__create_author_table.sql @@ -0,0 +1,12 @@ +CREATE SEQUENCE flyway_test.s_author_id START WITH 1; + +CREATE TABLE flyway_test.author ( + id INT NOT NULL, + first_name VARCHAR(50), + last_name VARCHAR(50) NOT NULL, + date_of_birth DATE, + year_of_birth INT, + address VARCHAR(50), + + CONSTRAINT pk_t_author PRIMARY KEY (ID) +); \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1_3__create_book_table_and_add_records.sql b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1_3__create_book_table_and_add_records.sql new file mode 100644 index 0000000000..9a1f1136aa --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1_3__create_book_table_and_add_records.sql @@ -0,0 +1,17 @@ +CREATE TABLE flyway_test.book ( + id INT NOT NULL, + author_id INT NOT NULL, + title VARCHAR(400) NOT NULL, + + CONSTRAINT pk_t_book PRIMARY KEY (id), + CONSTRAINT fk_t_book_author_id FOREIGN KEY (author_id) REFERENCES flyway_test.author(id) +); + + +INSERT INTO flyway_test.author VALUES (next value for flyway_test.s_author_id, 'George', 'Orwell', '1903-06-25', 1903, null); +INSERT INTO flyway_test.author VALUES (next value for flyway_test.s_author_id, 'Paulo', 'Coelho', '1947-08-24', 1947, null); + +INSERT INTO flyway_test.book VALUES (1, 1, '1984'); +INSERT INTO flyway_test.book VALUES (2, 1, 'Animal Farm'); +INSERT INTO flyway_test.book VALUES (3, 2, 'O Alquimista'); +INSERT INTO flyway_test.book VALUES (4, 2, 'Brida'); \ No newline at end of file diff --git a/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1__initialise_database.sql b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1__initialise_database.sql new file mode 100644 index 0000000000..35d2659ee0 --- /dev/null +++ b/jOOQ-examples/jOOQ-flyway-ddl-example/src/main/resources/db/migration/V1__initialise_database.sql @@ -0,0 +1 @@ +CREATE SCHEMA flyway_test; diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java index 37cda071a2..54c0a08ef8 100644 --- a/jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java +++ b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FilePattern.java @@ -69,6 +69,8 @@ public final class FilePattern { }; else if ("none".equals(sort)) return null; + else if ("flyway".equals(sort)) + return FlywayFileComparator.INSTANCE; else return FileComparator.INSTANCE; } diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayFileComparator.java b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayFileComparator.java new file mode 100644 index 0000000000..0498582d70 --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayFileComparator.java @@ -0,0 +1,57 @@ +/* + * 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.meta.tools; + +import java.io.File; +import java.util.Comparator; + +public final class FlywayFileComparator implements Comparator { + + public static final FlywayFileComparator INSTANCE = new FlywayFileComparator(); + + @Override + public final int compare(File o1, File o2) { + String s1 = o1 == null ? null : o1.getName(); + String s2 = o2 == null ? null : o2.getName(); + + FlywayVersion v1 = FlywayVersion.fromVersion(s1 == null ? null : s1.substring(1, s1.indexOf("__"))); + FlywayVersion v2 = FlywayVersion.fromVersion(s2 == null ? null : s2.substring(1, s2.indexOf("__"))); + + return v1.compareTo(v2); + } +} diff --git a/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayVersion.java b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayVersion.java new file mode 100644 index 0000000000..28022bbd5d --- /dev/null +++ b/jOOQ-meta/src/main/java/org/jooq/meta/tools/FlywayVersion.java @@ -0,0 +1,141 @@ +/* + * Copyright 2010-2019 Boxfuse GmbH + * + * 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. + */ +package org.jooq.meta.tools; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import org.jooq.tools.StringUtils; + +/** + * A version of a migration. + * + * @author Axel Fontaine + */ +public final class FlywayVersion implements Comparable { + /** + * Version for an empty schema. + */ + public static final FlywayVersion EMPTY = new FlywayVersion(null); + + /** + * Regex for matching proper version format + */ + private static final Pattern SPLIT_REGEX = Pattern.compile("\\.(?=\\d)"); + + /** + * The individual parts this version string is composed of. Ex. 1.2.3.4.0 -> [1, 2, 3, 4, 0] + */ + private final List versionParts; + + /** + * Create a FlywayVersion from a version String. + * + * @param version The version String. The value {@code current} will be interpreted as FlywayVersion.CURRENT, + * a marker for the latest version that has been applied to the database. + * @return The FlywayVersion + */ + public static FlywayVersion fromVersion(String version) { + if (StringUtils.isEmpty(version)) return EMPTY; + return new FlywayVersion(version); + } + + /** + * Creates a Version using this version string. + * + * @param version The version in one of the following formats: 6, 6.0, 005, 1.2.3.4, 201004200021.
{@code null} + * means that this version refers to an empty schema. + */ + private FlywayVersion(String version) { + if (!StringUtils.isEmpty(version)) { + String normalizedVersion = version.replace('_', '.'); + this.versionParts = tokenize(normalizedVersion); + } + else { + this.versionParts = new ArrayList<>(); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + FlywayVersion version1 = (FlywayVersion) o; + + return compareTo(version1) == 0; + } + + @Override + public int hashCode() { + return versionParts == null ? 0 : versionParts.hashCode(); + } + + @Override + public int compareTo(FlywayVersion o) { + if (o == null) { + return 1; + } + + if (this == EMPTY) { + if (o == EMPTY) return 0; + else return Integer.MIN_VALUE; + } + + if (o == EMPTY) { + return Integer.MAX_VALUE; + } + + final List parts1 = versionParts; + final List parts2 = o.versionParts; + int largestNumberOfParts = Math.max(parts1.size(), parts2.size()); + for (int i = 0; i < largestNumberOfParts; i++) { + final int compared = getOrZero(parts1, i).compareTo(getOrZero(parts2, i)); + if (compared != 0) { + return compared; + } + } + return 0; + } + + private BigInteger getOrZero(List elements, int i) { + return i < elements.size() ? elements.get(i) : BigInteger.ZERO; + } + + /** + * Splits this string into list of Long + * + * @param versionStr The string to split. + * @return The resulting array. + */ + private List tokenize(String versionStr) { + List parts = new ArrayList<>(); + for (String part : SPLIT_REGEX.split(versionStr)) { + parts.add(new BigInteger(part)); + } + + for (int i = parts.size() - 1; i > 0; i--) { + if (!parts.get(i).equals(BigInteger.ZERO)) { + break; + } + parts.remove(i); + } + + return parts; + } +}