diff --git a/flyway-commandline/pom.xml b/flyway-commandline/pom.xml index 32c8d9fbbd..2114e28a55 100644 --- a/flyway-commandline/pom.xml +++ b/flyway-commandline/pom.xml @@ -153,6 +153,12 @@ ${project.version} runtime + + ${project.groupId} + flyway-database-clickhouse + ${project.version} + runtime + @@ -1084,4 +1090,4 @@ - \ No newline at end of file + diff --git a/flyway-commandline/src/main/assembly/component.xml b/flyway-commandline/src/main/assembly/component.xml index b3cdd9471c..ecc6d58285 100644 --- a/flyway-commandline/src/main/assembly/component.xml +++ b/flyway-commandline/src/main/assembly/component.xml @@ -199,6 +199,7 @@ org.flywaydb:flyway-database-ignite org.flywaydb:flyway-database-tidb org.flywaydb:flyway-database-yugabytedb + org.flywaydb:flyway-database-clickhouse org.flywaydb:flyway-sqlserver org.flywaydb:flyway-singlestore org.flywaydb:flyway-mysql @@ -250,4 +251,4 @@ - \ No newline at end of file + diff --git a/flyway-community-db-support/flyway-database-clickhouse/pom.xml b/flyway-community-db-support/flyway-database-clickhouse/pom.xml new file mode 100644 index 0000000000..5029d047f8 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/pom.xml @@ -0,0 +1,66 @@ + + + + 4.0.0 + + org.flywaydb + flyway-community-db-support + 10.0.0 + + + flyway-database-clickhouse + ${project.artifactId} + + + + ${project.groupId} + flyway-core + + + org.projectlombok + lombok + provided + + + com.clickhouse + clickhouse-jdbc + 0.5.0 + true + + + + + + + src/main/resources + true + + + + + maven-resources-plugin + + + maven-jar-plugin + + + + diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/ClickHouseDatabaseExtension.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/ClickHouseDatabaseExtension.java new file mode 100644 index 0000000000..406c3229e9 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/ClickHouseDatabaseExtension.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.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 ClickHouseDatabaseExtension implements PluginMetadata { + public String getDescription() { + return "Community-contributed Ignite database support extension " + readVersion() + " by Redgate"; + } + + private static String readVersion() { + try { + return FileUtils.copyToString( + ClickHouseDatabaseExtension.class.getClassLoader().getResourceAsStream("org/flywaydb/community/database/clickhouse/version.txt"), + StandardCharsets.UTF_8); + } catch (IOException e) { + throw new FlywayException("Unable to read extension version: " + e.getMessage(), e); + } + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConfigurationExtension.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConfigurationExtension.java new file mode 100644 index 0000000000..ae076407ca --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConfigurationExtension.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +import lombok.Getter; +import org.flywaydb.core.extensibility.ConfigurationExtension; + +import java.util.Map; + +@Getter +public class ClickHouseConfigurationExtension implements ConfigurationExtension { + private static final String CLUSTER_NAME = "flyway.clickhouse.clusterName"; + + private String clusterName; + + @Override + public String getNamespace() { + return "clickhouse"; + } + + @Override + public void extractParametersFromConfiguration(Map configuration) { + String clusterName = configuration.remove(CLUSTER_NAME); + if (clusterName != null) { + this.clusterName = clusterName; + } + } + + @Override + public String getConfigurationParameterFromEnvironmentVariable(String environmentVariable) { + if ("FLYWAY_CLICKHOUSE_CLUSTER_NAME".equals(environmentVariable)) { + return CLUSTER_NAME; + } + return null; + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConnection.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConnection.java new file mode 100644 index 0000000000..d3ebd1e25f --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseConnection.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +import org.flywaydb.core.internal.database.base.Connection; + +import java.sql.SQLException; +import java.util.Optional; + +public class ClickHouseConnection extends Connection { + ClickHouseConnection(ClickHouseDatabase database, java.sql.Connection connection) { + super(database, connection); + } + + @Override + protected String getCurrentSchemaNameOrSearchPath() throws SQLException { + return Optional.ofNullable(getJdbcTemplate().getConnection().getSchema()).map(database::unQuote).orElse(null); + } + + @Override + public void doChangeCurrentSchemaOrSearchPathTo(String schema) throws SQLException { + getJdbcTemplate().getConnection().setSchema(schema); + } + + @Override + public ClickHouseSchema getSchema(String name) { + return new ClickHouseSchema(jdbcTemplate, database, name); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabase.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabase.java new file mode 100644 index 0000000000..4afafddda0 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabase.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +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 org.flywaydb.core.internal.util.StringUtils; + +import java.sql.Connection; + +public class ClickHouseDatabase extends Database { + @Override + public boolean useSingleConnection() { + return true; + } + + public ClickHouseDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) { + super(configuration, jdbcConnectionFactory, statementInterceptor); + } + + public String getClusterName() { + return configuration.getPluginRegister().getPlugin(ClickHouseConfigurationExtension.class).getClusterName(); + } + + @Override + protected ClickHouseConnection doGetConnection(Connection connection) { + return new ClickHouseConnection(this, connection); + } + + @Override + public void ensureSupported(Configuration configuration) { + } + + @Override + public boolean supportsDdlTransactions() { + return false; + } + + @Override + public boolean supportsMultiStatementTransactions() { + return false; + } + + @Override + public String getBooleanTrue() { + return "1"; + } + + @Override + public String getBooleanFalse() { + return "0"; + } + + @Override + public boolean catalogIsSchema() { + return true; + } + + @Override + public String getRawCreateScript(Table table, boolean baseline) { + String clusterName = getClusterName(); + boolean isClustered = StringUtils.hasText(clusterName); + + String script = "CREATE TABLE IF NOT EXISTS " + table + (isClustered ? (" ON CLUSTER " + clusterName) : "") + "(" + + " installed_rank Int32," + + " version Nullable(String)," + + " description String," + + " type String," + + " script String," + + " checksum Nullable(Int32)," + + " installed_by String," + + " installed_on DateTime DEFAULT now()," + + " execution_time Int32," + + " success Bool" + + ")"; + + String engine; + + if (isClustered) { + engine = "ReplicatedMergeTree('/clickhouse/tables/{shard}/{database}/{table}', '{replica}')"; + } else { + engine = "MergeTree"; + } + + script += " ENGINE = " + engine + + " PRIMARY KEY (script);"; + + return script + (baseline ? getBaselineStatement(table) + ";" : ""); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabaseType.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabaseType.java new file mode 100644 index 0000000000..1f940b0e09 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseDatabaseType.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +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.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; + +public class ClickHouseDatabaseType extends BaseDatabaseType { + @Override + public String getName() { + return "ClickHouse"; + } + + @Override + public int getNullType() { + return 0; + } + + @Override + public boolean handlesJDBCUrl(String url) { + return url.startsWith("jdbc:clickhouse:"); + } + + @Override + public String getDriverClass(String url, ClassLoader classLoader) { + return "com.clickhouse.jdbc.ClickHouseDriver"; + } + + @Override + public String getBackupDriverClass(String url, ClassLoader classLoader) { + return "ru.yandex.clickhouse.ClickHouseDriver"; + } + + @Override + public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) { + return databaseProductName.startsWith("ClickHouse"); + } + + @Override + public ClickHouseDatabase createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) { + return new ClickHouseDatabase(configuration, jdbcConnectionFactory, statementInterceptor); + } + + @Override + public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) { + return new ClickHouseParser(configuration, parsingContext, 3); + } + + @Override + public boolean detectUserRequiredByUrl(String url) { + return !url.contains("user="); + } + + @Override + public boolean detectPasswordRequiredByUrl(String url) { + return !url.contains("password="); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseParser.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseParser.java new file mode 100644 index 0000000000..3d4532bf55 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseParser.java @@ -0,0 +1,26 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +import org.flywaydb.core.api.configuration.Configuration; +import org.flywaydb.core.internal.parser.Parser; +import org.flywaydb.core.internal.parser.ParsingContext; + +public class ClickHouseParser extends Parser { + protected ClickHouseParser(Configuration configuration, ParsingContext parsingContext, int peekDepth) { + super(configuration, parsingContext, peekDepth); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseSchema.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseSchema.java new file mode 100644 index 0000000000..e1fbcafef4 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseSchema.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +import org.flywaydb.core.internal.database.base.Schema; +import org.flywaydb.core.internal.jdbc.JdbcTemplate; + +import java.sql.SQLException; + +public class ClickHouseSchema extends Schema { + /** + * @param jdbcTemplate The Jdbc Template for communicating with the DB. + * @param database The database-specific support. + * @param name The name of the schema. + */ + public ClickHouseSchema(JdbcTemplate jdbcTemplate, ClickHouseDatabase database, String name) { + super(jdbcTemplate, database, name); + } + + @Override + protected boolean doExists() throws SQLException { + int i = jdbcTemplate.queryForInt("SELECT COUNT() FROM system.databases WHERE name = ?", name); + return i > 0; + } + + @Override + protected boolean doEmpty() throws SQLException { + int i = jdbcTemplate.queryForInt("SELECT COUNT() FROM system.tables WHERE database = ?", name); + return i == 0; + } + + @Override + protected void doCreate() throws SQLException { + jdbcTemplate.executeStatement("CREATE DATABASE " + database.quote(name)); + } + + @Override + protected void doDrop() throws SQLException { + if (jdbcTemplate.getConnection().getSchema().equals(name)) { + jdbcTemplate.getConnection().setSchema("default"); + } + jdbcTemplate.executeStatement("DROP DATABASE " + database.quote(name)); + } + + @Override + protected void doClean() throws SQLException { + for (ClickHouseTable table : allTables()) { + table.drop(); + } + } + + @Override + protected ClickHouseTable[] doAllTables() throws SQLException { + return jdbcTemplate.queryForStringList("SELECT name FROM system.tables WHERE database = ?", name) + .stream() + .map(this::getTable) + .toArray(ClickHouseTable[]::new); + } + + @Override + public ClickHouseTable getTable(String tableName) { + return new ClickHouseTable(jdbcTemplate, database, this, tableName); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseTable.java b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseTable.java new file mode 100644 index 0000000000..40c18e43b1 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/java/org/flywaydb/community/database/clickhouse/ClickHouseTable.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) Red Gate Software Ltd 2010-2023 + * + * 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.flywaydb.community.database.clickhouse; + +import lombok.CustomLog; +import org.flywaydb.core.internal.database.base.Table; +import org.flywaydb.core.internal.jdbc.JdbcTemplate; +import org.flywaydb.core.internal.util.StringUtils; + +import java.sql.SQLException; + +@CustomLog +public class ClickHouseTable extends Table { + /** + * @param jdbcTemplate The JDBC template for communicating with the DB. + * @param database The database-specific support. + * @param schema The schema this table lives in. + * @param name The name of the table. + */ + public ClickHouseTable(JdbcTemplate jdbcTemplate, ClickHouseDatabase database, ClickHouseSchema schema, String name) { + super(jdbcTemplate, database, schema, name); + } + + @Override + protected void doDrop() throws SQLException { + String clusterName = database.getClusterName(); + + jdbcTemplate.executeStatement("DROP TABLE " + this + (StringUtils.hasText(clusterName) ? (" ON CLUSTER " + clusterName) : "")); + } + + @Override + protected boolean doExists() throws SQLException { + int count = jdbcTemplate.queryForInt("SELECT COUNT() FROM system.tables WHERE database = ? AND name = ?", schema.getName(), name); + return count > 0; + } + + @Override + protected void doLock() throws SQLException { + LOG.debug("Unable to lock " + this + " as ClickHouse does not support locking. No concurrent migration supported."); + } +} diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin b/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin new file mode 100644 index 0000000000..628c219f16 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin @@ -0,0 +1,2 @@ +org.flywaydb.community.database.clickhouse.ClickHouseConfigurationExtension +org.flywaydb.community.database.clickhouse.ClickHouseDatabaseType diff --git a/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/org/flywaydb/community/database/clickhouse/version.txt b/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/org/flywaydb/community/database/clickhouse/version.txt new file mode 100644 index 0000000000..17851514a7 --- /dev/null +++ b/flyway-community-db-support/flyway-database-clickhouse/src/main/resources/org/flywaydb/community/database/clickhouse/version.txt @@ -0,0 +1 @@ +${pom.version} \ No newline at end of file diff --git a/flyway-community-db-support/pom.xml b/flyway-community-db-support/pom.xml index 4b2b410c53..aebbd6b5e4 100644 --- a/flyway-community-db-support/pom.xml +++ b/flyway-community-db-support/pom.xml @@ -33,6 +33,7 @@ flyway-database-tidb flyway-database-ignite flyway-database-yugabytedb + flyway-database-clickhouse @@ -88,4 +89,4 @@ - \ No newline at end of file +