Schema Attribute ??? SQL ?????

?? Schema Attribute ?????SQL Generator ???MySQL 5.6.2 DDL ??????????

?? docs

Schema Attribute 使用與 SQL 產生器設計

1. 文件目的

本文件整理第二階段 MySQL TaskStore PoC 中,Schema Attribute 的使用方式,以及後續 SQL Generator 要如何從 C# Class 產出 MySQL 5.6.2 相容 DDL。

目前程式 repo 已完成第一版:

本文件是「下一步 SQL 產生器與自動建表執行器」的設計依據,不代表已經同意實際連線 MySQL、執行 DDL、安裝套件、修改啟動方式或新增 WebApi / ServiceHost。

2. 目前已完成與尚未完成

區塊狀態說明
Schema Attribute已完成第一版可在 C# Class / Property 上描述 table、column、index
Schema Parser已完成第一版可將 Attribute 解析成中立 SchemaDefinition
SQL Generator尚未完成尚未把 SchemaDefinition 轉成 MySQL DDL
Schema Initializer尚未完成尚未連線 DB,也尚未檢查 information_schema
TaskStore尚未完成尚未把任務、節點、命令、錯誤寫入 MySQL

3. 使用邊界

3.1 Schema Attribute 負責什麼

Schema Attribute 只負責描述資料結構意圖:

Schema Attribute 不負責:

3.2 SQL Generator 負責什麼

SQL Generator 只負責把 SchemaDefinition 轉成 SQL 字串:

SQL Generator 不直接執行 SQL,也不持有連線字串。

3.3 Schema Initializer 負責什麼

Schema Initializer 是後續才會建立的執行器,負責:

  1. 讀取已註冊的 schema class。
  2. 呼叫 SchemaDefinitionParser 取得 SchemaDefinition
  3. 讀取 MySQL information_schema
  4. 比對既有 table / column / index。
  5. 呼叫 SQL Generator 產出需要執行的 SQL。
  6. 依設定決定 Dry Run 或實際執行。
  7. 將結果寫入 Log。

4. C# Class 使用方式

4.1 Table 與 Index

[DbTable("task_executions", Description = "任務執行紀錄")]
[DbIndex("idx_task_executions_status", "status")]
[DbIndex("idx_task_executions_started_at", "started_at")]
public class TaskExecutionRecord
{
}

規則:

4.2 Column

[DbColumn("task_id", "varchar(64)", IsPrimaryKey = true, IsRequired = true, Description = "任務識別碼")]
public string TaskId { get; set; }

[DbColumn("status", "varchar(32)", IsRequired = true, Description = "任務狀態")]
public string Status { get; set; }

[DbColumn("raw_context", "longtext", Description = "任務原始上下文 JSON 字串")]
public string RawContext { get; set; }

規則:

5. Schema 轉換流程

flowchart LR
    A["C# Record Class"] --> B["DbTable / DbColumn / DbIndex"]
    B --> C["SchemaDefinitionParser"]
    C --> D["SchemaDefinition"]
    D --> E["MySqlSchemaSqlGenerator"]
    E --> F["DDL SQL 字串"]
    F --> G["Schema Initializer 後續執行"]

第一版已完成 AD

本文件規劃下一步 DF

6. SQL Generator 建議介面

建議先建立純字串產生器,不連線 DB。

public interface ISchemaSqlGenerator
{
    IReadOnlyList<string> GenerateCreateTableSql(SchemaDefinition schema);
    IReadOnlyList<string> GenerateAddColumnSql(SchemaDefinition schema, SchemaDiff diff);
    IReadOnlyList<string> GenerateAddIndexSql(SchemaDefinition schema, SchemaDiff diff);
}

若第一版尚未建立 SchemaDiff,可以先只做:

public interface ICreateTableSqlGenerator
{
    string GenerateCreateTableSql(TableDefinition table);
}

建議第一批先實作 GenerateCreateTableSql,等可產出完整建表 SQL 後,再補 ALTER TABLE

7. MySQL 5.6.2 DDL 規則

7.1 CREATE TABLE

產出範例:

CREATE TABLE IF NOT EXISTS `task_executions` (
  `task_id` varchar(64) NOT NULL COMMENT '任務識別碼',
  `workflow_id` varchar(64) NOT NULL COMMENT 'Workflow 識別碼',
  `status` varchar(32) NOT NULL COMMENT '任務狀態',
  `started_at` datetime NULL COMMENT '開始時間',
  `ended_at` datetime NULL COMMENT '結束時間',
  `raw_context` longtext NULL COMMENT '任務原始上下文 JSON 字串',
  PRIMARY KEY (`task_id`),
  INDEX `idx_task_executions_status` (`status`),
  INDEX `idx_task_executions_started_at` (`started_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='任務執行紀錄';

7.2 ADD COLUMN

產出範例:

ALTER TABLE `task_executions`
  ADD COLUMN `raw_context` longtext NULL COMMENT '任務原始上下文 JSON 字串';

7.3 ADD INDEX

產出範例:

ALTER TABLE `task_executions`
  ADD INDEX `idx_task_executions_status` (`status`);

唯一索引產出範例:

ALTER TABLE `task_executions`
  ADD UNIQUE INDEX `uk_task_executions_task_id` (`task_id`);

8. Identifier 與字串安全規則

SQL Generator 必須集中處理命名與字串 escaping。

類型規則
Table / Column / Index 名稱只允許英數字與底線,建議 regex:^[A-Za-z][A-Za-z0-9_]*$
Identifier 包裝使用反引號,例如 ` task_executions `
COMMENT 字串單引號需轉成兩個單引號,例如 patient'spatient''s
Type 字串第一版只允許白名單型別或安全格式,不接受任意 SQL 片段

不建議讓 Type 可放入完整 SQL,例如:

varchar(64) NOT NULL DEFAULT 'x'

原因是第一版的 NOT NULL、主鍵與描述已由 Attribute 欄位控制,若讓 Type 混入約束,後續 Generator 很難穩定判斷。

9. MySQL 型別白名單建議

第一版建議允許:

類型範例
字串varchar(32)varchar(64)varchar(128)varchar(255)
長文字textlongtext
整數intbigint
小數decimal(18,4)
時間datetime
布林tinyint(1)

第一版不建議允許:

10. 差異比對設計

後續 Schema Initializer 需要建立 SchemaDiff,但第一批 SQL Generator 可以先不做。

建議差異分類:

差異第一版處理方式
Table 不存在產出 CREATE TABLE IF NOT EXISTS
Column 不存在產出 ALTER TABLE ADD COLUMN
Index 不存在產出 ALTER TABLE ADD INDEX
Column 型別不同回報 mismatch,不產出修改 SQL
Column 必填不同回報 mismatch,不產出修改 SQL
Column 描述不同第一版只警告
DB 多出欄位忽略或警告,不刪除
DB 多出索引忽略或警告,不刪除

11. Log 與錯誤回報

SQL Generator 與 Initializer 的錯誤需可追蹤:

情境建議錯誤
Table name 無效SchemaDefinitionInvalid
Column name 無效SchemaDefinitionInvalid
Column type 不在白名單SchemaDefinitionInvalid
Primary key 缺失SchemaDefinitionInvalid
Index 指向不存在欄位SchemaDefinitionInvalid
DB 權限不足後續 DB 錯誤碼
schema mismatch後續 DB / Schema mismatch 錯誤碼

Log 至少需包含:

12. 測試規劃

12.1 SQL Generator 單元測試

測試項目預期結果
單一 table + primary key產出 CREATE TABLE IF NOT EXISTS
多欄位 + 多索引產出完整欄位與 index
unique index產出 UNIQUE INDEX
description 含單引號COMMENT 正確 escape
無效 table name回傳錯誤
無效 column type回傳錯誤
index 指向不存在欄位回傳錯誤

12.2 Initializer 驗證節點

驗證項目預期結果
Dry Run只輸出 SQL,不修改 DB
空 database建立 TaskStore table
重複啟動不重複建立、不報錯
缺少欄位補欄位
缺少索引補索引
欄位型別不一致停止或警告,不自動改型別

13. 建議實作順序

  1. 建立 MySqlSchemaSqlGeneratorOptions
  2. 建立 identifier / comment / type 驗證 helper。
  3. 實作 GenerateCreateTableSql
  4. 補 SQL Generator 單元測試。
  5. 實作 GenerateAddColumnSql
  6. 實作 GenerateAddIndexSql
  7. 設計 SchemaDiff
  8. 設計 SchemaInitializer Dry Run。
  9. 等使用者確認 MySQL 連線資訊後,再做實際連線驗證。

14. 與現有文件關係

文件關係
MySQL Schema 自動建表設計上層設計,定義整體自動建表策略
MySQL schema Class 初稿定義 TaskStore 四張表的欄位與索引
MySQL Schema Attribute 實作前確認實作 Attribute 前的範圍確認
本文件Attribute 已完成後,銜接 SQL Generator / Initializer 的設計

15. 建議下一步

建議下一步先做:

SQL Generator 實作前確認

確認項目:

確認後再進入程式 repo 實作 MySqlSchemaSqlGenerator 第一版。