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`;