diff --git a/flyway-database-kingbase/pom.xml b/flyway-database-kingbase/pom.xml
new file mode 100644
index 0000000..0abfe8b
--- /dev/null
+++ b/flyway-database-kingbase/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+ 4.0.0
+
+ org.flywaydb
+ flyway-community-db-support
+ 10.16.1
+
+
+ flyway-database-kingbase
+ ${project.artifactId}
+
+
+
+ ${project.groupId}
+ flyway-core
+
+
+ ${project.groupId}
+ flyway-database-postgresql
+ ${version.flyway}
+
+
+ org.flywaydb
+ 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-kingbase/src/main/java/org/flywaydb/community/database/KingbaseDatabaseExtension.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/KingbaseDatabaseExtension.java
new file mode 100644
index 0000000..5fb3923
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/KingbaseDatabaseExtension.java
@@ -0,0 +1,59 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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 KingbaseDatabaseExtension implements PluginMetadata {
+ public String getDescription() {
+ return "Community-contributed kingbase database support extension " + readVersion() + " by Redgate";
+ }
+
+ public static String readVersion() {
+ try {
+ return FileUtils.copyToString(
+ KingbaseDatabaseExtension.class.getClassLoader().getResourceAsStream("org/flywaydb/community" +
+ "/database/kingbase/version.txt"),
+ StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new FlywayException("Unable to read extension version: " + e.getMessage(), e);
+ }
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlConnection.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlConnection.java
new file mode 100644
index 0000000..8663a17
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlConnection.java
@@ -0,0 +1,58 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import org.flywaydb.core.internal.database.base.Schema;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.database.postgresql.PostgreSQLConnection;
+
+import java.util.concurrent.Callable;
+
+public class KingbaseMysqlConnection extends PostgreSQLConnection {
+
+ KingbaseMysqlConnection(KingbaseMysqlDatabase database, java.sql.Connection connection) {
+ super(database, connection);
+ }
+
+ @Override
+ public Schema getSchema(String name) {
+ return new KingbaseMysqlSchema(jdbcTemplate, (KingbaseMysqlDatabase) database, name);
+ }
+
+ @Override
+ public T lock(Table table, Callable callable) {
+ return new KingbaseMysqlExecutionTemplate(jdbcTemplate, table.toString()).execute(callable);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabase.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabase.java
new file mode 100644
index 0000000..65d53f7
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabase.java
@@ -0,0 +1,136 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
+import org.flywaydb.core.internal.jdbc.StatementInterceptor;
+import org.flywaydb.database.postgresql.PostgreSQLDatabase;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+
+@CustomLog
+public class KingbaseMysqlDatabase extends PostgreSQLDatabase {
+
+ public static final String LOCK_TABLE_NAME = "YB_FLYWAY_LOCK_TABLE";
+ /**
+ * This table is used to enforce locking through SELECT ... FOR UPDATE on a
+ * token row inserted in this table. The token row is inserted with the name
+ * of the Flyway's migration history table as a token for simplicity.
+ */
+ private static final String CREATE_LOCK_TABLE_DDL = "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE_NAME + " (table_name varchar PRIMARY KEY, locked bool)";
+
+ public KingbaseMysqlDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ super(configuration, jdbcConnectionFactory, statementInterceptor);
+ createLockTable();
+ }
+
+ @Override
+ protected KingbaseMysqlConnection doGetConnection(Connection connection) {
+ return new KingbaseMysqlConnection(this, connection);
+ }
+
+ @Override
+ public void ensureSupported(Configuration configuration) {
+ // Checks the Postgres version
+ ensureDatabaseIsRecentEnough("11.2");
+ }
+
+ @Override
+ public boolean supportsDdlTransactions() {
+ return false;
+ }
+
+ @Override
+ public String getRawCreateScript(Table table, boolean baseline) {
+ return "CREATE TABLE IF NOT EXISTS " + table + " (\n" +
+ " \"installed_rank\" INT NOT NULL PRIMARY KEY,\n" +
+ " \"version\" VARCHAR(50),\n" +
+ " \"description\" VARCHAR(200) NOT NULL,\n" +
+ " \"type\" VARCHAR(20) NOT NULL,\n" +
+ " \"script\" VARCHAR(1000) NOT NULL,\n" +
+ " \"checksum\" INTEGER,\n" +
+ " \"installed_by\" VARCHAR(100) NOT NULL,\n" +
+ " \"installed_on\" TIMESTAMP NOT NULL DEFAULT now(),\n" +
+ " \"execution_time\" INTEGER NOT NULL,\n" +
+ " \"success\" BOOLEAN NOT NULL\n" +
+ ");\n" +
+ (baseline ? getBaselineStatement(table) + ";\n" : "") +
+ "CREATE INDEX IF NOT EXISTS \"" + table.getName() + "_s_idx\" ON " + table + " (\"success\");";
+ }
+
+
+ @Override
+ public boolean useSingleConnection() {
+ return true;
+ }
+
+ @Override
+ public String getBooleanTrue() {
+ return "1";
+ }
+
+ @Override
+ public String getBooleanFalse() {
+ return "0";
+ }
+ @Override
+ public String getOpenQuote() {
+ return "`";
+ }
+
+ @Override
+ public String getCloseQuote() {
+ return "`";
+ }
+
+ @Override
+ public boolean catalogIsSchema() {
+ return true;
+ }
+
+ private void createLockTable() {
+ try {
+ jdbcTemplate.execute(CREATE_LOCK_TABLE_DDL);
+ } catch (SQLException e) {
+ throw new FlywaySqlException("Unable to initialize the lock table", e);
+ }
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabaseType.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabaseType.java
new file mode 100644
index 0000000..bbeaefb
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlDatabaseType.java
@@ -0,0 +1,144 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import org.flywaydb.community.database.KingbaseDatabaseExtension;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.api.ResourceProvider;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.extensibility.Tier;
+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.license.FlywayEditionUpgradeRequiredException;
+import org.flywaydb.core.internal.parser.Parser;
+import org.flywaydb.core.internal.parser.ParsingContext;
+import org.flywaydb.database.postgresql.PostgreSQLDatabaseType;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class KingbaseMysqlDatabaseType extends PostgreSQLDatabaseType implements CommunityDatabaseType {
+ @Override
+ public String getName() {
+ return "Kingbase";
+ }
+
+ @Override
+ public boolean handlesJDBCUrl(String url) {
+ if (url.startsWith("jdbc-secretsmanager:kingbase8:")) {
+
+ throw new FlywayEditionUpgradeRequiredException(Tier.ENTERPRISE, (Tier) null, "jdbc-secretsmanager");
+
+ }
+ return url.startsWith("jdbc:kingbase8:") || url.startsWith("jdbc:p6spy:kingbase8:");
+ }
+
+
+ @Override
+ public int getPriority() {
+ // Should be checked before plain PostgreSQL
+ return 1;
+ }
+
+// @Override
+// public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+// // The YB is what distinguishes Yugabyte
+// return databaseProductName.startsWith("Kingbase");
+// }
+
+ @Override
+ public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+ if (!databaseProductName.contains("Kingbase")) {
+ return false; // 先确保是 Kingbase 数据库
+ }
+
+ boolean result = checkDatabaseMode(connection, "mysql");
+// System.out.println("Checking Kingbase MySQL mode: " + result + ", databaseProductName: " + databaseProductName);
+ return result;
+ }
+
+ private boolean checkDatabaseMode(Connection connection, String expectedMode) {
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery("SHOW DATABASE_MODE;")) {
+
+ if (rs.next()) {
+ String mode = rs.getString(1).trim().toLowerCase();
+ return expectedMode.equals(mode);
+ }
+ } catch (SQLException e) {
+ throw new FlywayException("Failed to determine Kingbase database mode", e);
+ }
+ return false;
+ }
+
+
+ @Override
+ public Database createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ return new KingbaseMysqlDatabase(configuration, jdbcConnectionFactory, statementInterceptor);
+ }
+
+ @Override
+ public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) {
+ return new KingbaseMysqlParser(configuration, parsingContext);
+ }
+
+ @Override
+ public String getPluginVersion(Configuration config) {
+ return KingbaseDatabaseExtension.readVersion();
+ }
+
+ /**
+ * Returns the YugabyteDB Smart driver classname if the smart driver is
+ * being used. The plugin will work with the Postgresql JDBC driver also
+ * since the url in that case would start with 'jdbc:postgresql' which would
+ * return the PG JDBC driver class name.
+ * @param url
+ * @param classLoader
+ * @return "com.yugabyte.Driver" if url starts with "jdbc:yugabytedb:"
+ */
+ @Override
+ public String getDriverClass(String url, ClassLoader classLoader) {
+
+ if (url.startsWith("jdbc:p6spy:kingbase8:")) {
+ return "com.p6spy.engine.spy.P6SpyDriver";
+ }
+ return "com.kingbase8.Driver";
+ }
+
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlExecutionTemplate.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlExecutionTemplate.java
new file mode 100644
index 0000000..f0b8b93
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlExecutionTemplate.java
@@ -0,0 +1,188 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+package org.flywaydb.community.database.kingbasemysql;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.core.internal.strategy.RetryStrategy;
+import org.flywaydb.core.internal.util.FlywayDbWebsiteLinks;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+@CustomLog
+public class KingbaseMysqlExecutionTemplate {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final String tableName;
+ private static final Map tableEntries = new ConcurrentHashMap<>();
+
+ KingbaseMysqlExecutionTemplate(JdbcTemplate jdbcTemplate, String tableName) {
+ this.jdbcTemplate = jdbcTemplate;
+ this.tableName = tableName;
+ }
+
+ public T execute(Callable callable) {
+ Exception error = null;
+ try {
+ lock();
+ return callable.call();
+ } catch (RuntimeException e) {
+ error = e;
+ throw e;
+ } catch (Exception e) {
+ error = e;
+ throw new FlywayException(e);
+ } finally {
+ unlock(error);
+ }
+ }
+
+ private void lock() throws SQLException {
+ RetryStrategy strategy = new RetryStrategy();
+ strategy.doWithRetries(this::tryLock, "Interrupted while attempting to acquire lock through SELECT ... FOR UPDATE",
+ "Number of retries exceeded while attempting to acquire lock through SELECT ... FOR UPDATE. " +
+ "Configure the number of retries with the 'lockRetryCount' configuration option: " + FlywayDbWebsiteLinks.LOCK_RETRY_COUNT);
+
+ }
+
+ private boolean tryLock() {
+ Exception exception = null;
+ boolean txStarted = false, success = false;
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+
+ if (!tableEntries.containsKey(tableName)) {
+ try {
+ statement.executeUpdate("INSERT INTO "
+ + KingbaseMysqlDatabase.LOCK_TABLE_NAME
+ + " VALUES ('" + tableName + "', 'false')");
+ tableEntries.put(tableName, true);
+ LOG.info(Thread.currentThread().getName() + "> Inserted a token row for " + tableName + " in " + KingbaseMysqlDatabase.LOCK_TABLE_NAME);
+ } catch (SQLException e) {
+ if ("23505".equals(e.getSQLState())) {
+ // 23505 == UNIQUE_VIOLATION
+ LOG.debug(Thread.currentThread().getName() + "> Token row already added for " + tableName);
+ } else {
+ throw new FlywaySqlException("Could not add token row for " + tableName + " in table " + KingbaseMysqlDatabase.LOCK_TABLE_NAME, e);
+ }
+ }
+ }
+
+ boolean locked;
+ String selectForUpdate = "SELECT locked FROM "
+ + KingbaseMysqlDatabase.LOCK_TABLE_NAME
+ + " WHERE table_name = '"
+ + tableName
+ + "' FOR UPDATE";
+ String updateLocked = "UPDATE " + KingbaseMysqlDatabase.LOCK_TABLE_NAME
+ + " SET locked = true WHERE table_name = '"
+ + tableName + "'";
+
+ statement.execute("BEGIN");
+ txStarted = true;
+ ResultSet rs = statement.executeQuery(selectForUpdate);
+ if (rs.next()) {
+ locked = rs.getBoolean("locked");
+
+ if (locked) {
+ statement.execute("COMMIT");
+ txStarted = false;
+ LOG.debug(Thread.currentThread().getName() + "> Another Flyway operation is in progress. Allowing it to complete");
+ } else {
+ LOG.debug(Thread.currentThread().getName() + "> Setting locked = true");
+ statement.executeUpdate(updateLocked);
+ success = true;
+ }
+ } else {
+ // For some reason the record was not found, retry
+ tableEntries.remove(tableName);
+ }
+
+ } catch (SQLException e) {
+ LOG.warn(Thread.currentThread().getName() + "> Unable to perform lock action, SQLState: " + e.getSQLState());
+ if (!"40001".equalsIgnoreCase(e.getSQLState())) {
+ exception = new FlywaySqlException("Unable to perform lock action", e);
+ throw (FlywaySqlException) exception;
+ } // else retry
+ } finally {
+ if (txStarted) {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = true");
+ } catch (SQLException e) {
+ if (exception == null) {
+ throw new FlywaySqlException("Failed to commit the tx to set locked = true", e);
+ }
+ LOG.warn(Thread.currentThread().getName() + "> Failed to commit the tx to set locked = true: " + e);
+ }
+ }
+ }
+ return success;
+ }
+
+ private void unlock(Exception rethrow) {
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+ statement.execute("BEGIN");
+ ResultSet rs = statement.executeQuery("SELECT locked FROM " + KingbaseMysqlDatabase.LOCK_TABLE_NAME + " WHERE table_name = '" + tableName + "' FOR UPDATE");
+
+ if (rs.next()) {
+ boolean locked = rs.getBoolean("locked");
+ if (locked) {
+ statement.executeUpdate("UPDATE " + KingbaseMysqlDatabase.LOCK_TABLE_NAME + " SET locked = false WHERE table_name = '" + tableName + "'");
+ } else {
+ // Unexpected. This may happen only when callable took too long to complete
+ // and another thread forcefully reset it.
+ String msg = "Unlock failed but the Flyway operation may have succeeded. Check your Flyway operation before re-trying";
+ LOG.warn(Thread.currentThread().getName() + "> " + msg);
+ if (rethrow == null) {
+ throw new FlywayException(msg);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ rethrow = new FlywayException("Unable to perform unlock action", e);
+ throw (FlywaySqlException) rethrow;
+ }
+ LOG.warn("Unable to perform unlock action " + e);
+ } finally {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = false");
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ throw new FlywaySqlException("Failed to commit unlock action", e);
+ }
+ LOG.warn("Failed to commit unlock action: " + e);
+ }
+ }
+ }
+
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlParser.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlParser.java
new file mode 100644
index 0000000..417cc47
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlParser.java
@@ -0,0 +1,186 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.parser.*;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import static java.lang.Character.isDigit;
+
+public class KingbaseMysqlParser extends Parser {
+ private static final char ALTERNATIVE_SINGLE_LINE_COMMENT = '#';
+
+ private static final Pattern STORED_PROGRAM_REGEX = Pattern.compile(
+ "^CREATE\\s(((DEFINER\\s(\\w+\\s)?@\\s(\\w+\\s)?)?(PROCEDURE|FUNCTION|EVENT))|TRIGGER)", Pattern.CASE_INSENSITIVE);
+ private static final StatementType STORED_PROGRAM_STATEMENT = new StatementType();
+
+ public KingbaseMysqlParser(Configuration configuration, ParsingContext parsingContext) {
+ super(configuration, parsingContext, 8);
+ }
+
+ @Override
+ protected void resetDelimiter(ParserContext context) {
+ // Do not reset delimiter as delimiter changes survive beyond a single statement
+ }
+
+ @Override
+ protected Token handleKeyword(PeekingReader reader, ParserContext context, int pos, int line, int col, String keyword) throws IOException {
+ if ("DELIMITER".equalsIgnoreCase(keyword)) {
+ String text = "";
+ while (text.isEmpty()) {
+ text = reader.readUntilExcluding('\n', '\r').trim();
+ reader.swallow(1);
+ }
+ return new Token(TokenType.NEW_DELIMITER, pos, line, col, text, text, context.getParensDepth());
+ }
+ return super.handleKeyword(reader, context, pos, line, col, keyword);
+ }
+
+ @Override
+ protected char getIdentifierQuote() {
+ return '`';
+ }
+
+ @Override
+ protected char getAlternativeStringLiteralQuote() {
+ return '"';
+ }
+
+ @Override
+ protected boolean isSingleLineComment(String peek, ParserContext context, int col) {
+ return (super.isSingleLineComment(peek, context, col)
+ // Normally MySQL treats # as a comment, but this may have been overridden by DELIMITER # directive
+ || (peek.charAt(0) == ALTERNATIVE_SINGLE_LINE_COMMENT && !isDelimiter(peek, context, col, 0)));
+ }
+
+ @Override
+ protected Token handleStringLiteral(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException, IOException {
+ reader.swallow();
+ reader.swallowUntilIncludingWithEscape('\'', true, '\\');
+ return new Token(TokenType.STRING, pos, line, col, null, null, context.getParensDepth());
+ }
+
+ @Override
+ protected Token handleAlternativeStringLiteral(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException {
+ reader.swallow();
+ reader.swallowUntilIncludingWithEscape('"', true, '\\');
+ return new Token(TokenType.STRING, pos, line, col, null, null, context.getParensDepth());
+ }
+
+ @Override
+ protected Token handleCommentDirective(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException {
+ reader.swallow(2);
+ String text = reader.readUntilExcluding("*/");
+ reader.swallow(2);
+ return new Token(TokenType.MULTI_LINE_COMMENT_DIRECTIVE, pos, line, col, text, text, context.getParensDepth());
+ }
+
+ @Override
+ protected boolean isCommentDirective(String text) {
+ return text.length() >= 8
+ && text.charAt(0) == '/'
+ && text.charAt(1) == '*'
+ && text.charAt(2) == '!'
+ && isDigit(text.charAt(3))
+ && isDigit(text.charAt(4))
+ && isDigit(text.charAt(5))
+ && isDigit(text.charAt(6))
+ && isDigit(text.charAt(7));
+ }
+
+ @Override
+ protected StatementType detectStatementType(String simplifiedStatement, ParserContext context, PeekingReader reader) {
+ if (STORED_PROGRAM_REGEX.matcher(simplifiedStatement).matches()) {
+ return STORED_PROGRAM_STATEMENT;
+ }
+
+ return super.detectStatementType(simplifiedStatement, context, reader);
+ }
+
+ @Override
+ protected boolean shouldAdjustBlockDepth(ParserContext context, List tokens, Token token) {
+ // we assume that any blocks opened or closed inside some parens
+ // can't affect block depth outside those parens
+ return token.getParensDepth() == 0;
+ }
+
+ @Override
+ protected void adjustBlockDepth(ParserContext context, List tokens, Token keyword, PeekingReader reader) {
+ String keywordText = keyword.getText();
+
+ int parensDepth = keyword.getParensDepth();
+
+ if ("BEGIN".equalsIgnoreCase(keywordText) && context.getStatementType() == STORED_PROGRAM_STATEMENT) {
+ // BEGIN ... END is the usual way to define a nested block
+ context.increaseBlockDepth("");
+ }
+
+ if ("CASE".equalsIgnoreCase(keywordText)) {
+ // CASE is treated specially compared to IF or LOOP since it can either be
+ // a statement or an expression. CASE statements are terminated with END CASE,
+ // while CASE expressions are only terminated with END.
+ //
+ // We need to decide if some END token ends a CASE statement or a block. Since
+ // we can't easily tell if we're in a CASE statement or expression, we don't
+ // know if we should be expecting END or END CASE. So we'll just assume that
+ // END always closes a CASE, and then prevent END CASE from starting
+ // a new one:
+ if (!lastTokenIs(tokens, parensDepth, "END")) {
+ context.increaseBlockDepth("");
+ }
+
+ // we could do something similar for the other control flow keywords, but IF and
+ // REPEAT in particular would be tricky since they are also the names of functions
+ // (and functions don't have a matching END keyword).
+ }
+
+ // END always ends a block, unless it's part of END IF or END LOOP etc
+ //
+ // this is a little tricky since we can't peek ahead at tokens, so we have to
+ // wait until one token *after* the END and decrease the block depth there.
+ if (context.getBlockDepth() > 0
+ && lastTokenIs(tokens, parensDepth, "END")
+ && !"IF".equalsIgnoreCase(keywordText)
+ && !"LOOP".equalsIgnoreCase(keywordText)
+ && !"REPEAT".equalsIgnoreCase(keywordText)
+ && !"WHILE".equalsIgnoreCase(keywordText)) {
+ context.decreaseBlockDepth();
+ }
+ }
+}
\ No newline at end of file
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlSchema.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlSchema.java
new file mode 100644
index 0000000..11f5d38
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlSchema.java
@@ -0,0 +1,55 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLSchema;
+
+public class KingbaseMysqlSchema extends PostgreSQLSchema {
+ /**
+ * @param jdbcTemplate The Jdbc Template for communicating with the DB.
+ * @param database The database-specific support.
+ * @param name The name of the schema.
+ */
+ public KingbaseMysqlSchema(JdbcTemplate jdbcTemplate, KingbaseMysqlDatabase database, String name) {
+ super(jdbcTemplate, database, name);
+ }
+
+ @Override
+ public Table getTable(String tableName) {
+ return new KingbaseMysqlTable(jdbcTemplate, (KingbaseMysqlDatabase) database, this, tableName);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlTable.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlTable.java
new file mode 100644
index 0000000..473e497
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/KingbaseMysqlTable.java
@@ -0,0 +1,50 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasemysql;
+
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLTable;
+
+public class KingbaseMysqlTable extends PostgreSQLTable {
+ /**
+ * @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 KingbaseMysqlTable(JdbcTemplate jdbcTemplate, KingbaseMysqlDatabase database, KingbaseMysqlSchema schema, String name) {
+ super(jdbcTemplate, database, schema, name);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/package-info.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/package-info.java
new file mode 100644
index 0000000..7b5b662
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasemysql/package-info.java
@@ -0,0 +1,23 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-postgresql
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/**
+ * Private API. No compatibility guarantees provided.
+ */
+package org.flywaydb.community.database.kingbasemysql;
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleConnection.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleConnection.java
new file mode 100644
index 0000000..2e3003e
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleConnection.java
@@ -0,0 +1,58 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import org.flywaydb.core.internal.database.base.Schema;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.database.postgresql.PostgreSQLConnection;
+
+import java.util.concurrent.Callable;
+
+public class KingbaseOracleConnection extends PostgreSQLConnection {
+
+ KingbaseOracleConnection(KingbaseOracleDatabase database, java.sql.Connection connection) {
+ super(database, connection);
+ }
+
+ @Override
+ public Schema getSchema(String name) {
+ return new KingbaseOracleSchema(jdbcTemplate, (KingbaseOracleDatabase) database, name);
+ }
+
+ @Override
+ public T lock(Table table, Callable callable) {
+ return new KingbaseOracleExecutionTemplate(jdbcTemplate, table.toString()).execute(callable);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabase.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabase.java
new file mode 100644
index 0000000..3c2aa87
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabase.java
@@ -0,0 +1,118 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
+import org.flywaydb.core.internal.jdbc.StatementInterceptor;
+import org.flywaydb.database.postgresql.PostgreSQLDatabase;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+
+@CustomLog
+public class KingbaseOracleDatabase extends PostgreSQLDatabase {
+
+ public static final String LOCK_TABLE_NAME = "YB_FLYWAY_LOCK_TABLE";
+ /**
+ * This table is used to enforce locking through SELECT ... FOR UPDATE on a
+ * token row inserted in this table. The token row is inserted with the name
+ * of the Flyway's migration history table as a token for simplicity.
+ */
+ private static final String CREATE_LOCK_TABLE_DDL = "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE_NAME + " (table_name varchar PRIMARY KEY, locked bool)";
+
+ public KingbaseOracleDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ super(configuration, jdbcConnectionFactory, statementInterceptor);
+ createLockTable();
+ }
+
+ @Override
+ protected KingbaseOracleConnection doGetConnection(Connection connection) {
+ return new KingbaseOracleConnection(this, connection);
+ }
+
+ @Override
+ public void ensureSupported(Configuration configuration) {
+ // Checks the Postgres version
+ ensureDatabaseIsRecentEnough("11.2");
+ }
+
+ @Override
+ public boolean supportsDdlTransactions() {
+ return false;
+ }
+
+ @Override
+ public String getRawCreateScript(Table table, boolean baseline) {
+ String tablespace = configuration.getTablespace() == null
+ ? ""
+ : " TABLESPACE \"" + configuration.getTablespace() + "\"";
+
+ return "CREATE TABLE " + table + " (\n" +
+ " \"installed_rank\" INT NOT NULL,\n" +
+ " \"version\" VARCHAR2(50),\n" +
+ " \"description\" VARCHAR2(200) NOT NULL,\n" +
+ " \"type\" VARCHAR2(20) NOT NULL,\n" +
+ " \"script\" VARCHAR2(1000) NOT NULL,\n" +
+ " \"checksum\" INT,\n" +
+ " \"installed_by\" VARCHAR2(100) NOT NULL,\n" +
+ " \"installed_on\" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,\n" +
+ " \"execution_time\" INT NOT NULL,\n" +
+ " \"success\" NUMBER(1) NOT NULL,\n" +
+ " CONSTRAINT \"" + table.getName() + "_pk\" PRIMARY KEY (\"installed_rank\")\n" +
+ ")" + tablespace + ";\n" +
+ (baseline ? getBaselineStatement(table) + ";\n" : "") +
+ "CREATE INDEX \"" + table.getName() + "_s_idx\" ON " + table + " (\"success\");";
+ }
+
+
+
+ @Override
+ public boolean useSingleConnection() {
+ return true;
+ }
+
+ private void createLockTable() {
+ try {
+ jdbcTemplate.execute(CREATE_LOCK_TABLE_DDL);
+ } catch (SQLException e) {
+ throw new FlywaySqlException("Unable to initialize the lock table", e);
+ }
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabaseType.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabaseType.java
new file mode 100644
index 0000000..c8951af
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleDatabaseType.java
@@ -0,0 +1,143 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import org.flywaydb.community.database.KingbaseDatabaseExtension;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.api.ResourceProvider;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.extensibility.Tier;
+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.license.FlywayEditionUpgradeRequiredException;
+import org.flywaydb.core.internal.parser.Parser;
+import org.flywaydb.core.internal.parser.ParsingContext;
+import org.flywaydb.database.postgresql.PostgreSQLDatabaseType;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+public class KingbaseOracleDatabaseType extends PostgreSQLDatabaseType implements CommunityDatabaseType {
+ @Override
+ public String getName() {
+ return "Kingbase";
+ }
+
+ @Override
+ public boolean handlesJDBCUrl(String url) {
+ if (url.startsWith("jdbc-secretsmanager:kingbase8:")) {
+
+
+ throw new FlywayEditionUpgradeRequiredException(Tier.ENTERPRISE, (Tier) null, "jdbc-secretsmanager");
+
+ }
+ return url.startsWith("jdbc:kingbase8:") || url.startsWith("jdbc:p6spy:kingbase8:");
+ }
+
+ @Override
+ public int getPriority() {
+ // Should be checked before plain PostgreSQL
+ return 1;
+ }
+
+// @Override
+// public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+// // The YB is what distinguishes Yugabyte
+// return databaseProductName.startsWith("Kingbase");
+// }
+
+ @Override
+ public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+ if (!databaseProductName.contains("Kingbase")) {
+ return false; // 先确保是 Kingbase 数据库
+ }
+
+ boolean result = checkDatabaseMode(connection, "oracle");
+// System.out.println("Checking Kingbase Oracle mode: " + result + ", databaseProductName: " + databaseProductName);
+ return result;
+ }
+
+ private boolean checkDatabaseMode(Connection connection, String expectedMode) {
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery("SHOW DATABASE_MODE;")) {
+
+ if (rs.next()) {
+ String mode = rs.getString(1).trim().toLowerCase();
+ return expectedMode.equals(mode);
+ } } catch (SQLException e) {
+ throw new FlywayException("Failed to determine Kingbase database mode", e);
+ }
+ return false;
+ }
+
+
+ @Override
+ public Database createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ return new KingbaseOracleDatabase(configuration, jdbcConnectionFactory, statementInterceptor);
+ }
+
+ @Override
+ public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) {
+ return new KingbaseOracleParser(configuration, parsingContext);
+ }
+
+ @Override
+ public String getPluginVersion(Configuration config) {
+ return KingbaseDatabaseExtension.readVersion();
+ }
+
+ /**
+ * Returns the YugabyteDB Smart driver classname if the smart driver is
+ * being used. The plugin will work with the Postgresql JDBC driver also
+ * since the url in that case would start with 'jdbc:postgresql' which would
+ * return the PG JDBC driver class name.
+ * @param url
+ * @param classLoader
+ * @return "com.yugabyte.Driver" if url starts with "jdbc:yugabytedb:"
+ */
+ @Override
+ public String getDriverClass(String url, ClassLoader classLoader) {
+
+ if (url.startsWith("jdbc:p6spy:kingbase8:")) {
+ return "com.p6spy.engine.spy.P6SpyDriver";
+ }
+ return "com.kingbase8.Driver";
+ }
+
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleExecutionTemplate.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleExecutionTemplate.java
new file mode 100644
index 0000000..aae12b7
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleExecutionTemplate.java
@@ -0,0 +1,188 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+package org.flywaydb.community.database.kingbaseoracle;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.core.internal.strategy.RetryStrategy;
+import org.flywaydb.core.internal.util.FlywayDbWebsiteLinks;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+@CustomLog
+public class KingbaseOracleExecutionTemplate {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final String tableName;
+ private static final Map tableEntries = new ConcurrentHashMap<>();
+
+ KingbaseOracleExecutionTemplate(JdbcTemplate jdbcTemplate, String tableName) {
+ this.jdbcTemplate = jdbcTemplate;
+ this.tableName = tableName;
+ }
+
+ public T execute(Callable callable) {
+ Exception error = null;
+ try {
+ lock();
+ return callable.call();
+ } catch (RuntimeException e) {
+ error = e;
+ throw e;
+ } catch (Exception e) {
+ error = e;
+ throw new FlywayException(e);
+ } finally {
+ unlock(error);
+ }
+ }
+
+ private void lock() throws SQLException {
+ RetryStrategy strategy = new RetryStrategy();
+ strategy.doWithRetries(this::tryLock, "Interrupted while attempting to acquire lock through SELECT ... FOR UPDATE",
+ "Number of retries exceeded while attempting to acquire lock through SELECT ... FOR UPDATE. " +
+ "Configure the number of retries with the 'lockRetryCount' configuration option: " + FlywayDbWebsiteLinks.LOCK_RETRY_COUNT);
+
+ }
+
+ private boolean tryLock() {
+ Exception exception = null;
+ boolean txStarted = false, success = false;
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+
+ if (!tableEntries.containsKey(tableName)) {
+ try {
+ statement.executeUpdate("INSERT INTO "
+ + KingbaseOracleDatabase.LOCK_TABLE_NAME
+ + " VALUES ('" + tableName + "', 'false')");
+ tableEntries.put(tableName, true);
+ LOG.info(Thread.currentThread().getName() + "> Inserted a token row for " + tableName + " in " + KingbaseOracleDatabase.LOCK_TABLE_NAME);
+ } catch (SQLException e) {
+ if ("23505".equals(e.getSQLState())) {
+ // 23505 == UNIQUE_VIOLATION
+ LOG.debug(Thread.currentThread().getName() + "> Token row already added for " + tableName);
+ } else {
+ throw new FlywaySqlException("Could not add token row for " + tableName + " in table " + KingbaseOracleDatabase.LOCK_TABLE_NAME, e);
+ }
+ }
+ }
+
+ boolean locked;
+ String selectForUpdate = "SELECT locked FROM "
+ + KingbaseOracleDatabase.LOCK_TABLE_NAME
+ + " WHERE table_name = '"
+ + tableName
+ + "' FOR UPDATE";
+ String updateLocked = "UPDATE " + KingbaseOracleDatabase.LOCK_TABLE_NAME
+ + " SET locked = true WHERE table_name = '"
+ + tableName + "'";
+
+ statement.execute("BEGIN");
+ txStarted = true;
+ ResultSet rs = statement.executeQuery(selectForUpdate);
+ if (rs.next()) {
+ locked = rs.getBoolean("locked");
+
+ if (locked) {
+ statement.execute("COMMIT");
+ txStarted = false;
+ LOG.debug(Thread.currentThread().getName() + "> Another Flyway operation is in progress. Allowing it to complete");
+ } else {
+ LOG.debug(Thread.currentThread().getName() + "> Setting locked = true");
+ statement.executeUpdate(updateLocked);
+ success = true;
+ }
+ } else {
+ // For some reason the record was not found, retry
+ tableEntries.remove(tableName);
+ }
+
+ } catch (SQLException e) {
+ LOG.warn(Thread.currentThread().getName() + "> Unable to perform lock action, SQLState: " + e.getSQLState());
+ if (!"40001".equalsIgnoreCase(e.getSQLState())) {
+ exception = new FlywaySqlException("Unable to perform lock action", e);
+ throw (FlywaySqlException) exception;
+ } // else retry
+ } finally {
+ if (txStarted) {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = true");
+ } catch (SQLException e) {
+ if (exception == null) {
+ throw new FlywaySqlException("Failed to commit the tx to set locked = true", e);
+ }
+ LOG.warn(Thread.currentThread().getName() + "> Failed to commit the tx to set locked = true: " + e);
+ }
+ }
+ }
+ return success;
+ }
+
+ private void unlock(Exception rethrow) {
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+ statement.execute("BEGIN");
+ ResultSet rs = statement.executeQuery("SELECT locked FROM " + KingbaseOracleDatabase.LOCK_TABLE_NAME + " WHERE table_name = '" + tableName + "' FOR UPDATE");
+
+ if (rs.next()) {
+ boolean locked = rs.getBoolean("locked");
+ if (locked) {
+ statement.executeUpdate("UPDATE " + KingbaseOracleDatabase.LOCK_TABLE_NAME + " SET locked = false WHERE table_name = '" + tableName + "'");
+ } else {
+ // Unexpected. This may happen only when callable took too long to complete
+ // and another thread forcefully reset it.
+ String msg = "Unlock failed but the Flyway operation may have succeeded. Check your Flyway operation before re-trying";
+ LOG.warn(Thread.currentThread().getName() + "> " + msg);
+ if (rethrow == null) {
+ throw new FlywayException(msg);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ rethrow = new FlywayException("Unable to perform unlock action", e);
+ throw (FlywaySqlException) rethrow;
+ }
+ LOG.warn("Unable to perform unlock action " + e);
+ } finally {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = false");
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ throw new FlywaySqlException("Failed to commit unlock action", e);
+ }
+ LOG.warn("Failed to commit unlock action: " + e);
+ }
+ }
+ }
+
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleParser.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleParser.java
new file mode 100644
index 0000000..8d83ed6
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleParser.java
@@ -0,0 +1,397 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.parser.*;
+import org.flywaydb.core.internal.sqlscript.Delimiter;
+import org.flywaydb.core.internal.sqlscript.ParsedSqlStatement;
+import org.flywaydb.core.internal.util.StringUtils;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class KingbaseOracleParser extends Parser {
+
+ /**
+ * Delimiter of PL/SQL blocks and statements.
+ */
+ private static final Delimiter PLSQL_DELIMITER = new Delimiter("/", true
+
+ );
+
+ // accessible by ( keywordoptionalidentifier )
+ private static final String ACCESSIBLE_BY_REGEX = "ACCESSIBLE\\sBY\\s\\(?(((FUNCTION|PROCEDURE|PACKAGE|TRIGGER|TYPE)\\s)?[^\\s]\\s?+)*\\)?";
+
+ private static final Pattern PLSQL_TYPE_BODY_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\sTYPE\\sBODY\\s([^\\s]*\\s)?(IS|AS)");
+
+ private static final Pattern PLSQL_PACKAGE_BODY_REGEX = Pattern.compile(
+ "^CREATE(\\s*OR\\s*REPLACE)?(\\s*(NON)?EDITIONABLE)?\\s*PACKAGE\\s*BODY\\s*([^\\s]*\\s)?(IS|AS)");
+ private static final StatementType PLSQL_PACKAGE_BODY_STATEMENT = new StatementType();
+
+ private static final Pattern PLSQL_PACKAGE_DEFINITION_REGEX = Pattern.compile(
+ "^CREATE(\\s*OR\\s*REPLACE)?(\\s*(NON)?EDITIONABLE)?\\s*PACKAGE\\s([^\\s*]*\\s*)?(AUTHID\\s*[^\\s*]*\\s*|" + ACCESSIBLE_BY_REGEX + ")*(IS|AS)");
+
+ private static final Pattern PLSQL_VIEW_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?((\\sNO)?\\sFORCE)?(\\s(NON)?EDITIONABLE)?\\sVIEW\\s([^\\s]*\\s)?AS\\sWITH\\s(PROCEDURE|FUNCTION)");
+ private static final StatementType PLSQL_VIEW_STATEMENT = new StatementType();
+
+ private static final Pattern PLSQL_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\s(FUNCTION(\\s\\S*)|PROCEDURE|TYPE|TRIGGER)");
+ private static final Pattern DECLARE_BEGIN_REGEX = Pattern.compile("^DECLARE|BEGIN|WITH");
+ private static final StatementType PLSQL_STATEMENT = new StatementType();
+
+ private static final Pattern JAVA_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\sAND\\s(RESOLVE|COMPILE))?(\\sNOFORCE)?\\sJAVA\\s(SOURCE|RESOURCE|CLASS)");
+ private static final StatementType PLSQL_JAVA_STATEMENT = new StatementType();
+
+ private static final Pattern PLSQL_PACKAGE_BODY_WRAPPED_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\sPACKAGE\\sBODY(\\s\\S*)?\\sWRAPPED(\\s\\S*)*");
+ private static final Pattern PLSQL_PACKAGE_DEFINITION_WRAPPED_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\sPACKAGE(\\s\\S*)?\\sWRAPPED(\\s\\S*)*");
+ private static final Pattern PLSQL_WRAPPED_REGEX = Pattern.compile(
+ "^CREATE(\\sOR\\sREPLACE)?(\\s(NON)?EDITIONABLE)?\\s(FUNCTION|PROCEDURE|TYPE)(\\s\\S*)?\\sWRAPPED(\\s\\S*)*");
+
+ private static final StatementType PLSQL_WRAPPED_STATEMENT = new StatementType();
+ private int initialWrappedBlockDepth = -1;
+
+ private static Pattern toRegex(String... commands) {
+ return Pattern.compile(toRegexPattern(commands));
+ }
+
+ private static String toRegexPattern(String... commands) {
+ return "^(" + StringUtils.arrayToDelimitedString("|", commands) + ")";
+ }
+
+
+ public KingbaseOracleParser(Configuration configuration, ParsingContext parsingContext) {
+ super(configuration, parsingContext, 3);
+ }
+
+
+ @Override
+ protected ParsedSqlStatement createStatement(PeekingReader reader, Recorder recorder, int statementPos,
+ int statementLine, int statementCol, int nonCommentPartPos, int nonCommentPartLine, int nonCommentPartCol,
+ StatementType statementType, boolean canExecuteInTransaction, Delimiter delimiter, String sql,
+ boolean batchable) throws IOException {
+
+
+ if (PLSQL_VIEW_STATEMENT == statementType) {
+ sql = sql.trim();
+
+ // Strip extra semicolon to avoid issues with WITH statements containing PL/SQL
+ if (sql.endsWith(";")) {
+ sql = sql.substring(0, sql.length() - 1);
+ }
+ }
+
+ return super.createStatement(reader, recorder, statementPos, statementLine, statementCol,
+ nonCommentPartPos, nonCommentPartLine, nonCommentPartCol, statementType, canExecuteInTransaction, delimiter,
+ sql, batchable);
+ }
+
+ @Override
+ protected StatementType detectStatementType(String simplifiedStatement, ParserContext context, PeekingReader reader) {
+ if (PLSQL_PACKAGE_BODY_WRAPPED_REGEX.matcher(simplifiedStatement).matches()
+ || PLSQL_PACKAGE_DEFINITION_WRAPPED_REGEX.matcher(simplifiedStatement).matches()
+ || PLSQL_WRAPPED_REGEX.matcher(simplifiedStatement).matches()) {
+ if (initialWrappedBlockDepth == -1) {
+ initialWrappedBlockDepth = context.getBlockDepth();
+ }
+ return PLSQL_WRAPPED_STATEMENT;
+ }
+
+ if (PLSQL_PACKAGE_BODY_REGEX.matcher(simplifiedStatement).matches()) {
+ return PLSQL_PACKAGE_BODY_STATEMENT;
+ }
+
+ if (PLSQL_REGEX.matcher(simplifiedStatement).matches()
+ || PLSQL_PACKAGE_DEFINITION_REGEX.matcher(simplifiedStatement).matches()
+ || DECLARE_BEGIN_REGEX.matcher(simplifiedStatement).matches()) {
+ try {
+ String wrappedKeyword = " WRAPPED";
+ if (!reader.peek(wrappedKeyword.length()).equalsIgnoreCase(wrappedKeyword)) {
+ return PLSQL_STATEMENT;
+ }
+ } catch (IOException e) {
+ return PLSQL_STATEMENT;
+ }
+ }
+
+ if (JAVA_REGEX.matcher(simplifiedStatement).matches()) {
+ return PLSQL_JAVA_STATEMENT;
+ }
+
+ if (PLSQL_VIEW_REGEX.matcher(simplifiedStatement).matches()) {
+ return PLSQL_VIEW_STATEMENT;
+ }
+
+
+ return super.detectStatementType(simplifiedStatement, context, reader);
+ }
+
+ @Override
+ protected boolean shouldDiscard(Token token, boolean nonCommentPartSeen) {
+ // Discard dangling PL/SQL '/' delimiters
+ return ("/".equals(token.getText()) && !nonCommentPartSeen) || super.shouldDiscard(token, nonCommentPartSeen);
+ }
+
+ @Override
+ protected void adjustDelimiter(ParserContext context, StatementType statementType) {
+ if (statementType == PLSQL_STATEMENT || statementType == PLSQL_VIEW_STATEMENT || statementType == PLSQL_JAVA_STATEMENT
+ || statementType == PLSQL_PACKAGE_BODY_STATEMENT) {
+ context.setDelimiter(PLSQL_DELIMITER);
+
+
+ } else {
+ context.setDelimiter(Delimiter.SEMICOLON);
+ }
+ }
+
+
+ @Override
+ protected boolean shouldAdjustBlockDepth(ParserContext context, List tokens, Token token) {
+ // Package bodies can have an unbalanced BEGIN without END in the initialisation section.
+ TokenType tokenType = token.getType();
+ if (context.getStatementType() == PLSQL_PACKAGE_BODY_STATEMENT && (TokenType.EOF == tokenType || TokenType.DELIMITER == tokenType)) {
+ return true;
+ }
+
+ // Handle wrapped SQL on these token types to ensure it gets treated as one block
+ if (context.getStatementType() == PLSQL_WRAPPED_STATEMENT && (TokenType.EOF == tokenType || TokenType.DELIMITER == tokenType)) {
+ return true;
+ }
+
+ // In Oracle, symbols { } affect the block depth in embedded Java code
+ if (token.getType() == TokenType.SYMBOL && context.getStatementType() == PLSQL_JAVA_STATEMENT) {
+ return true;
+ }
+
+
+ final Token previousToken = getPreviousToken(tokens, token.getParensDepth());
+ if (previousToken != null && "CASE".equals(token.getText()) && "FROM".equals(previousToken.getText())) {
+ return false;
+ }
+
+ return super.shouldAdjustBlockDepth(context, tokens, token);
+ }
+
+ // These words increase the block depth - unless preceded by END (in which case the END will decrease the block depth)
+ private static final List CONTROL_FLOW_KEYWORDS = Arrays.asList("IF", "LOOP", "CASE");
+
+ @Override
+ protected void adjustBlockDepth(ParserContext context, List tokens, Token keyword, PeekingReader reader) {
+ TokenType tokenType = keyword.getType();
+ String keywordText = keyword.getText();
+ int parensDepth = keyword.getParensDepth();
+
+ if (lastTokenIs(tokens, parensDepth, "GOTO")) {
+ return;
+ }
+
+ if (context.getStatementType() == PLSQL_WRAPPED_STATEMENT) {
+ // ensure wrapped SQL has an increased block depth so it gets treated as one statement
+ if (context.getBlockDepth() == initialWrappedBlockDepth) {
+ context.increaseBlockDepth("WRAPPED");
+ }
+ // decrease block depth at the end to step out of a wrapped SQL block
+ if ((TokenType.EOF == tokenType || (TokenType.DELIMITER == tokenType && "/".equals(keywordText))) && context.getBlockDepth() > 0) {
+ context.decreaseBlockDepth();
+ }
+ // return early as we don't need to parse the contents of wrapped SQL - it's all one statement anyways
+ return;
+ } else {
+ // decrease block depth when wrapped SQL ends to step out of wrapped SQL block
+ if (context.getBlockDepth() > initialWrappedBlockDepth && context.getBlockInitiator().equals("WRAPPED")) {
+ initialWrappedBlockDepth = -1;
+ context.decreaseBlockDepth();
+ }
+ }
+
+ // In embedded Java code we judge the end of a class definition by the depth of braces.
+ // We ignore normal SQL keywords as Java code can contain arbitrary identifiers.
+ if (context.getStatementType() == PLSQL_JAVA_STATEMENT) {
+ if ("{".equals(keywordText)) {
+ context.increaseBlockDepth("PLSQL_JAVA_STATEMENT");
+ } else if ("}".equals(keywordText)) {
+ context.decreaseBlockDepth();
+ }
+ return;
+ }
+
+ if ("BEGIN".equals(keywordText)
+ || (CONTROL_FLOW_KEYWORDS.contains(keywordText) && !precedingEndAttachesToThisKeyword(tokens, parensDepth, context, keyword))
+ || ("TRIGGER".equals(keywordText) && lastTokenIs(tokens, parensDepth, "COMPOUND"))
+ || (context.getBlockDepth() == 0 && (
+ doTokensMatchPattern(tokens, keyword, PLSQL_PACKAGE_BODY_REGEX) ||
+ doTokensMatchPattern(tokens, keyword, PLSQL_PACKAGE_DEFINITION_REGEX) ||
+ doTokensMatchPattern(tokens, keyword, PLSQL_TYPE_BODY_REGEX)))
+ ) {
+ context.increaseBlockDepth(keywordText);
+ } else if ("END".equals(keywordText)) {
+ context.decreaseBlockDepth();
+ }
+
+ // Package bodies can have an unbalanced BEGIN without END in the initialisation section. This allows us
+ // to exit the package even though we are still at block depth 1 due to the BEGIN.
+ if (context.getStatementType() == PLSQL_PACKAGE_BODY_STATEMENT && (TokenType.EOF == tokenType || TokenType.DELIMITER == tokenType) && context.getBlockDepth() == 1) {
+ context.decreaseBlockDepth();
+ }
+ }
+
+ private boolean precedingEndAttachesToThisKeyword(List tokens, int parensDepth, ParserContext context, Token keyword) {
+ // Normally IF, LOOP and CASE all pair up with END IF, END LOOP, END CASE
+ // However, CASE ... END is valid in expressions, so in code such as
+ // FOR i IN 1 .. CASE WHEN foo THEN 5 ELSE 6 END
+ // LOOP
+ // ...
+ // END LOOP
+ // the first END does *not* attach to the subsequent LOOP. The same is possible with $IF ... $END constructions
+ return lastTokenIs(tokens, parensDepth, "END") &&
+ lastTokenIsOnLine(tokens, parensDepth, keyword.getLine()) &&
+ keyword.getText().equals(context.getLastClosedBlockInitiator());
+ }
+
+ @Override
+ protected boolean doTokensMatchPattern(List previousTokens, Token current, Pattern regex) {
+ if (regex == PLSQL_PACKAGE_DEFINITION_REGEX &&
+ previousTokens.stream().anyMatch(t -> t.getType() == TokenType.KEYWORD && t.getText().equalsIgnoreCase("ACCESSIBLE"))) {
+ ArrayList tokenStrings = new ArrayList<>();
+ tokenStrings.add(current.getText());
+
+ for (int i = previousTokens.size() - 1; i >= 0; i--) {
+ Token prevToken = previousTokens.get(i);
+ if (prevToken.getType() == TokenType.KEYWORD) {
+ tokenStrings.add(prevToken.getText());
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ for (int i = tokenStrings.size() - 1; i >= 0; i--) {
+ builder.append(tokenStrings.get(i));
+ if (i != 0) {
+ builder.append(" ");
+ }
+ }
+
+ return regex.matcher(builder.toString()).matches() || super.doTokensMatchPattern(previousTokens, current, regex);
+ }
+
+ return super.doTokensMatchPattern(previousTokens, current, regex);
+ }
+
+ @Override
+ protected boolean isDelimiter(String peek, ParserContext context, int col, int colIgnoringWhitespace) {
+ Delimiter delimiter = context.getDelimiter();
+
+ if (peek.startsWith(delimiter.getEscape() + delimiter.getDelimiter())) {
+ return true;
+ }
+
+ if (delimiter.shouldBeAloneOnLine()) {
+ // Only consider alone-on-line delimiters (such as "/" for PL/SQL) if
+ // it's the first character on the line
+ return colIgnoringWhitespace == 1 && peek.trim().equals(delimiter.getDelimiter());
+ } else {
+ if (colIgnoringWhitespace == 1 && "/".equals(peek.trim())) {
+ return true;
+ }
+ }
+
+ return super.isDelimiter(peek, context, col, colIgnoringWhitespace);
+ }
+
+ @Override
+ protected Token handleMultilineComment(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException {
+ reader.swallow("/*".length());
+ String text = reader.readUntilExcluding("*/");
+ reader.swallow("*/".length());
+ return new Token(TokenType.COMMENT, pos, line, col, text, text, context.getParensDepth());
+ }
+
+ @Override
+ protected Token handleDelimiter(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException {
+
+
+ if (reader.peek('/')) {
+ reader.swallow(1);
+ return new Token(TokenType.DELIMITER, pos, line, col, "/", "/", context.getParensDepth());
+ }
+
+ return super.handleDelimiter(reader, context, pos, line, col);
+ }
+
+ @Override
+ protected boolean isAlternativeStringLiteral(String peek) {
+ if (peek.length() < 3) {
+ return false;
+ }
+ // Oracle's quoted-literal syntax is introduced by q (case-insensitive) followed by a literal surrounded by
+ // any of !!, [], {}, (), <> provided the selected pair do not appear in the literal string; the others may do.
+ char firstChar = peek.charAt(0);
+ return (firstChar == 'q' || firstChar == 'Q') && peek.charAt(1) == '\'';
+ }
+
+ @Override
+ protected Token handleAlternativeStringLiteral(PeekingReader reader, ParserContext context, int pos, int line, int col) throws IOException {
+ reader.swallow(2);
+ String closeQuote = computeAlternativeCloseQuote((char) reader.read());
+ reader.swallowUntilExcluding(closeQuote);
+ reader.swallow(closeQuote.length());
+ return new Token(TokenType.STRING, pos, line, col, null, null, context.getParensDepth());
+ }
+
+ private String computeAlternativeCloseQuote(char specialChar) {
+ switch (specialChar) {
+ case '!':
+ return "!'";
+ case '[':
+ return "]'";
+ case '(':
+ return ")'";
+ case '{':
+ return "}'";
+ case '<':
+ return ">'";
+ default:
+ return specialChar + "'";
+ }
+ }
+
+ }
\ No newline at end of file
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleSchema.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleSchema.java
new file mode 100644
index 0000000..4f470dc
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleSchema.java
@@ -0,0 +1,55 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLSchema;
+
+public class KingbaseOracleSchema extends PostgreSQLSchema {
+ /**
+ * @param jdbcTemplate The Jdbc Template for communicating with the DB.
+ * @param database The database-specific support.
+ * @param name The name of the schema.
+ */
+ public KingbaseOracleSchema(JdbcTemplate jdbcTemplate, KingbaseOracleDatabase database, String name) {
+ super(jdbcTemplate, database, name);
+ }
+
+ @Override
+ public Table getTable(String tableName) {
+ return new KingbaseOracleTable(jdbcTemplate, (KingbaseOracleDatabase) database, this, tableName);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleTable.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleTable.java
new file mode 100644
index 0000000..6aa2082
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/KingbaseOracleTable.java
@@ -0,0 +1,50 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbaseoracle;
+
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLTable;
+
+public class KingbaseOracleTable extends PostgreSQLTable {
+ /**
+ * @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 KingbaseOracleTable(JdbcTemplate jdbcTemplate, KingbaseOracleDatabase database, KingbaseOracleSchema schema, String name) {
+ super(jdbcTemplate, database, schema, name);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/package-info.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/package-info.java
new file mode 100644
index 0000000..06ae537
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbaseoracle/package-info.java
@@ -0,0 +1,23 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-postgresql
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/**
+ * Private API. No compatibility guarantees provided.
+ */
+package org.flywaydb.community.database.kingbaseoracle;
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerConnection.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerConnection.java
new file mode 100644
index 0000000..fd08bdd
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerConnection.java
@@ -0,0 +1,58 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasesqlserver;
+
+import org.flywaydb.core.internal.database.base.Schema;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.database.postgresql.PostgreSQLConnection;
+
+import java.util.concurrent.Callable;
+
+public class KingbaseSQLServerConnection extends PostgreSQLConnection {
+
+ KingbaseSQLServerConnection(KingbaseSQLServerDatabase database, java.sql.Connection connection) {
+ super(database, connection);
+ }
+
+ @Override
+ public Schema getSchema(String name) {
+ return new KingbaseSQLServerSchema(jdbcTemplate, (KingbaseSQLServerDatabase) database, name);
+ }
+
+ @Override
+ public T lock(Table table, Callable callable) {
+ return new KingbaseSQLServerExecutionTemplate(jdbcTemplate, table.toString()).execute(callable);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabase.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabase.java
new file mode 100644
index 0000000..61cb806
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabase.java
@@ -0,0 +1,144 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasesqlserver;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcConnectionFactory;
+import org.flywaydb.core.internal.jdbc.StatementInterceptor;
+import org.flywaydb.core.internal.sqlscript.Delimiter;
+import org.flywaydb.database.postgresql.PostgreSQLDatabase;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+
+@CustomLog
+public class KingbaseSQLServerDatabase extends PostgreSQLDatabase {
+
+ public static final String LOCK_TABLE_NAME = "YB_FLYWAY_LOCK_TABLE";
+ /**
+ * This table is used to enforce locking through SELECT ... FOR UPDATE on a
+ * token row inserted in this table. The token row is inserted with the name
+ * of the Flyway's migration history table as a token for simplicity.
+ */
+// private static final String CREATE_LOCK_TABLE_DDL = "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE_NAME + " (table_name varchar PRIMARY KEY, locked bool)";
+ private static final String CREATE_LOCK_TABLE_DDL =
+// "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE_NAME +
+// " (table_name VARCHAR(255) PRIMARY KEY, locked BOOLEAN NOT NULL DEFAULT FALSE)";
+ "CREATE TABLE IF NOT EXISTS " + LOCK_TABLE_NAME +
+ " (table_name VARCHAR(255) PRIMARY KEY, locked BIT NOT NULL DEFAULT 0)";//将locked的boolean类型修改为bit类型
+ public KingbaseSQLServerDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ super(configuration, jdbcConnectionFactory, statementInterceptor);
+ createLockTable();
+ }
+
+ @Override
+ protected KingbaseSQLServerConnection doGetConnection(Connection connection) {
+ return new KingbaseSQLServerConnection(this, connection);
+ }
+
+ @Override
+ public String getBooleanTrue() {
+ return "1";
+ }
+
+ @Override
+ public String getBooleanFalse() {
+ return "0";
+ }
+
+ @Override
+ public void ensureSupported(Configuration configuration) {
+ // Checks the Postgres version
+ ensureDatabaseIsRecentEnough("11.2");
+ }
+
+
+ @Override
+ public boolean supportsDdlTransactions() {
+ return true;
+ }
+
+// 修改建表语句 在一个书事务之后加GO
+@Override
+public String getRawCreateScript(Table table, boolean baseline) {
+ return "CREATE TABLE " + table + " (\n" +
+ " [installed_rank] INT NOT NULL,\n" +
+ " [version] NVARCHAR(50),\n" +
+ " [description] NVARCHAR(200),\n" +
+ " [type] NVARCHAR(20) NOT NULL,\n" +
+ " [script] NVARCHAR(1000) NOT NULL,\n" +
+ " [checksum] INT,\n" +
+ " [installed_by] NVARCHAR(100) NOT NULL,\n" +
+ " [installed_on] DATETIME NOT NULL DEFAULT GETDATE(),\n" +
+ " [execution_time] INT NOT NULL,\n" +
+ " [success] BIT NOT NULL\n" +
+ ")" + ";\nGO\n" + // 每个 CREATE TABLE 后加 GO
+ (baseline ? getBaselineStatement(table) + ";\nGO\n" : "") + // 如果 baseline 需要执行,也加 GO
+ "ALTER TABLE " + table + " ADD CONSTRAINT [" + table.getName() + "_pk] PRIMARY KEY ([installed_rank]);\nGO\n" + // ALTER TABLE 后加 GO
+ "CREATE INDEX [" + table.getName() + "_s_idx] ON " + table + " ([success]);\nGO\n"; // CREATE INDEX 后加 GO
+}
+
+ //新增
+ @Override
+ public String getOpenQuote() { return "["; }
+ @Override
+ public String getCloseQuote() { return "]"; }
+ @Override
+ public String getEscapedQuote() { return "]]"; }
+
+ @Override
+ public Delimiter getDefaultDelimiter() {
+ return Delimiter.GO;
+ }
+
+ @Override
+ public boolean useSingleConnection() {
+ return true;
+ }
+
+ private void createLockTable() {
+ try {
+ jdbcTemplate.execute(CREATE_LOCK_TABLE_DDL);
+ } catch (SQLException e) {
+ throw new FlywaySqlException("Unable to initialize the lock table", e);
+ }
+ }
+}
+
+
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabaseType.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabaseType.java
new file mode 100644
index 0000000..158a529
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerDatabaseType.java
@@ -0,0 +1,162 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasesqlserver;
+
+import org.flywaydb.community.database.KingbaseDatabaseExtension;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.api.ResourceProvider;
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.extensibility.Tier;
+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.license.FlywayEditionUpgradeRequiredException;
+import org.flywaydb.core.internal.parser.Parser;
+import org.flywaydb.core.internal.parser.ParsingContext;
+import org.flywaydb.database.postgresql.PostgreSQLDatabaseType;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+public class KingbaseSQLServerDatabaseType extends PostgreSQLDatabaseType implements CommunityDatabaseType {
+ @Override
+ public String getName() {
+ return "Kingbase";
+ }
+
+// @Override
+// public boolean handlesJDBCUrl(String url) {
+// return url.startsWith("jdbc:kingbase8:")
+// || url.startsWith("jdbc:postgresql:") || url.startsWith("jdbc:p6spy:postgresql:");
+// }
+
+ @Override
+ public boolean handlesJDBCUrl(String url) {
+ if (url.startsWith("jdbc-secretsmanager:kingbase8:")) {
+
+ throw new FlywayEditionUpgradeRequiredException(Tier.ENTERPRISE, (Tier) null, "jdbc-secretsmanager");
+
+ }
+ return url.startsWith("jdbc:kingbase8:") || url.startsWith("jdbc:p6spy:kingbase8:");
+ }
+
+ @Override
+ public int getPriority() {
+ // Should be checked before plain PostgreSQL
+ return 1;
+ }
+
+// @Override
+// public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+// // The YB is what distinguishes Yugabyte
+// return databaseProductName.startsWith("Kingbase");
+// }
+
+ @Override
+ public boolean handlesDatabaseProductNameAndVersion(String databaseProductName, String databaseProductVersion, Connection connection) {
+ if (!databaseProductName.contains("Kingbase")) {
+ return false; // 先确保是 Kingbase 数据库
+ }
+
+ boolean result = checkDatabaseMode(connection, "sqlserver");
+// System.out.println("Checking Kingbase SQL Server mode: " + result + ", databaseProductName: " + databaseProductName);
+ return result;
+ }
+
+ private boolean checkDatabaseMode(Connection connection, String expectedMode) {
+ try (Statement stmt = connection.createStatement();
+ ResultSet rs = stmt.executeQuery("SHOW DATABASE_MODE;")) {
+
+ if (rs.next()) {
+ String mode = rs.getString(1).trim().toLowerCase();
+ return expectedMode.equals(mode);
+ }
+ } catch (SQLException e) {
+ throw new FlywayException("Failed to determine Kingbase database mode", e);
+ }
+ return false;
+ }
+
+
+ @Override
+ public Database createDatabase(Configuration configuration, JdbcConnectionFactory jdbcConnectionFactory, StatementInterceptor statementInterceptor) {
+ return new KingbaseSQLServerDatabase(configuration, jdbcConnectionFactory, statementInterceptor);
+ }
+
+
+ @Override
+ public Parser createParser(Configuration configuration, ResourceProvider resourceProvider, ParsingContext parsingContext) {
+ return new KingbaseSQLServerParser(configuration, parsingContext);
+ }
+
+ @Override
+ public void setDefaultConnectionProps(String url, Properties props, ClassLoader classLoader) {
+ props.put("applicationName", APPLICATION_NAME);
+ }
+
+
+ @Override
+ public String getPluginVersion(Configuration config) {
+ return KingbaseDatabaseExtension.readVersion();
+ }
+
+
+ /**
+ * Returns the YugabyteDB Smart driver classname if the smart driver is
+ * being used. The plugin will work with the Postgresql JDBC driver also
+ * since the url in that case would start with 'jdbc:postgresql' which would
+ * return the PG JDBC driver class name.
+ * @param url
+ * @param classLoader
+ * @return "com.yugabyte.Driver" if url starts with "jdbc:yugabytedb:"
+ */
+// @Override
+// public String getDriverClass(String url, ClassLoader classLoader) {
+// return url.startsWith("com.kingbase8.Driver") ? "com.kingbase8.Driver" : super.getDriverClass(url, classLoader);
+// }
+ @Override
+ public String getDriverClass(String url, ClassLoader classLoader) {
+
+ if (url.startsWith("jdbc:p6spy:kingbase8:")) {
+ return "com.p6spy.engine.spy.P6SpyDriver";
+ }
+ return "com.kingbase8.Driver";
+ }
+
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerExecutionTemplate.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerExecutionTemplate.java
new file mode 100644
index 0000000..2185f57
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerExecutionTemplate.java
@@ -0,0 +1,178 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+package org.flywaydb.community.database.kingbasesqlserver;
+
+import lombok.CustomLog;
+import org.flywaydb.core.api.FlywayException;
+import org.flywaydb.core.internal.exception.FlywaySqlException;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.core.internal.strategy.RetryStrategy;
+import org.flywaydb.core.internal.util.FlywayDbWebsiteLinks;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+
+@CustomLog
+public class KingbaseSQLServerExecutionTemplate {
+
+ private final JdbcTemplate jdbcTemplate;
+ private final String tableName;
+ private static final Map tableEntries = new ConcurrentHashMap<>();
+
+ KingbaseSQLServerExecutionTemplate(JdbcTemplate jdbcTemplate, String tableName) {
+ this.jdbcTemplate = jdbcTemplate;
+ this.tableName = tableName;
+ }
+
+ public T execute(Callable callable) {
+ Exception error = null;
+ try {
+ lock();
+ return callable.call();
+ } catch (RuntimeException e) {
+ error = e;
+ throw e;
+ } catch (Exception e) {
+ error = e;
+ throw new FlywayException(e);
+ } finally {
+ unlock(error);
+ }
+ }
+
+ private void lock() throws SQLException {
+ RetryStrategy strategy = new RetryStrategy();
+ strategy.doWithRetries(this::tryLock, "Interrupted while attempting to acquire lock through SELECT ... FOR UPDATE",
+ "Number of retries exceeded while attempting to acquire lock through SELECT ... FOR UPDATE. " +
+ "Configure the number of retries with the 'lockRetryCount' configuration option: " + FlywayDbWebsiteLinks.LOCK_RETRY_COUNT);
+ }
+
+ private boolean tryLock() {
+ Exception exception = null;
+ boolean txStarted = false, success = false;
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+
+ if (!tableEntries.containsKey(tableName)) {
+ try {
+// statement.executeUpdate("INSERT INTO " + KingbaseDatabase.LOCK_TABLE_NAME + " (table_name, locked) VALUES ('" + tableName + "', false)");
+ statement.executeUpdate("INSERT INTO " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME + " (table_name, locked) VALUES ('" + tableName + "', '0')");//修改locked为bit类型
+ tableEntries.put(tableName, true);
+ LOG.info(Thread.currentThread().getName() + "> Inserted a token row for " + tableName + " in " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME);
+ } catch (SQLException e) {
+ if ("23505".equals(e.getSQLState())) { // UNIQUE_VIOLATION
+ LOG.debug(Thread.currentThread().getName() + "> Token row already added for " + tableName);
+ } else {
+ throw new FlywaySqlException("Could not add token row for " + tableName + " in table " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME, e);
+ }
+ }
+ }
+
+ String selectForUpdate = "SELECT locked FROM " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME + " WHERE table_name = '" + tableName + "' FOR UPDATE";
+// String updateLocked = "UPDATE " + KingbaseDatabase.LOCK_TABLE_NAME + " SET locked = true WHERE table_name = '" + tableName + "'";
+ String updateLocked = "UPDATE " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME + " SET locked = '1' WHERE table_name = '" + tableName + "'"; //修改locked值为bit类型
+
+
+ statement.execute("BEGIN TRANSACTION"); // 修改 BEGIN 为 START TRANSACTION
+ txStarted = true;
+ ResultSet rs = statement.executeQuery(selectForUpdate);
+ if (rs.next()) {
+// boolean locked = rs.getBoolean("locked");
+ boolean locked = "1".equals(rs.getString("locked"));//修改locked为bit类型
+ if (locked) {
+ statement.execute("COMMIT");
+ txStarted = false;
+ LOG.debug(Thread.currentThread().getName() + "> Another Flyway operation is in progress. Allowing it to complete");
+ } else {
+ LOG.debug(Thread.currentThread().getName() + "> Setting locked = true");
+ statement.executeUpdate(updateLocked);
+ success = true;
+ }
+ } else {
+ tableEntries.remove(tableName);
+ }
+
+ } catch (SQLException e) {
+ LOG.warn(Thread.currentThread().getName() + "> Unable to perform lock action, SQLState: " + e.getSQLState());
+ if (!"40001".equalsIgnoreCase(e.getSQLState())) {
+ exception = new FlywaySqlException("Unable to perform lock action", e);
+ throw (FlywaySqlException) exception;
+ }
+ } finally {
+ if (txStarted) {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = true");
+ } catch (SQLException e) {
+ if (exception == null) {
+ throw new FlywaySqlException("Failed to commit the tx to set locked = true", e);
+ }
+ LOG.warn(Thread.currentThread().getName() + "> Failed to commit the tx to set locked = true: " + e);
+ }
+ }
+ }
+ return success;
+ }
+
+ private void unlock(Exception rethrow) {
+ Statement statement = null;
+ try {
+ statement = jdbcTemplate.getConnection().createStatement();
+ statement.execute("BEGIN TRANSACTION"); // 修改 BEGIN 为 START TRANSACTION
+ ResultSet rs = statement.executeQuery("SELECT locked FROM " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME + " WHERE table_name = '" + tableName + "' FOR UPDATE");
+
+ if (rs.next()) {
+ boolean locked = rs.getBoolean("locked");
+ if (locked) {
+// statement.executeUpdate("UPDATE " + KingbaseDatabase.LOCK_TABLE_NAME + " SET locked = false WHERE table_name = '" + tableName + "'");
+ statement.executeUpdate("UPDATE " + KingbaseSQLServerDatabase.LOCK_TABLE_NAME + " SET locked = '0' WHERE table_name = '" + tableName + "'"); //修改为bit类型
+
+ } else {
+ String msg = "Unlock failed but the Flyway operation may have succeeded. Check your Flyway operation before re-trying";
+ LOG.warn(Thread.currentThread().getName() + "> " + msg);
+ if (rethrow == null) {
+ throw new FlywayException(msg);
+ }
+ }
+ }
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ rethrow = new FlywayException("Unable to perform unlock action", e);
+ throw (FlywaySqlException) rethrow;
+ }
+ LOG.warn("Unable to perform unlock action " + e);
+ } finally {
+ try {
+ statement.execute("COMMIT");
+ LOG.debug(Thread.currentThread().getName() + "> Completed the tx to set locked = false");
+ } catch (SQLException e) {
+ if (rethrow == null) {
+ throw new FlywaySqlException("Failed to commit unlock action", e);
+ }
+ LOG.warn("Failed to commit unlock action: " + e);
+ }
+ }
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerParser.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerParser.java
new file mode 100644
index 0000000..9f8b4b1
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerParser.java
@@ -0,0 +1,153 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-kingbase
+ * ========================================================================
+ * Copyright (C) 2010 - 2025 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+package org.flywaydb.community.database.kingbasesqlserver;
+
+import org.flywaydb.core.api.configuration.Configuration;
+import org.flywaydb.core.internal.parser.*;
+import org.flywaydb.core.internal.sqlscript.Delimiter;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class KingbaseSQLServerParser extends Parser {
+ private static final List SPROCS_INVALID_IN_TRANSACTIONS = Arrays.asList(
+ "SP_ADDSUBSCRIPTION", "SP_DROPSUBSCRIPTION",
+ "SP_ADDDISTRIBUTOR", "SP_DROPDISTRIBUTOR",
+ "SP_ADDDISTPUBLISHER", "SP_DROPDISTPUBLISHER",
+ "SP_ADDLINKEDSERVER", "SP_DROPLINKEDSERVER",
+ "SP_ADDLINKEDSRVLOGIN", "SP_DROPLINKEDSRVLOGIN",
+ "SP_SERVEROPTION", "SP_REPLICATIONDBOPTION",
+ "SP_FULLTEXT_DATABASE");
+
+ private static final Pattern BEGIN_SINGLE_STATEMENT_REGEX = Pattern.compile("TRAN(SACTION)?|CONVERSATION|DIALOG");
+ private static final Pattern TRANSACTION_REGEX = Pattern.compile("TRAN(SACTION)?");
+
+ public KingbaseSQLServerParser(Configuration configuration, ParsingContext parsingContext) {
+ super(configuration, parsingContext, 3);
+ }
+
+ @Override
+ protected Delimiter getDefaultDelimiter() {
+ return Delimiter.GO;
+ }
+
+ @Override
+ protected boolean isDelimiter(String peek, ParserContext context, int col, int colIgnoringWhitespace) {
+ return peek.length() >= 2
+ && (peek.charAt(0) == 'G' || peek.charAt(0) == 'g')
+ && (peek.charAt(1) == 'O' || peek.charAt(1) == 'o')
+ && (peek.length() == 2 || Character.isWhitespace(peek.charAt(2)));
+ }
+
+ @Override
+ protected String readKeyword(PeekingReader reader, Delimiter delimiter, ParserContext context) throws IOException {
+ // #2414: Ignore delimiter as GO (unlike ;) can be part of a regular keyword
+ return "" + (char) reader.read() + reader.readKeywordPart(null, context);
+ }
+
+ @Override
+ protected Boolean detectCanExecuteInTransaction(String simplifiedStatement, List keywords) {
+ if (keywords.size() == 0) {
+ return null;
+ }
+
+ Token currentToken = keywords.get(keywords.size() - 1);
+ String current = currentToken.getText();
+
+ if (currentToken.getType() != TokenType.IDENTIFIER &&
+ ("BACKUP".equals(current) || "RESTORE".equals(current) || "RECONFIGURE".equals(current))) {
+ return false;
+ }
+
+ if (keywords.size() < 2) {
+ return null;
+ }
+
+ String previous = keywords.get(keywords.size() - 2).getText();
+
+ if ("EXEC".equals(previous) && SPROCS_INVALID_IN_TRANSACTIONS.contains(current)) {
+ return false;
+ }
+
+ // (CREATE|DROP|ALTER) (DATABASE|FULLTEXT (INDEX|CATALOG))
+ if (("CREATE".equals(previous) || "ALTER".equals(previous) || "DROP".equals(previous))
+ && ("DATABASE".equals(current) || "FULLTEXT".equals(current))) {
+ return false;
+ }
+
+ return null;
+ }
+
+ @Override
+ protected boolean shouldAdjustBlockDepth(ParserContext context, List tokens, Token token) {
+ TokenType tokenType = token.getType();
+ if (TokenType.DELIMITER.equals(tokenType) || ";".equals(token.getText())) {
+ return true;
+ } else if (TokenType.EOF.equals(tokenType)) {
+ return true;
+ }
+
+ return super.shouldAdjustBlockDepth(context, tokens, token);
+ }
+
+ @Override
+ protected void adjustBlockDepth(ParserContext context, List tokens, Token keyword, PeekingReader reader) throws IOException {
+ String keywordText = keyword.getText();
+
+ if ("BEGIN".equals(keywordText)) {
+ context.increaseBlockDepth("");
+ }
+
+ if (context.getBlockDepth() > 0 && ("END".equals(keywordText) ||
+ isSingleStatementBegin(tokens, keyword, keywordText) ||
+ isDistributedTransaction(tokens, keyword, keywordText))) {
+ context.decreaseBlockDepth();
+ }
+
+ super.adjustBlockDepth(context, tokens, keyword, reader);
+ }
+
+ private boolean isSingleStatementBegin(List tokens, Token keyword, String keywordText) {
+ return keywordText != null && BEGIN_SINGLE_STATEMENT_REGEX.matcher(keywordText).matches() &&
+ lastTokenIs(tokens, keyword.getParensDepth(), "BEGIN");
+ }
+
+ private boolean isDistributedTransaction(List tokens, Token keyword, String keywordText) {
+ return keywordText != null && TRANSACTION_REGEX.matcher(keywordText).matches() &&
+ lastTokenIs(tokens, keyword.getParensDepth(), "DISTRIBUTED") &&
+ tokenAtIndexIs(tokens, tokens.size() - 2, "BEGIN");
+ }
+
+ @Override
+ protected int getTransactionalDetectionCutoff() {
+ return Integer.MAX_VALUE;
+ }
+
+ @Override
+ protected char getOpeningIdentifierSymbol() {
+ return '[';
+ }
+
+ @Override
+ protected char getClosingIdentifierSymbol() {
+ return ']';
+ }
+}
\ No newline at end of file
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerSchema.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerSchema.java
new file mode 100644
index 0000000..0ef8e06
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerSchema.java
@@ -0,0 +1,55 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasesqlserver;
+
+import org.flywaydb.core.internal.database.base.Table;
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLSchema;
+
+public class KingbaseSQLServerSchema extends PostgreSQLSchema {
+ /**
+ * @param jdbcTemplate The Jdbc Template for communicating with the DB.
+ * @param database The database-specific support.
+ * @param name The name of the schema.
+ */
+ public KingbaseSQLServerSchema(JdbcTemplate jdbcTemplate, KingbaseSQLServerDatabase database, String name) {
+ super(jdbcTemplate, database, name);
+ }
+
+ @Override
+ public Table getTable(String tableName) {
+ return new KingbaseSQLServerTable(jdbcTemplate, (KingbaseSQLServerDatabase) database, this, tableName);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerTable.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerTable.java
new file mode 100644
index 0000000..196a021
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/KingbaseSQLServerTable.java
@@ -0,0 +1,50 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-yugabytedb
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/*
+ * Copyright (C) Red Gate Software Ltd 2010-2024
+ *
+ * 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.kingbasesqlserver;
+
+import org.flywaydb.core.internal.jdbc.JdbcTemplate;
+import org.flywaydb.database.postgresql.PostgreSQLTable;
+
+public class KingbaseSQLServerTable extends PostgreSQLTable {
+ /**
+ * @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 KingbaseSQLServerTable(JdbcTemplate jdbcTemplate, KingbaseSQLServerDatabase database, KingbaseSQLServerSchema schema, String name) {
+ super(jdbcTemplate, database, schema, name);
+ }
+}
diff --git a/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/package-info.java b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/package-info.java
new file mode 100644
index 0000000..ff92460
--- /dev/null
+++ b/flyway-database-kingbase/src/main/java/org/flywaydb/community/database/kingbasesqlserver/package-info.java
@@ -0,0 +1,23 @@
+/*-
+ * ========================LICENSE_START=================================
+ * flyway-database-postgresql
+ * ========================================================================
+ * Copyright (C) 2010 - 2024 Red Gate Software Ltd
+ * ========================================================================
+ * 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.
+ * =========================LICENSE_END==================================
+ */
+/**
+ * Private API. No compatibility guarantees provided.
+ */
+package org.flywaydb.community.database.kingbasesqlserver;
diff --git a/flyway-database-kingbase/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin b/flyway-database-kingbase/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin
new file mode 100644
index 0000000..b944d81
--- /dev/null
+++ b/flyway-database-kingbase/src/main/resources/META-INF/services/org.flywaydb.core.extensibility.Plugin
@@ -0,0 +1,3 @@
+org.flywaydb.community.database.kingbasemysql.KingbaseMysqlDatabaseType
+org.flywaydb.community.database.kingbaseoracle.KingbaseOracleDatabaseType
+org.flywaydb.community.database.kingbasesqlserver.KingbaseSQLServerDatabaseType
diff --git a/flyway-database-kingbase/src/main/resources/org/flywaydb/community/database/kingbasemysql/version.txt b/flyway-database-kingbase/src/main/resources/org/flywaydb/community/database/kingbasemysql/version.txt
new file mode 100644
index 0000000..1785151
--- /dev/null
+++ b/flyway-database-kingbase/src/main/resources/org/flywaydb/community/database/kingbasemysql/version.txt
@@ -0,0 +1 @@
+${pom.version}
\ No newline at end of file