column line
column 用穩定 ref 管理 table schema。它的 spec 鏡像 ColumnCreate,唯一重要轉換是所有資源關係都使用 authored refs;伺服器在 plan/apply 時才解析為 table IDs 與 col_<hex> internal keys。
JSONL Line
{"kind":"column","table":"orders","ref":"customer","spec":{"name":"Customer","type":"link","target":"customers","cardinality":"one"}}欄位契約
頂層對應 IacColumnLine:必填 kind: "column"、table、ref;state 可為 present/absent 且預設 present;renamed_from 可選;present 必須有 object spec,純 rename 可只提供 renamed_from,absent 不得帶 spec。IaC Pydantic model 把 spec 定義為 Dict[str, Any];下列精確欄位矩陣是後續 ColumnCreate 的契約。工作台先檢查可見的 ref graph,伺服器在持久化欄位前仍會套用完整 CRUD model。
spec 鏡像 ColumnCreate 的 authored 欄位:
| 欄位 | 必填 | 精確契約 |
|---|---|---|
name | 是 | column display name |
type | 是 | string、integer、float、boolean、date、datetime、text、select、multi_select、interval、link、rollup、lookup、formula、attachment、json、principal、user 或 social_client |
required | 否 | boolean,預設 false;computed types 禁止 true |
default_value | 否 | string、integer、float、boolean 或 null;computed types 禁止非 null |
max_length | 否 | integer 或 null,只適用 string/text;computed types 禁止非 null |
description | 否 | string 或 null |
options | 否 | string 陣列或 null,只適用 select/multi_select |
target | 條件式 | link 的目標 table ref;取代 REST target_table_id |
cardinality | 否 | link 的 "one" 或 "many";省略時 many |
link_field | 條件式 | outgoing rollup/lookup 所依賴的本 table link-column ref |
aggregation | 條件式 | rollup 的 count、count_distinct、sum、avg、min 或 max |
target_column | 條件式 | rollup/lookup 的目標 column ref;count rollup 禁止提供 |
direction | 否 | rollup 的 outgoing 或 incoming;預設 outgoing |
source | 條件式 | incoming rollup 的來源 table ref;取代 REST source_table_id |
match | 條件式 | incoming rollup 的 {source-column-ref: own-column-ref-or-$self},1–3 組 |
filter | 否 | 最多 10 個 predicate;其中 column 是聚合側 column ref |
expression | 條件式 | formula expression;欄位 token 必須是可解析 refs |
pick | 否 | Picked-lookup 的 argmax 選擇器 {order_column, direction};僅限 LOOKUP |
型別間條件矩陣仍由 ColumnCreate 驗證:link 需要 target;outgoing rollup 需要 link_field/aggregation;incoming rollup 需要 source/match/aggregation;lookup 需要 link_field/target_column;formula 需要 expression。不屬於該 type 的 computed config 會被拒絕;attachment 禁止 default_value、max_length、options 與 computed config。
其中四個型別的行為差異大到值得特別說明。json 被排除在 rollup、lookup、formula、row-policy 與 SCP predicate,以及 unique 與 match key 之外——它與 interval 都永遠不能當 record 自然鍵。principal、user 與 social_client 是 principal 欄位:cell 是單一字串,各處一律只支援等值比較,永遠不可排序。user/social_client 的 cell 是原始 ID,在 record 行中接受可攜身分 token;principal 的 cell 是帶標籤的 user:<id>/smc:<id>/room:<id> 字串,接受 $user:、$smc: 與 $room: token,且解析結果正是該帶標籤形式。三者都可以當自然鍵。
pick(Picked Lookup)
pick 會以連結目標表上某個可排序的儲存欄位對被連結的列排名並取第一筆,讓 lookup 具備 argmax 選擇能力:
| 子鍵 | 契約 |
|---|---|
order_column | 透過 link_field 在連結目標表上解析的 column ref,與 target_column 相同;型別必須是 integer、float、date 或 datetime |
direction | "asc" 或 "desc" |
pick 內禁止額外子鍵。缺少 link_field 時無法決定它所屬的資料表:
pick.order_column requires link_field (picked lookup) to resolve its owning tableRef 與身分規則
table、ref、renamed_from、target、source 及欄位 refs 都使用 authored pattern ^[a-z0-9][a-z0-9_-]{0,63}$。table: "orders" + ref: "customer" 的 qualified identity 是 orders.customer。
Column、rule、trigger、view 與 public_read refs,加上固定的 client_access identity,在同一 table 共用 qualified-ref namespace。因此 column orders.status 會和 view orders.status 或 public-read token ref orders.status 碰撞;authored 成 orders.client_access 的 child 也會和單例 client-access resource 碰撞。文件宣告優先於既有 state;所有跨 ref 失敗時 fail closed,不能用 display name 或 col_<hex> 猜測。
target_column 的解析 table 取決於 link/rollup/lookup 方向;match 兩側、filter predicate 與 formula tokens 也依其語意解析。Export 會將 live internal keys反向翻成 refs。
生命週期與規劃
Present column 可規劃 create、adopt、move、update 或 noop;state: "absent" 規劃 delete。Spec-less present 只有 renamed_from 純 rename 合法。Plan 先審查 ref/diff 與可見的 live dependency;完整 ColumnCreate/ColumnUpdate model 仍可能在 apply 持久化前回傳 per-line error。
Computed config 一旦上線就不可變
在已存在的 link、rollup、lookup 或 formula 欄位上,只有 name 與 description 可以更新。任何其他變更——target、source、link_field、target_column、match、expression、filter、pick——現在都會在 plan 階段讓該行失敗:
only name/description can be updated on lookup columns (got: pick, target_column); to change computed config delete and recreate the column (state: absent + new line)這是新行為,而且它是修正正確性,而不是新增限制。過去這類變更會規劃成 update,被 executor 的更新欄位過濾器靜默丟棄,可是 state 卻記下了新的 spec、該行也回報 applied: true——因此像是翻轉 picked lookup 的 pick.direction,看起來套用了,實際上沒有。
唯一忠實的做法是 state: "absent" 加上一行新的。請對審查者明說代價:這會以新的內部 key 重建欄位,並失去該欄位的資料。
機制上的原因是:IaC 的 update 或 move 只會把 name、type、required、default_value、max_length、description 與 options 送給 CRUD。欄位 spec 中其他所有東西都只在建立時有效——這也代表 select 欄位的 options 確實可以更新。
這是 diff 階段錯誤,因此 apply 回傳 200 且只略過那一行,而不是拒絕整份文件。
IaC state 只擁有 declared spec fields。Display name 改變不改 ref;若要改 ref,使用 renamed_from,不要建立另一欄再刪除原欄。
驗證錯誤
Child lifecycle 與 parent/qualified ref 的精確 detail:
state=absent lines carry no spec (table lines are the sole adopt-then-delete exception)
state=present lines require a spec (unless a pure rename via renamed_from)
unknown table ref 'orders'
duplicate ref 'orders.customer' (already declared in this document)Computed ref 會依欄位位置加入 context:
unknown table ref 'customers' (target)
unknown table ref 'events' (source)
unknown column ref 'orders.customer' (link_field)
unknown column ref 'customers.email' (target_column)
unknown column ref 'events.order_no' (match)
unknown column ref 'orders.order_no' (filter)
unknown column ref 'orders.total' (expression)Computed config 不可變與 picked lookup 規則屬於伺服器 plan 錯誤:
only name/description can be updated on <link|rollup|lookup|formula> columns (got: <fields>); to change computed config delete and recreate the column (state: absent + new line)
pick.order_column requires link_field (picked lookup) to resolve its owning table前兩個 lifecycle validator 類別目前由工作台本機穩定攔截;server plan 還會執行完整 ColumnCreate 型別矩陣與 live dependency 驗證。精確 detail 來源:components/iac/parse.ts。
動手試試
在 IaC 工作台建立 customers、orders 與上面的 link column,再把 target 改成未知 ref。修正後執行 server plan,確認 target 最終解析到預期 table,而不是同名 display name。