This is an automated email from the ASF dual-hosted git repository.

yuqi1129 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 b28427af7c [#14312] improvement(scripts): Make the remaining MySQL 
upgrade scripts retryable (#14320)
b28427af7c is described below

commit b28427af7c5f3bfa96fd1c83fa111d09092bdf04
Author: li jie <[email protected]>
AuthorDate: Wed Oct 28 22:58:06 2026 +0800

    [#14312] improvement(scripts): Make the remaining MySQL upgrade scripts 
retryable (#14320)
    
    ### What changes were proposed in this pull request?
    
    - Guard the unique-key conversions in `upgrade-0.8.0-to-0.9.0-mysql.sql`
    and `upgrade-0.9.0-to-1.0.0-mysql.sql` (ADD COLUMN, DROP INDEX, ADD
    UNIQUE KEY, ALTER COLUMN DROP DEFAULT) with `information_schema` checks,
    so only the exact completed steps are skipped on retry; unexpected
    schema states still fail instead of being silently accepted.
    - Guard the ADD COLUMN and CREATE INDEX statements in
    `upgrade-1.2.0-to-1.3.0-mysql.sql`, and let the `view_meta.audit_info`
    backfill also cover the empty string left by a non-strict SQL mode, so a
    retry can finish a partial backfill.
    - Add `testMySqlUpgradeScriptsRetryable` to `TestSQLScripts`: upgrade
    step by step from the 0.5.0 schema, re-run every MySQL upgrade script,
    and compare the resulting columns and indexes with a fresh install of
    the latest schema.
    - Document the retry behavior in the upgrade guide.
    
    The remaining scripts up to 1.2.0 are unchanged: they only contain
    `CREATE TABLE IF NOT EXISTS` and `MODIFY COLUMN` statements, which are
    naturally re-runnable. No stored routines or extra privileges are
    required; all guards run inside the upgrade session via
    `PREPARE`/`EXECUTE`.
    
    ### Why are the changes needed?
    
    The MySQL upgrade scripts are not retryable today: if an upgrade fails
    halfway, the already-applied statements make the re-run fail, so users
    cannot recover by simply retrying the script. The 1.3.0-to-2.0.0 scripts
    were made retryable by #13689; this PR covers the remaining MySQL
    scripts, following the same approach.
    
    Fix: #14312
    
    ### Does this PR introduce _any_ user-facing change?
    
    No API or configuration changes. Operators can now re-run the MySQL
    upgrade scripts (up to upgrade-1.2.0-to-1.3.0) after a partial or
    completed run without `--force`.
    
    ### How was this patch tested?
    
    - `./gradlew :core:test --tests
    org.apache.gravitino.storage.TestSQLScripts -PskipITs` (embedded H2
    mode; the new test skips non-MySQL backends)
    - The MySQL path is exercised by CI with Docker: `./gradlew :core:test
    --tests org.apache.gravitino.storage.TestSQLScripts -PskipITs
    -PskipDockerTests=false`
    - `./gradlew :core:spotlessApply :core:compileTestJava`
    
    ---------
    
    Co-authored-by: lijie <[email protected]>
    Co-authored-by: Qi Yu <[email protected]>
---
 .../apache/gravitino/storage/TestSQLScripts.java   | 94 ++++++++++++++++++++++
 docs/how-to-upgrade.md                             |  9 ++-
 scripts/mysql/upgrade-0.8.0-to-0.9.0-mysql.sql     | 39 ++++++++-
 scripts/mysql/upgrade-0.9.0-to-1.0.0-mysql.sql     | 39 ++++++++-
 scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql     | 87 +++++++++++++++-----
 5 files changed, 234 insertions(+), 34 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 813934acaa..45158c6eb8 100644
--- a/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
+++ b/core/src/test/java/org/apache/gravitino/storage/TestSQLScripts.java
@@ -134,6 +134,100 @@ public class TestSQLScripts extends TestJDBCBackend {
     }
   }
 
+  /**
+   * Verifies every MySQL upgrade script from 0.5.0 onward can be re-run as a 
whole after a
+   * completed upgrade: guarded DDL (ADD COLUMN, DROP INDEX, CREATE INDEX, 
...) must skip instead of
+   * failing. The retried upgrade must keep the columns and indexes of the 
first upgrade, and match
+   * a fresh install of the target schema apart from the legacy tables and 
columns that upgrades
+   * intentionally retain.
+   */
+  @TestTemplate
+  public void testMySqlUpgradeScriptsRetryable() throws SQLException, 
IOException {
+    if (!"mysql".equals(backendType)) {
+      return;
+    }
+
+    Path scriptDir = upgradeScriptDirectory();
+    File[] scriptFiles = scriptDir.toFile().listFiles();
+    Assertions.assertNotNull(scriptFiles, "No script files found in " + 
scriptDir);
+    Arrays.sort(scriptFiles, Comparator.comparing(File::getName));
+
+    Pattern upgradePattern = 
Pattern.compile("upgrade-([\\d.]+)-to-([\\d.]+)-mysql\\.sql");
+    List<File> upgrades = new ArrayList<>();
+    String fromVersion = null;
+    for (File scriptFile : scriptFiles) {
+      Matcher upgradeMatcher = upgradePattern.matcher(scriptFile.getName());
+      if (!upgradeMatcher.matches()) {
+        continue;
+      }
+      if (fromVersion == null) {
+        fromVersion = upgradeMatcher.group(1);
+      }
+      upgrades.add(scriptFile);
+    }
+    Assertions.assertFalse(upgrades.isEmpty(), "No MySQL upgrade scripts found 
in " + scriptDir);
+
+    // Upgrade step by step from the oldest schema, then re-run every upgrade 
script.
+    dropAllTables();
+    executeScript(scriptDir.resolve("schema-" + fromVersion + 
"-mysql.sql").toFile());
+    for (File upgrade : upgrades) {
+      executeScript(upgrade);
+    }
+    Map<String, Set<String>> upgradedColumns = readSchemaColumns();
+    List<String> upgradedIndexes = readSchemaIndexes(upgradedColumns.keySet());
+    for (File upgrade : upgrades) {
+      executeScript(upgrade);
+    }
+    Matcher lastMatcher = upgradePattern.matcher(upgrades.get(upgrades.size() 
- 1).getName());
+    Assertions.assertTrue(lastMatcher.matches());
+    String latestVersion = lastMatcher.group(2);
+    Map<String, Set<String>> retriedColumns = readSchemaColumns();
+    List<String> retriedIndexes = readSchemaIndexes(retriedColumns.keySet());
+    Assertions.assertEquals(
+        upgradedColumns, retriedColumns, "Retried upgrades must not change 
upgraded columns");
+    Assertions.assertEquals(
+        upgradedIndexes, retriedIndexes, "Retried upgrades must not change 
upgraded indexes");
+
+    // Compare with a fresh install of the latest schema. Tables missing from 
it were
+    // intentionally retained by the upgrades (e.g. the legacy 
policy_relation_meta table), so
+    // they are excluded from the comparison. So are the role_meta columns 
that the 0.6.0 upgrade
+    // only marked as deprecated and never dropped.
+    dropAllTables();
+    executeScript(scriptDir.resolve("schema-" + latestVersion + 
"-mysql.sql").toFile());
+    Map<String, Set<String>> freshColumns = readSchemaColumns();
+    Map<String, Set<String>> retainedColumns =
+        Map.of(
+            "role_meta",
+            Set.of(
+                "securable_object_full_name",
+                "securable_object_type",
+                "privileges",
+                "privilege_conditions"));
+    Map<String, Set<String>> retriedComparableColumns = new TreeMap<>();
+    retriedColumns.forEach(
+        (table, columns) -> {
+          if (freshColumns.containsKey(table)) {
+            Set<String> comparableColumns = new TreeSet<>(columns);
+            comparableColumns.removeAll(retainedColumns.getOrDefault(table, 
Set.of()));
+            retriedComparableColumns.put(table, comparableColumns);
+          }
+        });
+    Assertions.assertEquals(
+        freshColumns,
+        retriedComparableColumns,
+        "Retried upgrades must match fresh " + latestVersion + " columns");
+    // The 0.6.0 upgrade created tag_meta's unique key as uk_mn_tn_del, while 
every fresh schema
+    // names the same key uk_mi_tn_del.
+    Assertions.assertEquals(
+        readSchemaIndexes(freshColumns.keySet()),
+        retriedIndexes.stream()
+            .filter(index -> freshColumns.containsKey(index.split(":")[0]))
+            .map(index -> index.replaceFirst("^tag_meta:uk_mn_tn_del:", 
"tag_meta:uk_mi_tn_del:"))
+            .sorted()
+            .toList(),
+        "Retried upgrades must match fresh " + latestVersion + " indexes");
+  }
+
   /**
    * The owner unique key allows one live row per (owner, object). Rows left 
by concurrent
    * assignments are merged during the upgrade: the newest live row (largest 
id) stays, while older
diff --git a/docs/how-to-upgrade.md b/docs/how-to-upgrade.md
index 12a8b7ae38..268ab09599 100644
--- a/docs/how-to-upgrade.md
+++ b/docs/how-to-upgrade.md
@@ -160,10 +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.
+The MySQL upgrade scripts can be rerun after fixing the cause of a partial 
failure;
+they guard column additions, index creation and deletion, and unique-key 
conversions so a
+restarted upgrade skips the steps that already completed. The PostgreSQL and H2
+1.3.0-to-2.0.0 upgrade scripts are retryable as well. Unexpected schema 
differences, such
+as a missing MySQL index during a rename, require manual schema repair before 
retrying.
 
 ### Step 6: Verify the Upgrade
 
diff --git a/scripts/mysql/upgrade-0.8.0-to-0.9.0-mysql.sql 
b/scripts/mysql/upgrade-0.8.0-to-0.9.0-mysql.sql
index eb9ce970e0..7170c10d6b 100644
--- a/scripts/mysql/upgrade-0.8.0-to-0.9.0-mysql.sql
+++ b/scripts/mysql/upgrade-0.8.0-to-0.9.0-mysql.sql
@@ -17,9 +17,40 @@
 -- under the License.
 --
 
+-- MySQL has no ADD COLUMN IF NOT EXISTS. Guard the unique-key conversion so
+-- a failed upgrade can be restarted from the beginning: skip exactly the
+-- completed steps and fail on any other unexpected schema state.
+
 -- using default 'unknown' to fill in the new column for compatibility
-ALTER TABLE `fileset_version_info` ADD COLUMN `storage_location_name` 
VARCHAR(256) NOT NULL DEFAULT 'unknown' COMMENT 'fileset storage location name' 
AFTER `properties`;
-ALTER TABLE `fileset_version_info` DROP INDEX `uk_fid_ver_del`;
-ALTER TABLE `fileset_version_info` ADD CONSTRAINT `uk_fid_ver_sto_del` UNIQUE 
KEY (`fileset_id`, `version`, `storage_location_name`, `deleted_at`);
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info' 
AND column_name = 'storage_location_name'),
+    'SELECT 1',
+    'ALTER TABLE `fileset_version_info` ADD COLUMN `storage_location_name` 
VARCHAR(256) NOT NULL DEFAULT ''unknown'' COMMENT ''fileset storage location 
name'' AFTER `properties`');
+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 = 'uk_fid_ver_del'),
+    'SELECT 1',
+    'ALTER TABLE `fileset_version_info` DROP INDEX `uk_fid_ver_del`');
+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 = 'fileset_version_info' 
AND index_name = 'uk_fid_ver_sto_del'),
+    'SELECT 1',
+    'ALTER TABLE `fileset_version_info` ADD CONSTRAINT `uk_fid_ver_sto_del` 
UNIQUE KEY (`fileset_id`, `version`, `storage_location_name`, `deleted_at`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
 -- remove the default value for storage_location_name
-ALTER TABLE `fileset_version_info` ALTER COLUMN `storage_location_name` DROP 
DEFAULT;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'fileset_version_info' 
AND column_name = 'storage_location_name' AND column_default IS NOT NULL),
+    'ALTER TABLE `fileset_version_info` ALTER COLUMN `storage_location_name` 
DROP DEFAULT',
+    'SELECT 1');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
diff --git a/scripts/mysql/upgrade-0.9.0-to-1.0.0-mysql.sql 
b/scripts/mysql/upgrade-0.9.0-to-1.0.0-mysql.sql
index bab851396f..2ee6515e68 100644
--- a/scripts/mysql/upgrade-0.9.0-to-1.0.0-mysql.sql
+++ b/scripts/mysql/upgrade-0.9.0-to-1.0.0-mysql.sql
@@ -59,12 +59,43 @@ CREATE TABLE IF NOT EXISTS `policy_relation_meta` (
     KEY `idx_mid` (`metadata_object_id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 'policy 
metadata object relation';
 
+-- MySQL has no ADD COLUMN IF NOT EXISTS. Guard the unique-key conversion so
+-- a failed upgrade can be restarted from the beginning: skip exactly the
+-- completed steps and fail on any other unexpected schema state.
+
 -- using default 'unknown' to fill in the new column for compatibility
-ALTER TABLE `model_version_info` ADD COLUMN `model_version_uri_name` 
VARCHAR(256) NOT NULL DEFAULT 'unknown' COMMENT 'model version uri name' AFTER 
`model_version_properties`;
-ALTER TABLE `model_version_info` DROP INDEX `uk_mid_ver_del`;
-ALTER TABLE `model_version_info` ADD CONSTRAINT `uk_mid_ver_uri_del` UNIQUE 
KEY (`model_id`, `version`, `model_version_uri_name`, `deleted_at`);
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND 
column_name = 'model_version_uri_name'),
+    'SELECT 1',
+    'ALTER TABLE `model_version_info` ADD COLUMN `model_version_uri_name` 
VARCHAR(256) NOT NULL DEFAULT ''unknown'' COMMENT ''model version uri name'' 
AFTER `model_version_properties`');
+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 = 'uk_mid_ver_del'),
+    'SELECT 1',
+    'ALTER TABLE `model_version_info` DROP INDEX `uk_mid_ver_del`');
+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 = 'model_version_info' AND 
index_name = 'uk_mid_ver_uri_del'),
+    'SELECT 1',
+    'ALTER TABLE `model_version_info` ADD CONSTRAINT `uk_mid_ver_uri_del` 
UNIQUE KEY (`model_id`, `version`, `model_version_uri_name`, `deleted_at`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
+
 -- remove the default value for model_version_uri_name
-ALTER TABLE `model_version_info` ALTER COLUMN `model_version_uri_name` DROP 
DEFAULT;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'model_version_info' AND 
column_name = 'model_version_uri_name' AND column_default IS NOT NULL),
+    'ALTER TABLE `model_version_info` ALTER COLUMN `model_version_uri_name` 
DROP DEFAULT',
+    'SELECT 1');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
 CREATE TABLE IF NOT EXISTS `statistic_meta` (
     `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto increment 
id',
diff --git a/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql 
b/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
index 4c4ef09ce5..09efe2b8f1 100644
--- a/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
+++ b/scripts/mysql/upgrade-1.2.0-to-1.3.0-mysql.sql
@@ -17,28 +17,65 @@
 -- under the License.
 --
 
-ALTER TABLE `user_meta`
-    ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0
-    COMMENT 'updated at';
+-- MySQL has no ADD COLUMN / CREATE INDEX IF NOT EXISTS. Guard completed DDL
+-- so a failed upgrade can be restarted from the beginning. Each operation
+-- runs in this session; no stored routines or extra privileges are required.
 
-ALTER TABLE `role_meta`
-    ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0
-    COMMENT 'updated at';
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'user_meta' AND 
column_name = 'updated_at'),
+    'SELECT 1',
+    'ALTER TABLE `user_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT 
NULL DEFAULT 0 COMMENT ''updated at''');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
-ALTER TABLE `owner_meta`
-    ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0
-    COMMENT 'updated at';
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'role_meta' AND 
column_name = 'updated_at'),
+    'SELECT 1',
+    'ALTER TABLE `role_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT 
NULL DEFAULT 0 COMMENT ''updated at''');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
-ALTER TABLE `group_meta`
-    ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0
-    COMMENT 'updated at';
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'owner_meta' AND 
column_name = 'updated_at'),
+    'SELECT 1',
+    'ALTER TABLE `owner_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT 
NULL DEFAULT 0 COMMENT ''updated at''');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
-CREATE INDEX idx_user_meta_name_del_upd
-    ON user_meta (metalake_id, user_name, deleted_at, updated_at);
-CREATE INDEX idx_owner_meta_del_upd_obj
-    ON owner_meta (deleted_at, updated_at, metadata_object_id);
-CREATE INDEX idx_group_meta_name_del_upd
-    ON group_meta (metalake_id, group_name, deleted_at, updated_at);
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'group_meta' AND 
column_name = 'updated_at'),
+    'SELECT 1',
+    'ALTER TABLE `group_meta` ADD COLUMN `updated_at` BIGINT(20) UNSIGNED NOT 
NULL DEFAULT 0 COMMENT ''updated 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 = 'user_meta' AND 
index_name = 'idx_user_meta_name_del_upd'),
+    'SELECT 1',
+    'CREATE INDEX `idx_user_meta_name_del_upd` ON `user_meta` (`metalake_id`, 
`user_name`, `deleted_at`, `updated_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 = 'owner_meta' AND 
index_name = 'idx_owner_meta_del_upd_obj'),
+    'SELECT 1',
+    'CREATE INDEX `idx_owner_meta_del_upd_obj` ON `owner_meta` (`deleted_at`, 
`updated_at`, `metadata_object_id`)');
+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 = 'group_meta' AND 
index_name = 'idx_group_meta_name_del_upd'),
+    'SELECT 1',
+    'CREATE INDEX `idx_group_meta_name_del_upd` ON `group_meta` 
(`metalake_id`, `group_name`, `deleted_at`, `updated_at`)');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
 CREATE TABLE IF NOT EXISTS `entity_change_log` (
   `id`            BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'auto 
increment id',
@@ -52,13 +89,19 @@ CREATE TABLE IF NOT EXISTS `entity_change_log` (
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT 
'Append-only log of entity structural changes for targeted metadataIdCache 
invalidation';
 
 -- add audit_info as nullable first for MySQL 5.7 compatibility
-ALTER TABLE `view_meta`
-    ADD COLUMN `audit_info` MEDIUMTEXT COMMENT 'view audit info' AFTER 
`schema_id`;
+SET @ddl = IF(EXISTS(SELECT 1 FROM information_schema.columns
+    WHERE table_schema = DATABASE() AND table_name = 'view_meta' AND 
column_name = 'audit_info'),
+    'SELECT 1',
+    'ALTER TABLE `view_meta` ADD COLUMN `audit_info` MEDIUMTEXT COMMENT ''view 
audit info'' AFTER `schema_id`');
+PREPARE stmt FROM @ddl;
+EXECUTE stmt;
+DEALLOCATE PREPARE stmt;
 
--- backfill existing rows before enforcing NOT NULL
+-- backfill existing rows before enforcing NOT NULL. Cover both NULL and the
+-- truncated-by-SQL-mode empty string so a retry can finish a partial backfill.
 UPDATE `view_meta`
     SET `audit_info` = '{}'
-    WHERE `audit_info` IS NULL;
+    WHERE `audit_info` IS NULL OR `audit_info` = '';
 
 ALTER TABLE `view_meta`
     MODIFY COLUMN `audit_info` MEDIUMTEXT NOT NULL COMMENT 'view audit info' 
AFTER `schema_id`;

Reply via email to