# sql5 v1.16 版本說明(已被 v1.17 取代)
## 版本資訊
- **版本**:1.16
- **日期**:2026-05-04
- **名稱**:TRIGGERs 支援(部分)
## SQLite Trigger 語法
```sql
-- 建立 trigger
CREATE TRIGGER [IF NOT EXISTS] trigger_name
[BEFORE|AFTER|INSTEAD OF] [DELETE|INSERT|UPDATE [OF column_name, ...]]
ON table_name
[FOR EACH ROW]
[WHEN condition]
BEGIN
trigger_body; -- 單一 SQL 語句(不支援多語句 Transaction)
END;
-- 刪除 trigger
DROP TRIGGER [IF EXISTS] trigger_name;
```
### 時機(Timing)
- `BEFORE` — 操作發生前觸發
- `AFTER` — 操作完成後觸發
- `INSTEAD OF` — 取代原本操作(通常用於視圖)
### 事件(Event)
- `INSERT`
- `DELETE`
- `UPDATE [OF col1, col2]` — 可限定特定欄位
### FOR EACH ROW
- 所有 trigger 都是 ROW-level(SQLite 不支援 STATEMENT-level)
### WHEN 子句
- 每行滿足條件才執行 trigger body
## 實作規劃
### 1. AST / Parser
- `Statement::CreateTrigger(CreateTriggerStmt)`
- `Statement::DropTrigger(DropTriggerStmt)`
- 解析 `CREATE TRIGGER` 語法到 AST(參考 `CreateViewStmt` 結構)
```rust
// 新增 AST 節點
#[derive(Debug, Clone)]
pub struct CreateTriggerStmt {
pub name: String,
pub table: String,
pub timing: TriggerTiming, // Before, After, InsteadOf
pub event: TriggerEvent, // Delete, Insert, Update(Option<Vec<String>>)
pub for_each_row: bool,
pub when: Option<Expr>,
pub body: String, // trigger body(內部 SQL 語句字串)
}
#[derive(Debug, Clone, PartialEq)]
pub enum TriggerTiming { Before, After, InsteadOf }
#[derive(Debug, Clone, PartialEq)]
pub enum TriggerEvent { Delete, Insert, Update(Vec<String>) }
#[derive(Debug, Clone)]
pub struct DropTriggerStmt {
pub name: String,
}
```
### 2. Catalog
- 在 `TableMeta` 或單獨 `TriggerMeta` 中儲存 trigger 定義
- Catalog 提供 `create_trigger()`, `drop_trigger()`, `get_triggers(table)` 等 API
- Trigger 需要持久化(重啟後仍有效)
### 3. Planner
- `plan_create_trigger()`: 驗證 table 存在,寫入 catalog
- `plan_drop_trigger()`: 從 catalog 刪除
- **觸發時機**:
- INSERT/UPDATE/DELETE 執行時,根據該表的 triggers 列表
- BEFORE trigger 在 mutation 前執行,AFTER 在之後
- INSTEAD OF trigger 用於視圖(需要單獨處理)
### 4. Executor
修改 `exec_insert` / `exec_update` / `exec_delete`:
```rust
// 概念:每行資料變化前/後遍歷並執行該表的 triggers
fn fire_triggers(&mut self, table: &str, event: TriggerEvent, row: &Row, old_row: Option<&Row>) {
let triggers = self.catalog.get_triggers(table, &event);
for trigger in triggers {
if let Some(when) = &trigger.when {
if !eval_expr(when, row, &self.col_names).map(is_truthy).unwrap_or(false) {
continue;
}
}
// parse and execute trigger.body
}
}
```
### 5. 限制(Phase 1)
- Trigger body 只支援單一 SQL 語句(不含 BEGIN...END 封裝)
- 暫不支援 `INSTEAD OF` 視圖 trigger
- 暫不支援巢狀 trigger(trigger 內再 trigger)
- Trigger 變數 `NEW.col` / `OLD.col` 需要特殊處理
## 架構變化
```
src/
parser/
ast.rs — +CreateTriggerStmt, DropTriggerStmt
parser.rs — +parse_create_trigger(), parse_drop_trigger()
catalog/
catalog.rs — +trigger CRUD,持久化
planner/
planner.rs — +plan_create_trigger, plan_drop_trigger
executor.rs — exec_insert/update/delete + fire_triggers()
```
## 測試計畫
- [ ] `CREATE TRIGGER` 基本解析
- [ ] `DROP TRIGGER`
- [ ] BEFORE INSERT trigger 驗證
- [ ] AFTER INSERT/UPDATE/DELETE trigger 驗證
- [ ] WHEN 子句
- [ ] 單一欄位 UPDATE trigger(`UPDATE OF col`)
- [ ] Trigger 持久化(重啟後仍在)
## 對應 SQLite 相容性
| TRIGGERs | 🔧 實作中 (v1.16) |
| ATTACH | ❌ 待支援 |
| VACUUM | ❌ 待支援 |