This is an automated email from the ASF dual-hosted git repository.
jerryshao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new 3d2bd431bb [#12516] fix(scripts): make 1.3 to 2.0 upgrades retryable
(#13689)
3d2bd431bb is described below
commit 3d2bd431bbec3f4e47db70ba3a3f47f385bc9f90
Author: Qi Yu <[email protected]>
AuthorDate: Thu Oct 8 17:22:09 2026 +0800
[#12516] fix(scripts): make 1.3 to 2.0 upgrades retryable (#13689)
### What changes were proposed in this pull request?
Make the MySQL, PostgreSQL, and H2 1.3.0-to-2.0.0 upgrade scripts
retryable after partial execution. Guard column additions, index
creation/deletion/renaming, and primary-key conversion while preserving
existing data and reporting unexpected schema errors.
Add tests for repeated upgrades, retries after persistent structural
changes and data updates, and unexpected MySQL primary keys. Sample the
first and last MySQL index rename; compare upgraded schemas with fresh
2.0.0 installs, excluding the deliberately retained
`policy_relation_meta` table. Earlier upgrade scripts are unchanged.
### Why are the changes needed?
A failed upgrade can leave committed changes behind. Re-running the
current scripts then fails on already-applied operations, preventing
recovery through a normal retry.
Related to #12516. This overlaps the 1.3.0-to-2.0.0 index-creation
changes in #12968 and extends retry handling to the rest of that
migration.
### Does this PR introduce _any_ user-facing change?
Users can retry a partially completed 1.3.0-to-2.0.0 upgrade after
fixing the failure cause. Unexpected schema differences require manual
repair before retrying; the upgrade guide now documents this. No API or
configuration changes.
### How was this patch tested?
- Previous head `e6d4a0a`: full `TestSQLScripts` suite passed (21 tests
across H2, MySQL, and PostgreSQL).
- Previous head `e6d4a0a`: the partial-execution matrix passed for all
three backends: 78 checkpoints (30 MySQL, 27 PostgreSQL, 21 H2), with
data preservation and fresh-2.0.0 schema comparisons.
- MySQL regression tests reject unrelated or reordered primary keys and
an incomplete conversion retaining the legacy unique index.
- Follow-up `d8fd07b`: only `testUpgradeToTwoZeroIsIdempotent` was
rerun, passing on H2, MySQL, and PostgreSQL (three invocations). It now
verifies that renaming an explicit H2 index changes the schema
comparison; only metadata-confirmed generated suffixes are normalized.
The MySQL error assertion distinguishes the quoted source index, and the
checkpoint selector checks its lower bound. No full core or retry-matrix
rerun was performed for this follow-up.
- Core Spotless formatting and `git diff --check` passed.
```sh
./gradlew :core:test --tests
org.apache.gravitino.storage.TestSQLScripts.testUpgradeToTwoZeroIsIdempotent
-PskipITs -PskipDockerTests=false -PskipWeb=true
```
---
.../apache/gravitino/storage/TestSQLScripts.java | 339 ++++++++++++++
docs/how-to-upgrade.md | 5 +
scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql | 28 +-
scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql | 508 ++++++++++++++++++---
.../upgrade-1.3.0-to-2.0.0-postgresql.sql | 4 +-
5 files changed, 802 insertions(+), 82 deletions(-)
diff --git
a/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
b/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
index 0997134152..e3ccdb44cc 100644
--- a/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
+++ b/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
+import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
@@ -32,6 +33,9 @@ import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.gravitino.storage.relational.TestJDBCBackend;
@@ -39,6 +43,7 @@ import
org.apache.gravitino.storage.relational.session.SqlSessionFactoryHelper;
import org.apache.ibatis.session.SqlSession;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.TestTemplate;
+import org.opentest4j.AssertionFailedError;
public class TestSQLScripts extends TestJDBCBackend {
@@ -255,6 +260,340 @@ public class TestSQLScripts extends TestJDBCBackend {
}
}
+ /** Verifies a completed upgrade can be repeated without resetting migrated
data. */
+ @TestTemplate
+ public void testUpgradeToTwoZeroIsIdempotent() throws SQLException,
IOException {
+ Path scriptDir = upgradeScriptDirectory();
+ String suffix = "-" + backendType.toLowerCase() + ".sql";
+ File upgrade = scriptDir.resolve("upgrade-1.3.0-to-2.0.0" +
suffix).toFile();
+ dropAllTables();
+ executeScript(scriptDir.resolve("schema-1.3.0" + suffix).toFile());
+ seedUpgradeData();
+ executeScript(upgrade);
+ executeStatements(
+ List.of(
+ "UPDATE idp_user_meta SET enabled = FALSE, audit_info = 'preserved
audit'",
+ "UPDATE idp_group_meta SET group_comment = 'preserved comment'",
+ "UPDATE tag_meta SET allowed_values = '[\"blue\"]'",
+ "UPDATE tag_relation_meta SET tag_value = 'blue'",
+ "UPDATE fileset_meta SET occ_version = 42",
+ "UPDATE policy_meta SET occ_version = 43"),
+ "customized migrated data");
+ Map<String, Set<String>> expectedColumns = readSchemaColumns();
+ List<String> expectedIndexes = readSchemaIndexes(expectedColumns.keySet());
+ Map<String, List<List<String>>> expectedData = readUpgradeData();
+ executeScript(upgrade);
+ executeScript(upgrade);
+ Assertions.assertEquals(expectedColumns, readSchemaColumns());
+ Assertions.assertEquals(expectedIndexes,
readSchemaIndexes(expectedColumns.keySet()));
+ Assertions.assertEquals(expectedData, readUpgradeData(), "Retry must
preserve migrated values");
+ if ("mysql".equals(backendType)) {
+ // Neither rename source nor target exists: this is damage, not an
already-applied DDL.
+ executeStatements(
+ List.of("ALTER TABLE schema_meta DROP INDEX schema_meta_idx_mid"),
+ "missing target index");
+ AssertionFailedError failure =
+ Assertions.assertThrows(AssertionFailedError.class, () ->
executeScript(upgrade));
+ Assertions.assertInstanceOf(SQLException.class, failure.getCause());
+ Assertions.assertTrue(
+ failure.getCause().getMessage().contains("'idx_mid'"),
failure.getMessage());
+ }
+ if ("h2".equals(backendType)) {
+ executeStatements(
+ List.of("ALTER INDEX idx_tid_value RENAME TO wrong_idx_tid_value"),
+ "rename explicitly named H2 index");
+ try {
+ Assertions.assertNotEquals(
+ expectedIndexes,
+ readSchemaIndexes(expectedColumns.keySet()),
+ "An explicitly named H2 index must retain its name in schema
comparisons");
+ } finally {
+ executeStatements(
+ List.of("ALTER INDEX wrong_idx_tid_value RENAME TO idx_tid_value"),
+ "restore explicitly named H2 index");
+ }
+ }
+ assertSchemaMatchesFreshInstall(expectedColumns, expectedIndexes);
+ }
+
+ /** Verifies retries after structural DDL and data updates, including
prepared DDL execution. */
+ @TestTemplate
+ public void testUpgradeToTwoZeroRetriesPartialExecution() throws
SQLException, IOException {
+ Path scriptDir = upgradeScriptDirectory();
+ String suffix = "-" + backendType.toLowerCase() + ".sql";
+ File schema = scriptDir.resolve("schema-1.3.0" + suffix).toFile();
+ File upgrade = scriptDir.resolve("upgrade-1.3.0-to-2.0.0" +
suffix).toFile();
+ List<String> statements = extractStatements(upgrade.toPath());
+ dropAllTables();
+ executeScript(schema);
+ seedUpgradeData();
+ executeScript(upgrade);
+ Map<String, Set<String>> expectedColumns = readSchemaColumns();
+ List<String> expectedIndexes = readSchemaIndexes(expectedColumns.keySet());
+ Map<String, List<List<String>>> expectedData = readUpgradeData();
+ assertSchemaMatchesFreshInstall(expectedColumns, expectedIndexes);
+
+ String lastIndexRename =
+ statements.stream()
+ .filter(sql -> sql.contains("RENAME INDEX"))
+ .reduce((first, last) -> last)
+ .orElse("");
+ boolean testedIndexRename = false;
+ for (int completed = 1; completed <= statements.size(); completed++) {
+ String last = statements.get(completed - 1).toUpperCase();
+ if (!(last.startsWith("ALTER TABLE")
+ || last.startsWith("CREATE ")
+ || last.startsWith("UPDATE ")
+ || last.startsWith("EXECUTE "))) {
+ continue;
+ }
+ // MySQL renames share one guard shape; sample the first and last rename.
+ if (last.startsWith("EXECUTE ")
+ && completed >= 3
+ && statements.get(completed - 3).contains("RENAME INDEX")) {
+ if (testedIndexRename && !statements.get(completed -
3).equals(lastIndexRename)) {
+ continue;
+ }
+ testedIndexRename = true;
+ }
+ dropAllTables();
+ executeScript(schema);
+ seedUpgradeData();
+ // Close the connection after the prefix, as if the migration process
stopped here.
+ String checkpoint = "Interrupted after statement " + completed + ": " +
last;
+ executeStatements(statements.subList(0, completed), checkpoint);
+ executeScript(upgrade);
+ Assertions.assertEquals(expectedColumns, readSchemaColumns(),
checkpoint);
+ Assertions.assertEquals(
+ expectedIndexes, readSchemaIndexes(expectedColumns.keySet()),
checkpoint);
+ Assertions.assertEquals(expectedData, readUpgradeData(), checkpoint);
+ if ("mysql".equals(backendType)) {
+ try (SqlSession session =
+
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+ Connection connection = session.getConnection();
+ ResultSet keys =
+ connection
+ .getMetaData()
+ .getPrimaryKeys(connection.getCatalog(), null,
"table_version_info")) {
+ List<String> columns = new ArrayList<>();
+ while (keys.next()) {
+ columns.add(keys.getString("COLUMN_NAME"));
+ }
+ Assertions.assertEquals(
+ Set.of("table_id", "version", "deleted_at"),
Set.copyOf(columns), checkpoint);
+ }
+ }
+ }
+ }
+
+ /** Verifies unexpected MySQL primary keys are rejected rather than silently
accepted. */
+ @TestTemplate
+ public void testUpgradeToTwoZeroRejectsUnexpectedPrimaryKey() throws
SQLException, IOException {
+ if (!"mysql".equals(backendType)) {
+ return;
+ }
+ Path scriptDir = upgradeScriptDirectory();
+ for (String key :
+ List.of("table_id", "version, table_id, deleted_at", "table_id,
version, deleted_at")) {
+ dropAllTables();
+ executeScript(scriptDir.resolve("schema-1.3.0-mysql.sql").toFile());
+ seedUpgradeData();
+ // The target key is incomplete while the old unique index remains.
+ executeStatements(
+ List.of("ALTER TABLE table_version_info ADD PRIMARY KEY (" + key +
")"),
+ "unexpected primary key");
+ boolean keepLegacyIndex = !key.startsWith("version");
+ if (!keepLegacyIndex) {
+ // Check column order independently of the legacy-index guard.
+ executeStatements(
+ List.of("ALTER TABLE table_version_info DROP INDEX
uk_table_id_version_deleted_at"),
+ "remove legacy index");
+ }
+ AssertionFailedError failure =
+ Assertions.assertThrows(
+ AssertionFailedError.class,
+ () ->
executeScript(scriptDir.resolve("upgrade-1.3.0-to-2.0.0-mysql.sql").toFile()));
+ Assertions.assertInstanceOf(SQLException.class, failure.getCause());
+ String expectedError = keepLegacyIndex ? "primary key" :
"uk_table_id_version_deleted_at";
+ Assertions.assertTrue(
+
failure.getCause().getMessage().toLowerCase().contains(expectedError),
+ failure.getMessage());
+ Assertions.assertEquals(
+ keepLegacyIndex,
+ readSchemaIndexes(Set.of("table_version_info")).stream()
+ .anyMatch(index ->
index.contains("uk_table_id_version_deleted_at")),
+ "Failed conversion must preserve the existing indexes");
+ }
+ }
+
+ private void assertSchemaMatchesFreshInstall(
+ Map<String, Set<String>> upgradedColumns, List<String> upgradedIndexes)
+ throws SQLException, IOException {
+ Map<String, Set<String>> expectedColumns = new TreeMap<>(upgradedColumns);
+ // The migration intentionally retains this legacy table; fresh 2.0
installs omit it.
+ expectedColumns.keySet().removeIf(table ->
table.equalsIgnoreCase("policy_relation_meta"));
+ List<String> expectedIndexes =
+ upgradedIndexes.stream()
+ .filter(index ->
!index.toLowerCase().startsWith("policy_relation_meta:"))
+ .toList();
+ dropAllTables();
+ executeScript(
+ upgradeScriptDirectory().resolve("schema-2.0.0-" + backendType +
".sql").toFile());
+ Map<String, Set<String>> freshColumns = readSchemaColumns();
+ Assertions.assertEquals(freshColumns, expectedColumns, "Upgrade must match
fresh 2.0 columns");
+ Assertions.assertEquals(
+ readSchemaIndexes(freshColumns.keySet()),
+ expectedIndexes,
+ "Upgrade must match fresh 2.0 indexes");
+ }
+
+ private Path upgradeScriptDirectory() {
+ String home = System.getenv("GRAVITINO_HOME");
+ Assertions.assertNotNull(home, "GRAVITINO_HOME environment variable is not
set");
+ return Path.of(home, "scripts", backendType.toLowerCase());
+ }
+
+ private void seedUpgradeData() throws SQLException {
+ executeStatements(
+ List.of(
+ "INSERT INTO idp_user_meta (user_id, user_name, password_hash, "
+ + "current_version, last_version) VALUES (1, 'user', 'hash',
7, 9)",
+ "INSERT INTO idp_group_meta (group_id, group_name) VALUES (1,
'group')",
+ "INSERT INTO tag_meta (tag_id, tag_name, metalake_id, audit_info)
VALUES (1, "
+ + "'tag', 1, '{}')",
+ "INSERT INTO tag_relation_meta (id, tag_id, metadata_object_id, "
+ + "metadata_object_type, audit_info) VALUES (1, 1, 1, 'TABLE',
'{}')",
+ "INSERT INTO fileset_meta (fileset_id, fileset_name, metalake_id,
catalog_id, "
+ + "schema_id, type, audit_info, current_version, last_version)
VALUES (1, "
+ + "'fileset', 1, 1, 1, 'MANAGED', '{}', 7, 9)",
+ "INSERT INTO policy_meta (policy_id, policy_name, policy_type,
metalake_id, "
+ + "audit_info, current_version, last_version) VALUES (1,
'policy', 'custom', 1, "
+ + "'{}', 7, 9)",
+ "INSERT INTO table_version_info (table_id, version, deleted_at)
VALUES (1, 7, 0)"),
+ "source schema data");
+ }
+
+ private Map<String, List<List<String>>> readUpgradeData() throws
SQLException {
+ Map<String, List<List<String>>> data = new TreeMap<>();
+ Map<String, String> selections =
+ Map.of(
+ "idp_user_meta",
+ "user_id, user_name, password_hash, current_version,
last_version, enabled, audit_info",
+ "idp_group_meta", "group_id, group_name, group_comment,
audit_info",
+ "tag_meta", "tag_id, tag_name, allowed_values",
+ "tag_relation_meta", "id, tag_id, metadata_object_id, tag_value",
+ "fileset_meta", "fileset_id, current_version, last_version,
occ_version",
+ "policy_meta", "policy_id, current_version, last_version,
occ_version",
+ "table_version_info", "table_id, version, deleted_at");
+ try (SqlSession session =
+
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+ Connection connection = session.getConnection();
+ Statement statement = connection.createStatement()) {
+ for (Map.Entry<String, String> entry : selections.entrySet()) {
+ List<List<String>> rows = new ArrayList<>();
+ try (ResultSet result =
+ statement.executeQuery(
+ "SELECT " + entry.getValue() + " FROM " + entry.getKey() + "
ORDER BY 1")) {
+ while (result.next()) {
+ List<String> row = new ArrayList<>();
+ for (int column = 1; column <=
result.getMetaData().getColumnCount(); column++) {
+ row.add(result.getString(column));
+ }
+ rows.add(row);
+ }
+ }
+ data.put(entry.getKey(), rows);
+ }
+ }
+ return data;
+ }
+
+ private Map<String, Set<String>> readSchemaColumns() throws SQLException {
+ Map<String, Set<String>> columns = new TreeMap<>();
+ try (SqlSession session =
+
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+ Connection connection = session.getConnection();
+ ResultSet result =
+ connection
+ .getMetaData()
+ .getColumns(connection.getCatalog(), connection.getSchema(),
"%", "%")) {
+ while (result.next()) {
+ columns
+ .computeIfAbsent(result.getString("TABLE_NAME"), key -> new
TreeSet<>())
+ .add(result.getString("COLUMN_NAME"));
+ }
+ }
+ Assertions.assertFalse(columns.isEmpty(), "Schema metadata must contain
columns");
+ return columns;
+ }
+
+ private List<String> readSchemaIndexes(Set<String> tables) throws
SQLException {
+ List<String> indexes = new ArrayList<>();
+ try (SqlSession session =
+
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+ Connection connection = session.getConnection()) {
+ Set<String> generatedIndexes = new TreeSet<>();
+ if ("h2".equals(backendType)) {
+ try (PreparedStatement statement =
+ connection.prepareStatement(
+ "SELECT INDEX_NAME FROM INFORMATION_SCHEMA.INDEXES "
+ + "WHERE TABLE_SCHEMA = ? AND IS_GENERATED = TRUE")) {
+ statement.setString(1, connection.getSchema());
+ try (ResultSet result = statement.executeQuery()) {
+ while (result.next()) {
+ generatedIndexes.add(result.getString("INDEX_NAME"));
+ }
+ }
+ }
+ }
+ for (String table : tables) {
+ try (ResultSet result =
+ connection
+ .getMetaData()
+ .getIndexInfo(
+ connection.getCatalog(), connection.getSchema(), table,
false, false)) {
+ while (result.next()) {
+ if (result.getShort("ORDINAL_POSITION") == 0) {
+ continue;
+ }
+ // H2 appends counters to generated constraint indexes. Keep the
constraint name so
+ // a named unique index and its fresh-install constraint compare
equally; explicit
+ // index names are never normalized.
+ String name = result.getString("INDEX_NAME");
+ if (generatedIndexes.contains(name)) {
+ name =
+ name.replaceFirst("_INDEX_[0-9A-F]+$", "")
+ .replaceFirst("^PRIMARY_KEY_[0-9A-F]+$", "");
+ }
+ indexes.add(
+ table
+ + ":"
+ + name
+ + ":"
+ + result.getShort("ORDINAL_POSITION")
+ + ":"
+ + result.getString("COLUMN_NAME")
+ + ":"
+ + result.getBoolean("NON_UNIQUE"));
+ }
+ }
+ }
+ }
+ indexes.sort(Comparator.naturalOrder());
+ return indexes;
+ }
+
+ private void executeStatements(List<String> statements, String context)
throws SQLException {
+ try (SqlSession session =
+
SqlSessionFactoryHelper.getInstance().getSqlSessionFactory().openSession(true);
+ Connection connection = session.getConnection();
+ Statement statement = connection.createStatement()) {
+ for (String sql : statements) {
+ Assertions.assertDoesNotThrow(() -> statement.execute(sql), context +
", sql: " + sql);
+ }
+ }
+ }
+
private void executeScript(File scriptFile) throws IOException, SQLException
{
List<String> ddls = extractStatements(scriptFile.toPath());
try (SqlSession sqlSession =
diff --git a/docs/how-to-upgrade.md b/docs/how-to-upgrade.md
index b8b465d5b5..12a8b7ae38 100644
--- a/docs/how-to-upgrade.md
+++ b/docs/how-to-upgrade.md
@@ -160,6 +160,11 @@ These scripts should run to completion without any errors.
If you
do encounter errors you need to analyze the cause and attempt to
trace it back to one of the preceding steps.
+For the MySQL, PostgreSQL, and H2 1.3.0-to-2.0.0 upgrade scripts, you can
rerun the
+script after fixing the cause of a partial failure; earlier upgrade scripts
are unchanged.
+Unexpected schema differences, such as a missing MySQL index during a rename,
require
+manual schema repair before retrying.
+
### Step 6: Verify the Upgrade
The final step of the upgrade process is validating your freshly
diff --git a/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
b/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
index f34df6f3e8..6b66d9e791 100644
--- a/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
+++ b/scripts/h2/upgrade-1.3.0-to-2.0.0-h2.sql
@@ -26,29 +26,29 @@
ALTER TABLE `table_column_version_info`
ALTER COLUMN `column_comment` VARCHAR(4096) DEFAULT '';
-ALTER TABLE `model_meta` ADD COLUMN `current_version` INT UNSIGNED NOT NULL
DEFAULT 1 COMMENT 'model current version' AFTER `model_latest_version`;
-ALTER TABLE `model_meta` ADD COLUMN `last_version` INT UNSIGNED NOT NULL
DEFAULT 1 COMMENT 'model last allocated version' AFTER `current_version`;
+ALTER TABLE `model_meta` ADD COLUMN IF NOT EXISTS `current_version` INT
UNSIGNED NOT NULL DEFAULT 1 COMMENT 'model current version' AFTER
`model_latest_version`;
+ALTER TABLE `model_meta` ADD COLUMN IF NOT EXISTS `last_version` INT UNSIGNED
NOT NULL DEFAULT 1 COMMENT 'model last allocated version' AFTER
`current_version`;
-ALTER TABLE `tag_meta` ADD COLUMN `allowed_values` CLOB DEFAULT NULL COMMENT
'tag allowed values as a JSON string array, NULL allows any value, [] allows no
value' AFTER `properties`;
+ALTER TABLE `tag_meta` ADD COLUMN IF NOT EXISTS `allowed_values` CLOB DEFAULT
NULL COMMENT 'tag allowed values as a JSON string array, NULL allows any value,
[] allows no value' AFTER `properties`;
-ALTER TABLE `tag_relation_meta` DROP INDEX `uk_ti_mi_del`;
+ALTER TABLE `tag_relation_meta` DROP CONSTRAINT IF EXISTS `uk_ti_mi_del`;
-ALTER TABLE `tag_relation_meta` ADD COLUMN `tag_value` VARCHAR(256) NOT NULL
DEFAULT '' COMMENT 'tag assignment value, empty string means no value' AFTER
`metadata_object_type`;
+ALTER TABLE `tag_relation_meta` ADD COLUMN IF NOT EXISTS `tag_value`
VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'tag assignment value, empty string
means no value' AFTER `metadata_object_type`;
-ALTER TABLE `idp_user_meta` ADD COLUMN `enabled` TINYINT(1) NOT NULL DEFAULT 1
COMMENT 'whether the user is enabled, 0 is disabled, 1 is enabled' AFTER
`password_hash`;
+ALTER TABLE `idp_user_meta` ADD COLUMN IF NOT EXISTS `enabled` TINYINT(1) NOT
NULL DEFAULT 1 COMMENT 'whether the user is enabled, 0 is disabled, 1 is
enabled' AFTER `password_hash`;
-ALTER TABLE `idp_group_meta` ADD COLUMN `group_comment` VARCHAR(1024) DEFAULT
'' COMMENT 'idp group comment' AFTER `group_name`;
+ALTER TABLE `idp_group_meta` ADD COLUMN IF NOT EXISTS `group_comment`
VARCHAR(1024) DEFAULT '' COMMENT 'idp group comment' AFTER `group_name`;
-ALTER TABLE `idp_user_meta` ADD COLUMN `audit_info` CLOB NOT NULL DEFAULT '{}'
COMMENT 'idp user audit info' AFTER `enabled`;
+ALTER TABLE `idp_user_meta` ADD COLUMN IF NOT EXISTS `audit_info` CLOB NOT
NULL DEFAULT '{}' COMMENT 'idp user audit info' AFTER `enabled`;
-ALTER TABLE `idp_group_meta` ADD COLUMN `audit_info` CLOB NOT NULL DEFAULT
'{}' COMMENT 'idp group audit info' AFTER `group_comment`;
+ALTER TABLE `idp_group_meta` ADD COLUMN IF NOT EXISTS `audit_info` CLOB NOT
NULL DEFAULT '{}' COMMENT 'idp group audit info' AFTER `group_comment`;
-ALTER TABLE `idp_user_group_rel` ADD COLUMN `audit_info` CLOB NOT NULL DEFAULT
'{}' COMMENT 'idp user group relation audit info' AFTER `group_id`;
+ALTER TABLE `idp_user_group_rel` ADD COLUMN IF NOT EXISTS `audit_info` CLOB
NOT NULL DEFAULT '{}' COMMENT 'idp user group relation audit info' AFTER
`group_id`;
CREATE UNIQUE INDEX IF NOT EXISTS `uk_ti_mi_mo_tv_del` ON `tag_relation_meta`
(`tag_id`, `metadata_object_id`, `metadata_object_type`, `tag_value`,
`deleted_at`);
CREATE INDEX IF NOT EXISTS `idx_tid_value` ON `tag_relation_meta` (`tag_id`,
`tag_value`);
-ALTER TABLE `job_run_meta` ADD COLUMN `job_started_at` BIGINT(20) UNSIGNED NOT
NULL DEFAULT 0 COMMENT 'job started at' AFTER `job_run_status`;
+ALTER TABLE `job_run_meta` ADD COLUMN IF NOT EXISTS `job_started_at`
BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT 'job started at' AFTER
`job_run_status`;
CREATE TABLE IF NOT EXISTS `policy_tag_relation_meta` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
@@ -64,7 +64,7 @@ CREATE TABLE IF NOT EXISTS `policy_tag_relation_meta` (
KEY `policy_tag_relation_meta_idx_tag_id` (`tag_id`)
) ENGINE=InnoDB;
-ALTER TABLE `job_run_meta` ADD COLUMN `runtime_job_template` CLOB DEFAULT NULL
COMMENT 'job run runtime job template' AFTER `job_finished_at`;
+ALTER TABLE `job_run_meta` ADD COLUMN IF NOT EXISTS `runtime_job_template`
CLOB DEFAULT NULL COMMENT 'job run runtime job template' AFTER
`job_finished_at`;
CREATE TABLE IF NOT EXISTS `semantic_model_meta` (
`semantic_model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'semantic model
id',
@@ -122,7 +122,7 @@ UPDATE `owner_meta`
-- only when the stored snapshot changes. The default is the whole backfill,
because `occ_version`
-- is only ever compared against itself on the same row.
ALTER TABLE `fileset_meta`
- ADD COLUMN `occ_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'fileset
optimistic concurrency version' AFTER `last_version`;
+ ADD COLUMN IF NOT EXISTS `occ_version` INT UNSIGNED NOT NULL DEFAULT 1
COMMENT 'fileset optimistic concurrency version' AFTER `last_version`;
ALTER TABLE `policy_meta`
- ADD COLUMN `occ_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'policy
optimistic concurrency version' AFTER `last_version`;
+ ADD COLUMN IF NOT EXISTS `occ_version` INT UNSIGNED NOT NULL DEFAULT 1
COMMENT 'policy optimistic concurrency version' AFTER `last_version`;
diff --git a/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
b/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
index 121271190b..2815471571 100644
--- a/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
+++ b/scripts/mysql/upgrade-1.3.0-to-2.0.0-mysql.sql
@@ -17,30 +17,64 @@
-- under the License.
--
+-- MySQL has no ADD COLUMN / CREATE INDEX IF NOT EXISTS. Guard completed DDL
+-- so a failed upgrade can be restarted from the beginning without --force.
+-- Keep each operation in this session; no stored routines or extra privileges
are required.
+
-- Preserve policy_relation_meta from pre-2.0 installations, including its
existing data.
-- The 2.0 server no longer reads direct object-policy assignments from this
table.
ALTER TABLE `table_column_version_info`
MODIFY COLUMN `column_comment` VARCHAR(4096) DEFAULT '' COMMENT 'column
comment';
-ALTER TABLE `tag_meta`
- ADD COLUMN `allowed_values` MEDIUMTEXT DEFAULT NULL COMMENT 'tag allowed
values as a JSON string array, NULL allows any value, [] allows no value' AFTER
`properties`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'tag_meta' AND
column_name = 'allowed_values'),
+ 'SELECT 1',
+ 'ALTER TABLE `tag_meta` ADD COLUMN `allowed_values` MEDIUMTEXT DEFAULT
NULL COMMENT ''tag allowed values as a JSON string array, NULL allows any
value, [] allows no value'' AFTER `properties`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `tag_relation_meta`
- DROP INDEX `uk_ti_mi_mo_del`;
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
index_name = 'uk_ti_mi_mo_del'),
+ 'SELECT 1',
+ 'ALTER TABLE `tag_relation_meta` DROP INDEX `uk_ti_mi_mo_del`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `tag_relation_meta`
- ADD COLUMN `tag_value` VARCHAR(256) NOT NULL DEFAULT '' COMMENT 'tag
assignment value, empty string means no value' AFTER `metadata_object_type`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
column_name = 'tag_value'),
+ 'SELECT 1',
+ 'ALTER TABLE `tag_relation_meta` ADD COLUMN `tag_value` VARCHAR(256) NOT
NULL DEFAULT '''' COMMENT ''tag assignment value, empty string means no value''
AFTER `metadata_object_type`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `idp_user_meta`
- ADD COLUMN `enabled` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'whether the
user is enabled, 0 is disabled, 1 is enabled' AFTER `password_hash`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'idp_user_meta' AND
column_name = 'enabled'),
+ 'SELECT 1',
+ 'ALTER TABLE `idp_user_meta` ADD COLUMN `enabled` TINYINT(1) NOT NULL
DEFAULT 1 COMMENT ''whether the user is enabled, 0 is disabled, 1 is enabled''
AFTER `password_hash`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `idp_group_meta`
- ADD COLUMN `group_comment` VARCHAR(1024) DEFAULT '' COMMENT 'idp group
comment' AFTER `group_name`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'idp_group_meta' AND
column_name = 'group_comment'),
+ 'SELECT 1',
+ 'ALTER TABLE `idp_group_meta` ADD COLUMN `group_comment` VARCHAR(1024)
DEFAULT '''' COMMENT ''idp group comment'' AFTER `group_name`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-- add audit_info as nullable first for MySQL 5.7 compatibility (TEXT cannot
have defaults)
-ALTER TABLE `idp_user_meta`
- ADD COLUMN `audit_info` MEDIUMTEXT COMMENT 'idp user audit info' AFTER
`enabled`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'idp_user_meta' AND
column_name = 'audit_info'),
+ 'SELECT 1',
+ 'ALTER TABLE `idp_user_meta` ADD COLUMN `audit_info` MEDIUMTEXT COMMENT
''idp user audit info'' AFTER `enabled`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
UPDATE `idp_user_meta`
SET `audit_info` = '{}'
@@ -49,8 +83,13 @@ UPDATE `idp_user_meta`
ALTER TABLE `idp_user_meta`
MODIFY COLUMN `audit_info` MEDIUMTEXT NOT NULL COMMENT 'idp user audit
info' AFTER `enabled`;
-ALTER TABLE `idp_group_meta`
- ADD COLUMN `audit_info` MEDIUMTEXT COMMENT 'idp group audit info' AFTER
`group_comment`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'idp_group_meta' AND
column_name = 'audit_info'),
+ 'SELECT 1',
+ 'ALTER TABLE `idp_group_meta` ADD COLUMN `audit_info` MEDIUMTEXT COMMENT
''idp group audit info'' AFTER `group_comment`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
UPDATE `idp_group_meta`
SET `audit_info` = '{}'
@@ -59,8 +98,13 @@ UPDATE `idp_group_meta`
ALTER TABLE `idp_group_meta`
MODIFY COLUMN `audit_info` MEDIUMTEXT NOT NULL COMMENT 'idp group audit
info' AFTER `group_comment`;
-ALTER TABLE `idp_user_group_rel`
- ADD COLUMN `audit_info` MEDIUMTEXT COMMENT 'idp user group relation audit
info' AFTER `group_id`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'idp_user_group_rel' AND
column_name = 'audit_info'),
+ 'SELECT 1',
+ 'ALTER TABLE `idp_user_group_rel` ADD COLUMN `audit_info` MEDIUMTEXT
COMMENT ''idp user group relation audit info'' AFTER `group_id`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
UPDATE `idp_user_group_rel`
SET `audit_info` = '{}'
@@ -69,48 +113,351 @@ UPDATE `idp_user_group_rel`
ALTER TABLE `idp_user_group_rel`
MODIFY COLUMN `audit_info` MEDIUMTEXT NOT NULL COMMENT 'idp user group
relation audit info' AFTER `group_id`;
-CREATE UNIQUE INDEX `uk_ti_mi_mo_tv_del` ON `tag_relation_meta` (`tag_id`,
`metadata_object_id`, `metadata_object_type`, `tag_value`, `deleted_at`);
-CREATE INDEX `idx_tid_value` ON `tag_relation_meta` (`tag_id`, `tag_value`);
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
index_name = 'uk_ti_mi_mo_tv_del'),
+ 'SELECT 1',
+ 'CREATE UNIQUE INDEX `uk_ti_mi_mo_tv_del` ON `tag_relation_meta`
(`tag_id`, `metadata_object_id`, `metadata_object_type`, `tag_value`,
`deleted_at`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
index_name = 'idx_tid_value'),
+ 'SELECT 1',
+ 'CREATE INDEX `idx_tid_value` ON `tag_relation_meta` (`tag_id`,
`tag_value`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-- Index names are only scoped per-table in MySQL, so the same name could be
-- reused across tables. Prefix each reused name with its table name so that
-- every index name is unique across the whole schema. A database upgraded
-- from 1.3.0 ends up with the same index names as a fresh 2.0.0 install.
-ALTER TABLE `schema_meta` RENAME INDEX `idx_mid` TO `schema_meta_idx_mid`;
-ALTER TABLE `table_meta` RENAME INDEX `uk_sid_tn_del` TO
`table_meta_uk_sid_tn_del`;
-ALTER TABLE `table_meta` RENAME INDEX `idx_mid` TO `table_meta_idx_mid`;
-ALTER TABLE `table_meta` RENAME INDEX `idx_cid` TO `table_meta_idx_cid`;
-ALTER TABLE `table_column_version_info` RENAME INDEX `idx_mid` TO
`table_column_version_info_idx_mid`;
-ALTER TABLE `table_column_version_info` RENAME INDEX `idx_cid` TO
`table_column_version_info_idx_cid`;
-ALTER TABLE `table_column_version_info` RENAME INDEX `idx_sid` TO
`table_column_version_info_idx_sid`;
-ALTER TABLE `fileset_meta` RENAME INDEX `uk_sid_fn_del` TO
`fileset_meta_uk_sid_fn_del`;
-ALTER TABLE `fileset_meta` RENAME INDEX `idx_mid` TO `fileset_meta_idx_mid`;
-ALTER TABLE `fileset_meta` RENAME INDEX `idx_cid` TO `fileset_meta_idx_cid`;
-ALTER TABLE `fileset_version_info` RENAME INDEX `idx_mid` TO
`fileset_version_info_idx_mid`;
-ALTER TABLE `fileset_version_info` RENAME INDEX `idx_cid` TO
`fileset_version_info_idx_cid`;
-ALTER TABLE `fileset_version_info` RENAME INDEX `idx_sid` TO
`fileset_version_info_idx_sid`;
-ALTER TABLE `topic_meta` RENAME INDEX `uk_sid_tn_del` TO
`topic_meta_uk_sid_tn_del`;
-ALTER TABLE `topic_meta` RENAME INDEX `idx_mid` TO `topic_meta_idx_mid`;
-ALTER TABLE `topic_meta` RENAME INDEX `idx_cid` TO `topic_meta_idx_cid`;
-ALTER TABLE `user_role_rel` RENAME INDEX `idx_rid` TO `user_role_rel_idx_rid`;
-ALTER TABLE `group_role_rel` RENAME INDEX `idx_rid` TO
`group_role_rel_idx_rid`;
-ALTER TABLE `tag_relation_meta` RENAME INDEX `idx_mid` TO
`tag_relation_meta_idx_mid`;
-ALTER TABLE `model_meta` RENAME INDEX `idx_mid` TO `model_meta_idx_mid`;
-ALTER TABLE `model_meta` RENAME INDEX `idx_cid` TO `model_meta_idx_cid`;
-
-ALTER TABLE `model_meta`
- ADD COLUMN `current_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT
'model current version' AFTER `model_latest_version`,
- ADD COLUMN `last_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'model
last allocated version' AFTER `current_version`;
-ALTER TABLE `model_version_info` RENAME INDEX `idx_mid` TO
`model_version_info_idx_mid`;
-ALTER TABLE `model_version_info` RENAME INDEX `idx_cid` TO
`model_version_info_idx_cid`;
-ALTER TABLE `model_version_info` RENAME INDEX `idx_sid` TO
`model_version_info_idx_sid`;
-ALTER TABLE `policy_version_info` RENAME INDEX `idx_mid` TO
`policy_version_info_idx_mid`;
-ALTER TABLE `function_meta` RENAME INDEX `uk_sid_fn_del` TO
`function_meta_uk_sid_fn_del`;
-ALTER TABLE `function_meta` RENAME INDEX `idx_mid` TO `function_meta_idx_mid`;
-ALTER TABLE `function_meta` RENAME INDEX `idx_cid` TO `function_meta_idx_cid`;
-ALTER TABLE `function_version_info` RENAME INDEX `idx_mid` TO
`function_version_info_idx_mid`;
-ALTER TABLE `function_version_info` RENAME INDEX `idx_cid` TO
`function_version_info_idx_cid`;
-ALTER TABLE `function_version_info` RENAME INDEX `idx_sid` TO
`function_version_info_idx_sid`;
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'schema_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'schema_meta' AND
index_name = 'schema_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `schema_meta` RENAME INDEX `idx_mid` TO
`schema_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'uk_sid_tn_del')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'table_meta_uk_sid_tn_del'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_meta` RENAME INDEX `uk_sid_tn_del` TO
`table_meta_uk_sid_tn_del`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'table_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_meta` RENAME INDEX `idx_mid` TO `table_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_meta' AND
index_name = 'table_meta_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_meta` RENAME INDEX `idx_cid` TO `table_meta_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name =
'table_column_version_info_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_column_version_info` RENAME INDEX `idx_mid` TO
`table_column_version_info_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name =
'table_column_version_info_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_column_version_info` RENAME INDEX `idx_cid` TO
`table_column_version_info_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name = 'idx_sid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name =
'table_column_version_info' AND index_name =
'table_column_version_info_idx_sid'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_column_version_info` RENAME INDEX `idx_sid` TO
`table_column_version_info_idx_sid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'uk_sid_fn_del')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'fileset_meta_uk_sid_fn_del'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_meta` RENAME INDEX `uk_sid_fn_del` TO
`fileset_meta_uk_sid_fn_del`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'fileset_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_meta` RENAME INDEX `idx_mid` TO
`fileset_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
index_name = 'fileset_meta_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_meta` RENAME INDEX `idx_cid` TO
`fileset_meta_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'fileset_version_info_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_version_info` RENAME INDEX `idx_mid` TO
`fileset_version_info_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'fileset_version_info_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_version_info` RENAME INDEX `idx_cid` TO
`fileset_version_info_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'idx_sid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info'
AND index_name = 'fileset_version_info_idx_sid'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_version_info` RENAME INDEX `idx_sid` TO
`fileset_version_info_idx_sid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'uk_sid_tn_del')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'topic_meta_uk_sid_tn_del'),
+ 'SELECT 1',
+ 'ALTER TABLE `topic_meta` RENAME INDEX `uk_sid_tn_del` TO
`topic_meta_uk_sid_tn_del`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'topic_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `topic_meta` RENAME INDEX `idx_mid` TO `topic_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'topic_meta' AND
index_name = 'topic_meta_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `topic_meta` RENAME INDEX `idx_cid` TO `topic_meta_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'user_role_rel' AND
index_name = 'idx_rid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'user_role_rel' AND
index_name = 'user_role_rel_idx_rid'),
+ 'SELECT 1',
+ 'ALTER TABLE `user_role_rel` RENAME INDEX `idx_rid` TO
`user_role_rel_idx_rid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'group_role_rel' AND
index_name = 'idx_rid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'group_role_rel' AND
index_name = 'group_role_rel_idx_rid'),
+ 'SELECT 1',
+ 'ALTER TABLE `group_role_rel` RENAME INDEX `idx_rid` TO
`group_role_rel_idx_rid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'tag_relation_meta' AND
index_name = 'tag_relation_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `tag_relation_meta` RENAME INDEX `idx_mid` TO
`tag_relation_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
index_name = 'model_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_meta` RENAME INDEX `idx_mid` TO `model_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
index_name = 'model_meta_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_meta` RENAME INDEX `idx_cid` TO `model_meta_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
column_name = 'current_version'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_meta` ADD COLUMN `current_version` INT UNSIGNED NOT
NULL DEFAULT 1 COMMENT ''model current version'' AFTER `model_latest_version`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'model_meta' AND
column_name = 'last_version'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_meta` ADD COLUMN `last_version` INT UNSIGNED NOT NULL
DEFAULT 1 COMMENT ''model last allocated version'' AFTER `current_version`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'model_version_info_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_version_info` RENAME INDEX `idx_mid` TO
`model_version_info_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'model_version_info_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_version_info` RENAME INDEX `idx_cid` TO
`model_version_info_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'idx_sid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND
index_name = 'model_version_info_idx_sid'),
+ 'SELECT 1',
+ 'ALTER TABLE `model_version_info` RENAME INDEX `idx_sid` TO
`model_version_info_idx_sid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'policy_version_info' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'policy_version_info' AND
index_name = 'policy_version_info_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `policy_version_info` RENAME INDEX `idx_mid` TO
`policy_version_info_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'uk_sid_fn_del')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'function_meta_uk_sid_fn_del'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_meta` RENAME INDEX `uk_sid_fn_del` TO
`function_meta_uk_sid_fn_del`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'function_meta_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_meta` RENAME INDEX `idx_mid` TO
`function_meta_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_meta' AND
index_name = 'function_meta_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_meta` RENAME INDEX `idx_cid` TO
`function_meta_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'idx_mid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'function_version_info_idx_mid'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_version_info` RENAME INDEX `idx_mid` TO
`function_version_info_idx_mid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'idx_cid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'function_version_info_idx_cid'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_version_info` RENAME INDEX `idx_cid` TO
`function_version_info_idx_cid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
+SET @ddl = IF(NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'idx_sid')
+ AND EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'function_version_info'
AND index_name = 'function_version_info_idx_sid'),
+ 'SELECT 1',
+ 'ALTER TABLE `function_version_info` RENAME INDEX `idx_sid` TO
`function_version_info_idx_sid`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-- `table_version_info` had no primary key. `version` and `deleted_at` become
-- NOT NULL and the existing unique key is promoted to the primary key, which
@@ -121,14 +468,28 @@ ALTER TABLE `function_version_info` RENAME INDEX
`idx_sid` TO `function_version_
-- SELECT COUNT(*) FROM `table_version_info`
-- WHERE `version` IS NULL OR `deleted_at` IS NULL;
-- The statement below fails rather than silently coercing NULL to 0.
-ALTER TABLE `table_version_info`
- MODIFY COLUMN `version` BIGINT(20) UNSIGNED NOT NULL COMMENT 'table
current version',
- MODIFY COLUMN `deleted_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT
'table deletion timestamp, 0 means not deleted',
- DROP INDEX `uk_table_id_version_deleted_at`,
- ADD PRIMARY KEY (`table_id`, `version`, `deleted_at`);
+-- Skip only the exact completed conversion; unexpected keys must fail the
ALTER.
+SET @ddl = IF((SELECT COUNT(*) = 3 AND SUM(
+ (seq_in_index = 1 AND column_name = 'table_id') OR
+ (seq_in_index = 2 AND column_name = 'version') OR
+ (seq_in_index = 3 AND column_name = 'deleted_at')) = 3
+ FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_version_info' AND
index_name = 'PRIMARY')
+ AND NOT EXISTS(SELECT 1 FROM information_schema.statistics
+ WHERE table_schema = DATABASE() AND table_name = 'table_version_info' AND
index_name = 'uk_table_id_version_deleted_at'),
+ 'SELECT 1',
+ 'ALTER TABLE `table_version_info` MODIFY COLUMN `version` BIGINT(20)
UNSIGNED NOT NULL COMMENT ''table current version'', MODIFY COLUMN `deleted_at`
BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT ''table deletion timestamp, 0
means not deleted'', DROP INDEX `uk_table_id_version_deleted_at`, ADD PRIMARY
KEY (`table_id`, `version`, `deleted_at`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `job_run_meta`
- ADD COLUMN `job_started_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT
'job started at' AFTER `job_run_status`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'job_run_meta' AND
column_name = 'job_started_at'),
+ 'SELECT 1',
+ 'ALTER TABLE `job_run_meta` ADD COLUMN `job_started_at` BIGINT(20)
UNSIGNED NOT NULL DEFAULT 0 COMMENT ''job started at'' AFTER `job_run_status`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS `policy_tag_relation_meta` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment
id',
@@ -144,8 +505,13 @@ CREATE TABLE IF NOT EXISTS `policy_tag_relation_meta` (
KEY `policy_tag_relation_meta_idx_tag_id` (`tag_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'policy
tag relation';
-ALTER TABLE `job_run_meta`
- ADD COLUMN `runtime_job_template` MEDIUMTEXT DEFAULT NULL COMMENT 'job run
runtime job template' AFTER `job_finished_at`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'job_run_meta' AND
column_name = 'runtime_job_template'),
+ 'SELECT 1',
+ 'ALTER TABLE `job_run_meta` ADD COLUMN `runtime_job_template` MEDIUMTEXT
DEFAULT NULL COMMENT ''job run runtime job template'' AFTER `job_finished_at`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
CREATE TABLE IF NOT EXISTS `semantic_model_meta` (
`semantic_model_id` BIGINT(20) UNSIGNED NOT NULL COMMENT 'semantic model
id',
@@ -204,8 +570,18 @@ UPDATE `owner_meta` o
-- in that snapshot changed. `occ_version` takes over the CAS;
`current_version` again advances
-- only when the stored snapshot changes. The default is the whole backfill,
because `occ_version`
-- is only ever compared against itself on the same row.
-ALTER TABLE `fileset_meta`
- ADD COLUMN `occ_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'fileset
optimistic concurrency version' AFTER `last_version`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'fileset_meta' AND
column_name = 'occ_version'),
+ 'SELECT 1',
+ 'ALTER TABLE `fileset_meta` ADD COLUMN `occ_version` INT UNSIGNED NOT NULL
DEFAULT 1 COMMENT ''fileset optimistic concurrency version'' AFTER
`last_version`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
-ALTER TABLE `policy_meta`
- ADD COLUMN `occ_version` INT UNSIGNED NOT NULL DEFAULT 1 COMMENT 'policy
optimistic concurrency version' AFTER `last_version`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+ WHERE table_schema = DATABASE() AND table_name = 'policy_meta' AND
column_name = 'occ_version'),
+ 'SELECT 1',
+ 'ALTER TABLE `policy_meta` ADD COLUMN `occ_version` INT UNSIGNED NOT NULL
DEFAULT 1 COMMENT ''policy optimistic concurrency version'' AFTER
`last_version`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
diff --git a/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
b/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
index 06d0412acf..771b9ccd67 100644
--- a/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
+++ b/scripts/postgresql/upgrade-1.3.0-to-2.0.0-postgresql.sql
@@ -170,8 +170,8 @@ UPDATE owner_meta
-- that snapshot changed. occ_version takes over the CAS; current_version
again advances only when
-- the stored snapshot changes. The default is the whole backfill, because
occ_version is only ever
-- compared against itself on the same row.
-ALTER TABLE fileset_meta ADD COLUMN occ_version INT NOT NULL DEFAULT 1;
+ALTER TABLE fileset_meta ADD COLUMN IF NOT EXISTS occ_version INT NOT NULL
DEFAULT 1;
COMMENT ON COLUMN fileset_meta.occ_version IS 'fileset optimistic concurrency
version';
-ALTER TABLE policy_meta ADD COLUMN occ_version INT NOT NULL DEFAULT 1;
+ALTER TABLE policy_meta ADD COLUMN IF NOT EXISTS occ_version INT NOT NULL
DEFAULT 1;
COMMENT ON COLUMN policy_meta.occ_version IS 'policy optimistic concurrency
version';