1.0.555.sql 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  1. -- ============================================================================
  2. -- 1.0.555 · R2-A2:mdp_entity 两列补 DEFAULT(短期恢复,不动类型)
  3. --
  4. -- 为什么需要本脚本(1.0.554 的效果被撤销,已实证):
  5. -- 1.0.554 把 inbound_enabled / stale_guard_enabled 收口为 TINYINT + DEFAULT,
  6. -- migration 与 verify 均 Success。但下一次启动时 SqlSugar CodeFirst
  7. -- (EnableInitTable=true)按实体声明 `public int` 把它们改回 INT,
  8. -- 其 ALTER 形态为 `alter table t change column c c int NOT NULL`——
  9. -- 不带 DEFAULT 子句,于是连带抹掉 DEFAULT。
  10. -- 对照组:inbound_max_rows 类型本就等于实体声明的 INT,CodeFirst 无差异
  11. -- 可纠正、不生成 ALTER,其 DEFAULT 500 完整穿越了一次 CodeFirst 遍历。
  12. --
  13. -- 结论:只要列类型与实体声明一致,CodeFirst 就不会重写该列,DEFAULT 得以存活。
  14. -- 故本批策略 = 类型保持 INT(与实体一致),只补 DEFAULT。
  15. -- 类型漂移(INT vs 契约 TINYINT)留 R2-B,待 CodeFirst schema authority 定案。
  16. --
  17. -- 1.0.554 保持历史不变:不修改、不改 hash、不改 migration log、不重放。
  18. --
  19. -- 算法:本地 3307 实测 ALTER COLUMN ... SET DEFAULT 接受 INSTANT / INPLACE,LOCK=NONE
  20. -- / COPY 三者。取 INSTANT(纯元数据变更,不重建表,无写阻塞),实测 58ms。
  21. --
  22. -- 范围:只动 mdp_entity 的 inbound_enabled / stale_guard_enabled 两列的 DEFAULT。
  23. -- inbound_max_rows 只做前置断言,绝不 ALTER。
  24. -- 不碰 MdpEntity.cs / CodeFirst / EnableInitTable / R2-B / P0 / 1.0.554。
  25. -- ============================================================================
  26. -- ── G1 表存在 ───────────────────────────────────────────────────────────────
  27. SET @t := (SELECT COUNT(*) FROM information_schema.`TABLES`
  28. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity');
  29. SELECT IF(@t <> 1, 'R2A2-G1 FAIL: mdp_entity 不存在', 'R2A2-G1 ok') AS gate_g1;
  30. SET @sql := IF(@t <> 1,
  31. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-G1: mdp_entity 不存在,中止'", 'DO 0');
  32. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  33. -- ── G2 inbound_enabled 无 NULL ─────────────────────────────────────────────
  34. SET @g2 := (SELECT COUNT(*) FROM `mdp_entity` WHERE `inbound_enabled` IS NULL);
  35. SELECT @g2 AS g2_null_rows;
  36. SELECT IF(@g2 <> 0, 'R2A2-G2 FAIL: inbound_enabled 存在 NULL', 'R2A2-G2 ok') AS gate_g2;
  37. SET @sql := IF(@g2 <> 0,
  38. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-G2: inbound_enabled 存在 NULL,中止'", 'DO 0');
  39. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  40. -- ── G3 stale_guard_enabled 无 NULL ─────────────────────────────────────────
  41. SET @g3 := (SELECT COUNT(*) FROM `mdp_entity` WHERE `stale_guard_enabled` IS NULL);
  42. SELECT @g3 AS g3_null_rows;
  43. SELECT IF(@g3 <> 0, 'R2A2-G3 FAIL: stale_guard_enabled 存在 NULL', 'R2A2-G3 ok') AS gate_g3;
  44. SET @sql := IF(@g3 <> 0,
  45. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-G3: stale_guard_enabled 存在 NULL,中止'", 'DO 0');
  46. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  47. -- ── G4 行数基线(只记录,不硬编码)────────────────────────────────────────
  48. SET @rows_before := (SELECT COUNT(*) FROM `mdp_entity`);
  49. SELECT @rows_before AS rows_before;
  50. -- ── G5 inbound_max_rows 必须已是 INT NOT NULL DEFAULT 500 ──────────────────
  51. -- 它由 1.0.554 修好并已穿越一次 CodeFirst 遍历;若 500 丢失说明前提变了,
  52. -- 属 SCOPE CHANGED,本脚本不得顺手补。
  53. SET @g5 := (SELECT COUNT(*) FROM information_schema.`COLUMNS`
  54. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity'
  55. AND `COLUMN_NAME` = 'inbound_max_rows'
  56. AND `COLUMN_TYPE` = 'int' AND `IS_NULLABLE` = 'NO'
  57. AND `COLUMN_DEFAULT` <=> '500');
  58. SELECT @g5 AS g5_inbound_max_rows_ok;
  59. SELECT IF(@g5 <> 1,
  60. 'R2A2-G5 FAIL: inbound_max_rows 不是 INT NOT NULL DEFAULT 500 — SCOPE CHANGED',
  61. 'R2A2-G5 ok') AS gate_g5;
  62. SET @sql := IF(@g5 <> 1,
  63. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-G5: inbound_max_rows 不符前提(应为 INT NOT NULL DEFAULT 500),SCOPE CHANGED,中止'",
  64. 'DO 0');
  65. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  66. -- ── G6 类型守卫:两列必须已是 INT NOT NULL;本脚本不改类型 ──────────────────
  67. SET @g6 := (SELECT COUNT(*) FROM information_schema.`COLUMNS`
  68. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity'
  69. AND `COLUMN_NAME` IN ('inbound_enabled', 'stale_guard_enabled')
  70. AND `COLUMN_TYPE` = 'int' AND `IS_NULLABLE` = 'NO');
  71. SELECT @g6 AS g6_int_notnull_cols;
  72. SELECT IF(@g6 <> 2,
  73. 'R2A2-G6 FAIL: 两列类型不是 INT NOT NULL — 本脚本不自动改类型',
  74. 'R2A2-G6 ok') AS gate_g6;
  75. SET @sql := IF(@g6 <> 2,
  76. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-G6: inbound_enabled / stale_guard_enabled 类型不是 INT NOT NULL,本脚本不自动改类型,中止'",
  77. 'DO 0');
  78. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  79. -- ── Step 1 · 逐列目标态比对(NULL-safe <=>),仅对仍漂移的列生成子句 ────────
  80. SET @need_ie := (SELECT COUNT(*) FROM information_schema.`COLUMNS`
  81. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity'
  82. AND `COLUMN_NAME` = 'inbound_enabled'
  83. AND NOT (`COLUMN_DEFAULT` <=> '0'));
  84. SET @need_sg := (SELECT COUNT(*) FROM information_schema.`COLUMNS`
  85. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity'
  86. AND `COLUMN_NAME` = 'stale_guard_enabled'
  87. AND NOT (`COLUMN_DEFAULT` <=> '1'));
  88. SELECT @need_ie AS need_inbound_enabled, @need_sg AS need_stale_guard_enabled;
  89. -- CONCAT_WS 会跳过 NULL,于是只有需要改的列才进入 ALTER 子句
  90. SET @c1 := IF(@need_ie > 0, 'ALTER COLUMN `inbound_enabled` SET DEFAULT 0', NULL);
  91. SET @c2 := IF(@need_sg > 0, 'ALTER COLUMN `stale_guard_enabled` SET DEFAULT 1', NULL);
  92. SET @clauses := CONCAT_WS(', ', @c1, @c2);
  93. -- ── Step 2 · 仅在有漂移列时执行;ALGORITHM=INSTANT(纯元数据,无写阻塞)────
  94. SET @sql := IF(@clauses IS NULL OR @clauses = '',
  95. 'DO 0',
  96. CONCAT('ALTER TABLE `mdp_entity` ', @clauses, ', ALGORITHM=INSTANT'));
  97. SELECT @sql AS planned_statement;
  98. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  99. -- ── Step 3 · 事后自断言:两列 DEFAULT 必须到位 ─────────────────────────────
  100. SET @after := (SELECT COUNT(*) FROM information_schema.`COLUMNS`
  101. WHERE `TABLE_SCHEMA` = DATABASE() AND `TABLE_NAME` = 'mdp_entity'
  102. AND ((`COLUMN_NAME` = 'inbound_enabled' AND `COLUMN_DEFAULT` <=> '0')
  103. OR (`COLUMN_NAME` = 'stale_guard_enabled' AND `COLUMN_DEFAULT` <=> '1')));
  104. SELECT @after AS defaults_in_place;
  105. SELECT IF(@after <> 2, 'R2A2-POST1 FAIL: 两列 DEFAULT 未全部到位', 'R2A2-POST1 ok') AS gate_post1;
  106. SET @sql := IF(@after <> 2,
  107. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-POST: 两列 DEFAULT 未全部到位,中止'", 'DO 0');
  108. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;
  109. -- ── Step 4 · 行数完整性 ────────────────────────────────────────────────────
  110. SET @rows_after := (SELECT COUNT(*) FROM `mdp_entity`);
  111. SELECT @rows_before AS rows_before, @rows_after AS rows_after;
  112. SELECT IF(@rows_after <> @rows_before, 'R2A2-POST2 FAIL: 行数变化', 'R2A2-POST2 ok') AS gate_post2;
  113. SET @sql := IF(@rows_after <> @rows_before,
  114. "SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'R2A2-POST: mdp_entity 行数在 ALTER 前后不一致,中止'", 'DO 0');
  115. PREPARE s FROM @sql; EXECUTE s; DEALLOCATE PREPARE s;