From 284606e2071ef679eb0a492361c677af1e6577fc Mon Sep 17 00:00:00 2001 From: Jan Volf <1275348+javolek@users.noreply.github.com> Date: Tue, 23 Apr 2024 10:49:56 +0200 Subject: [PATCH 1/2] Crate Database support Extension implementation --- flyway-database-crate/pom.xml | 43 +++++++++++++ .../database/CrateDatabaseExtension.java | 24 +++++++ .../database/crate/CrateConnection.java | 22 +++++++ .../database/crate/CrateDatabase.java | 64 +++++++++++++++++++ .../database/crate/CrateDatabaseType.java | 57 +++++++++++++++++ .../community/database/crate/CrateParser.java | 11 ++++ .../community/database/crate/CrateSchema.java | 64 +++++++++++++++++++ .../community/database/crate/CrateTable.java | 29 +++++++++ .../org.flywaydb.core.extensibility.Plugin | 1 + .../community/database/crate/version.txt | 1 + pom.xml | 1 + 11 files changed, 317 insertions(+) create mode 100644 flyway-database-crate/pom.xml create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/CrateDatabaseExtension.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateConnection.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabase.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabaseType.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateParser.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java create mode 100644 flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateTable.java create mode 100644 flyway-database-crate/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin create mode 100644 flyway-database-crate/src/main/resources/org/flywaydb/community/database/crate/version.txt diff --git a/flyway-database-crate/pom.xml b/flyway-database-crate/pom.xml new file mode 100644 index 0000000..f659f4b --- /dev/null +++ b/flyway-database-crate/pom.xml @@ -0,0 +1,43 @@ + + + 4.0.0 + + org.flywaydb + flyway-community-db-support + 10.11.0 + + + flyway-database-crate + ${project.artifactId} + + + + ${project.groupId} + flyway-core + + + org.projectlombok + lombok + provided + + + + + + + src/main/resources + true + + + + + maven-resources-plugin + + + maven-jar-plugin + + + + \ No newline at end of file diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/CrateDatabaseExtension.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/CrateDatabaseExtension.java new file mode 100644 index 0000000..008f1b3 --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/CrateDatabaseExtension.java @@ -0,0 +1,24 @@ +package org.flywaydb.community.database; + +import org.flywaydb.core.api.FlywayException; +import org.flywaydb.core.extensibility.PluginMetadata; +import org.flywaydb.core.internal.util.FileUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class CrateDatabaseExtension implements PluginMetadata { + public String getDescription() { + return "Community-contributed Crate database support extension " + readVersion() + " by JaVol"; + } + + public static String readVersion() { + try { + return FileUtils.copyToString( + CrateDatabaseExtension.class.getClassLoader().getResourceAsStream("org/flywaydb/community/database/crate/version.txt"), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new FlywayException("Unable to read extension version: " + e.getMessage(), e); + } + } +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateConnection.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateConnection.java new file mode 100644 index 0000000..ca30560 --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateConnection.java @@ -0,0 +1,22 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.internal.database.base.Connection; +import org.flywaydb.core.internal.database.base.Schema; + +import java.sql.SQLException; + +public class CrateConnection extends Connection { + public CrateConnection(CrateDatabase database, java.sql.Connection connection) { + super(database, connection); + } + + @Override + protected String getCurrentSchemaNameOrSearchPath() throws SQLException { + return jdbcTemplate.queryForString("SELECT current_schema"); + } + + @Override + public Schema getSchema(String name) { + return new CrateSchema(jdbcTemplate, database, name); + } +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabase.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabase.java new file mode 100644 index 0000000..b4ec47c --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabase.java @@ -0,0 +1,64 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.api.configuration.Configuration; +import org.flywaydb.core.internal.database.base.Database; +import org.flywaydb.core.internal.database.base.Table; +import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory; +import org.flywaydb.core.internal.jdbc.StatementInterceptor; + +import java.sql.Connection; + +public class CrateDatabase extends Database { + + public CrateDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) { + super(configuration, jdbcConnectionFactory, statementInterceptor); + } + + @Override + protected CrateConnection doGetConnection(Connection connection) { + return new CrateConnection(this, connection); + } + + @Override + public void ensureSupported(Configuration configuration) { + // NOOP + } + + @Override + public boolean supportsDdlTransactions() { + return false; + } + + @Override + public String getBooleanTrue() { + return "TRUE"; + } + + @Override + public String getBooleanFalse() { + return "FALSE"; + } + + @Override + public boolean catalogIsSchema() { + return false; + } + + @Override + public String getRawCreateScript(Table table, boolean baseline) { + return "CREATE TABLE " + table + """ + ( + installed_rank INT NOT NULL PRIMARY KEY, + version VARCHAR(50), + description VARCHAR(200) NOT NULL, + type VARCHAR(20) NOT NULL, + script VARCHAR(1000) NOT NULL, + checksum INTEGER, + installed_by varchar(100) NOT NULL, + installed_on TIMESTAMP NOT NULL DEFAULT now(), + execution_time INTEGER NOT NULL, + success BOOLEAN NOT NULL + ); + """ + (baseline ? getBaselineStatement(table) + ";\n" : ""); + } +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabaseType.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabaseType.java new file mode 100644 index 0000000..41e09fe --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateDatabaseType.java @@ -0,0 +1,57 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.api.ResourceProvider; +import org.flywaydb.core.api.configuration.Configuration; +import org.flywaydb.core.internal.database.base.BaseDatabaseType; +import org.flywaydb.core.internal.database.base.CommunityDatabaseType; +import org.flywaydb.core.internal.database.base.Database; +import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory; +import org.flywaydb.core.internal.jdbc.StatementInterceptor; +import org.flywaydb.core.internal.parser.Parser; +import org.flywaydb.core.internal.parser.ParsingContext; + +import java.sql.Connection; +import java.sql.Types; + +public class CrateDatabaseType extends BaseDatabaseType implements CommunityDatabaseType { + @Override + public String getName() { + return "CrateDB"; + } + + @Override + public int getNullType() { + return Types.NULL; + } + + @Override + public boolean handlesJDBCUrl(String url) { + return url.startsWith("jdbc:postgresql:"); + } + + @Override + public String getDriverClass(String url, ClassLoader classLoader) { + return "org.postgresql.Driver"; + } + + @Override + public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) { + return databaseProductName.contains("PostgreSQL"); + } + + @Override + public Database createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) { + return new CrateDatabase(configuration, jdbcConnectionFactory, statementInterceptor); + } + + @Override + public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) { + return new CrateParser(configuration, parsingContext); + } + + @Override + public int getPriority() { + // have a precedence over standard PostgreSQL + return 1; + } +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateParser.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateParser.java new file mode 100644 index 0000000..f87a480 --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateParser.java @@ -0,0 +1,11 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.api.configuration.Configuration; +import org.flywaydb.core.internal.parser.Parser; +import org.flywaydb.core.internal.parser.ParsingContext; + +public class CrateParser extends Parser { + public CrateParser(Configuration configuration, ParsingContext parsingContext) { + super(configuration, parsingContext, 3); + } +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java new file mode 100644 index 0000000..c331cd4 --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java @@ -0,0 +1,64 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.internal.database.base.Schema; +import org.flywaydb.core.internal.database.base.Table; +import org.flywaydb.core.internal.jdbc.JdbcTemplate; + +import java.sql.SQLException; +import java.util.List; + +public class CrateSchema extends Schema { + public CrateSchema(JdbcTemplate jdbcTemplate, CrateDatabase database, String name) { + super(jdbcTemplate, database, name); + } + + @Override + protected boolean doExists() throws SQLException { + return jdbcTemplate.queryForInt("SELECT COUNT() FROM information_schema.schemata WHERE schema_name = ?", + name) > 0; + } + + @Override + protected boolean doEmpty() throws SQLException { + return jdbcTemplate.queryForInt(""" + SELECT COUNT() FROM ( + SELECT table_name FROM information_schema.tables WHERE table_schema = ? UNION + SELECT table_name FROM information_schema.views WHERE table_schema = ? UNION + SELECT rountine_name FROM information_schema.routines WHERE routine_schema = ? + ) objs""", + name, name, name) == 0; + } + + @Override + protected void doCreate() throws SQLException { + // schema cannot be explicitly created + // NOOP + } + + @Override + protected void doDrop() throws SQLException { + // schema cannot be explicitly removed + // NOOP + } + + @Override + protected void doClean() throws SQLException { + + } + + @Override + protected CrateTable[] doAllTables() throws SQLException { + return jdbcTemplate + .queryForStringList("SELECT table_name FROM information_schema.tables WHERE table_schema = ?", + name) + .stream() + .map( tableName -> new CrateTable(jdbcTemplate, database, this, tableName) ) + .toArray(CrateTable[]::new); + } + + @Override + public Table getTable(String tableName) { + return new CrateTable(jdbcTemplate, database, this, tableName); + } + +} diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateTable.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateTable.java new file mode 100644 index 0000000..0637006 --- /dev/null +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateTable.java @@ -0,0 +1,29 @@ +package org.flywaydb.community.database.crate; + +import org.flywaydb.core.internal.database.base.Table; +import org.flywaydb.core.internal.jdbc.JdbcTemplate; + +import java.sql.SQLException; + +public class CrateTable extends Table { + public CrateTable(JdbcTemplate jdbcTemplate, CrateDatabase database, CrateSchema schema, String name) { + super(jdbcTemplate, database, schema, name); + } + + @Override + protected void doDrop() throws SQLException { + jdbcTemplate.execute("DROP TABLE " + this); + } + + @Override + protected boolean doExists() throws SQLException { + return jdbcTemplate.queryForInt( + "SELECT COUNT() FROM information_schema.tables WHERE table_schema = ? AND table_name = ?", + schema.getName(), name) > 0; + } + + @Override + protected void doLock() throws SQLException { + // NOOP - There is no support for locking in CrateDB + } +} diff --git a/flyway-database-crate/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin b/flyway-database-crate/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin new file mode 100644 index 0000000..6b5adcc --- /dev/null +++ b/flyway-database-crate/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin @@ -0,0 +1 @@ +org.flywaydb.community.database.crate.CrateDatabaseType diff --git a/flyway-database-crate/src/main/resources/org/flywaydb/community/database/crate/version.txt b/flyway-database-crate/src/main/resources/org/flywaydb/community/database/crate/version.txt new file mode 100644 index 0000000..1785151 --- /dev/null +++ b/flyway-database-crate/src/main/resources/org/flywaydb/community/database/crate/version.txt @@ -0,0 +1 @@ +${pom.version} \ No newline at end of file diff --git a/pom.xml b/pom.xml index d8a0157..f26f015 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ flyway-database-yugabytedb flyway-database-clickhouse flyway-database-oceanbase + flyway-database-crate From 89590b004dd7186ada4e91cb03cfed516aa28557 Mon Sep 17 00:00:00 2001 From: Jan Volf <1275348+javolek@users.noreply.github.com> Date: Wed, 24 Apr 2024 11:54:35 +0200 Subject: [PATCH 2/2] fixed schema object selection, implementing cleaning of database schema --- .../community/database/crate/CrateSchema.java | 30 +++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java index c331cd4..be20455 100644 --- a/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java +++ b/flyway-database-crate/src/main/java/org/flywaydb/community/database/crate/CrateSchema.java @@ -22,9 +22,9 @@ protected boolean doExists() throws SQLException { protected boolean doEmpty() throws SQLException { return jdbcTemplate.queryForInt(""" SELECT COUNT() FROM ( - SELECT table_name FROM information_schema.tables WHERE table_schema = ? UNION + SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema = ? UNION SELECT table_name FROM information_schema.views WHERE table_schema = ? UNION - SELECT rountine_name FROM information_schema.routines WHERE routine_schema = ? + SELECT routine_name FROM information_schema.routines WHERE routine_schema = ? ) objs""", name, name, name) == 0; } @@ -43,7 +43,33 @@ protected void doDrop() throws SQLException { @Override protected void doClean() throws SQLException { + // drop views + List dropViewStatements = jdbcTemplate.queryForStringList( + "SELECT table_name FROM information_schema.views WHERE table_schema = ?", name) + .stream().map(viewName -> "DROP VIEW IF EXISTS "+database.quote(name, viewName)).toList(); + for (String stmt : dropViewStatements) { + jdbcTemplate.execute(stmt); + } + // drop tables + List dropTableStatements = jdbcTemplate.queryForStringList( + "SELECT table_name FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema = ?", name) + .stream().map(tableName -> "DROP TABLE IF EXISTS "+database.quote(name, tableName)).toList(); + for (String stmt : dropTableStatements) { + jdbcTemplate.execute(stmt); + } + + // drop functions + List dropFunctionStatements = jdbcTemplate.queryForList( + "SELECT routine_name, specific_name FROM information_schema.routines WHERE routine_schema = ?", name) + .stream().map(result -> { + String fnName = result.get("routine_name"); + String fnArgs = result.get("specific_name").substring(fnName.length()); + return "DROP FUNCTION IF EXISTS " + database.quote(name, fnName) + fnArgs; + }).toList(); + for (String stmt : dropFunctionStatements) { + jdbcTemplate.execute(stmt); + } } @Override