litedbmodel 2.2.2 → 2.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/README.md +2 -1
  2. package/dist/DBModel.d.ts.map +1 -1
  3. package/dist/DBModel.js +3 -0
  4. package/dist/DBModel.js.map +1 -1
  5. package/dist/Middleware.d.ts +19 -2
  6. package/dist/Middleware.d.ts.map +1 -1
  7. package/dist/Middleware.js +1 -0
  8. package/dist/Middleware.js.map +1 -1
  9. package/dist/decorators.d.ts.map +1 -1
  10. package/dist/decorators.js +8 -0
  11. package/dist/decorators.js.map +1 -1
  12. package/dist/drivers/MysqlHelper.d.ts +1 -1
  13. package/dist/drivers/MysqlHelper.d.ts.map +1 -1
  14. package/dist/drivers/MysqlHelper.js.map +1 -1
  15. package/dist/drivers/PostgresHelper.d.ts +1 -1
  16. package/dist/drivers/PostgresHelper.d.ts.map +1 -1
  17. package/dist/drivers/PostgresHelper.js.map +1 -1
  18. package/dist/drivers/SqliteHelper.d.ts +1 -1
  19. package/dist/drivers/SqliteHelper.d.ts.map +1 -1
  20. package/dist/drivers/SqliteHelper.js.map +1 -1
  21. package/dist/index.cjs +105 -74
  22. package/dist/scp/connection-routing.d.ts +38 -1
  23. package/dist/scp/connection-routing.d.ts.map +1 -1
  24. package/dist/scp/connection-routing.js +64 -5
  25. package/dist/scp/connection-routing.js.map +1 -1
  26. package/dist/scp/dbmodel-runtime.d.ts +2 -1
  27. package/dist/scp/dbmodel-runtime.d.ts.map +1 -1
  28. package/dist/scp/dbmodel-runtime.js +2 -1
  29. package/dist/scp/dbmodel-runtime.js.map +1 -1
  30. package/dist/scp/decorator-adapter.d.ts +22 -0
  31. package/dist/scp/decorator-adapter.d.ts.map +1 -1
  32. package/dist/scp/decorator-adapter.js +26 -0
  33. package/dist/scp/decorator-adapter.js.map +1 -1
  34. package/dist/scp/emit/emitter.d.ts +21 -10
  35. package/dist/scp/emit/emitter.d.ts.map +1 -1
  36. package/dist/scp/emit/emitter.js +119 -51
  37. package/dist/scp/emit/emitter.js.map +1 -1
  38. package/dist/scp/emit/endpoint.d.ts +3 -8
  39. package/dist/scp/emit/endpoint.d.ts.map +1 -1
  40. package/dist/scp/exec-context.d.ts +39 -17
  41. package/dist/scp/exec-context.d.ts.map +1 -1
  42. package/dist/scp/exec-context.js +69 -35
  43. package/dist/scp/exec-context.js.map +1 -1
  44. package/dist/scp/grouping.d.ts.map +1 -1
  45. package/dist/scp/grouping.js +0 -0
  46. package/dist/scp/grouping.js.map +1 -1
  47. package/dist/scp/index.cjs +257 -670
  48. package/dist/scp/index.d.ts +6 -11
  49. package/dist/scp/index.d.ts.map +1 -1
  50. package/dist/scp/index.js +10 -28
  51. package/dist/scp/index.js.map +1 -1
  52. package/dist/scp/index.mjs +257 -658
  53. package/dist/scp/leaf-transport.d.ts +127 -39
  54. package/dist/scp/leaf-transport.d.ts.map +1 -1
  55. package/dist/scp/leaf-transport.js +30 -24
  56. package/dist/scp/leaf-transport.js.map +1 -1
  57. package/dist/scp/leaves.d.ts +57 -20
  58. package/dist/scp/leaves.d.ts.map +1 -1
  59. package/dist/scp/leaves.js +224 -65
  60. package/dist/scp/leaves.js.map +1 -1
  61. package/dist/scp/makesql/compile-select.d.ts +28 -1
  62. package/dist/scp/makesql/compile-select.d.ts.map +1 -1
  63. package/dist/scp/makesql/compile-select.js +19 -17
  64. package/dist/scp/makesql/compile-select.js.map +1 -1
  65. package/dist/scp/makesql/index.d.ts +2 -2
  66. package/dist/scp/makesql/index.d.ts.map +1 -1
  67. package/dist/scp/makesql/index.js +4 -6
  68. package/dist/scp/makesql/index.js.map +1 -1
  69. package/dist/scp/makesql/tx.d.ts +43 -89
  70. package/dist/scp/makesql/tx.d.ts.map +1 -1
  71. package/dist/scp/makesql/tx.js +45 -496
  72. package/dist/scp/makesql/tx.js.map +1 -1
  73. package/dist/scp/makesql/writeouttype.d.ts +5 -7
  74. package/dist/scp/makesql/writeouttype.d.ts.map +1 -1
  75. package/dist/scp/makesql/writeouttype.js +13 -19
  76. package/dist/scp/makesql/writeouttype.js.map +1 -1
  77. package/dist/scp/middleware.d.ts +4 -4
  78. package/dist/scp/middleware.d.ts.map +1 -1
  79. package/dist/scp/middleware.js +2 -2
  80. package/dist/scp/middleware.js.map +1 -1
  81. package/dist/scp/relation.d.ts +31 -12
  82. package/dist/scp/relation.d.ts.map +1 -1
  83. package/dist/scp/relation.js +16 -6
  84. package/dist/scp/relation.js.map +1 -1
  85. package/dist/scp/typed-object.d.ts +23 -19
  86. package/dist/scp/typed-object.d.ts.map +1 -1
  87. package/dist/scp/typed-object.js +24 -30
  88. package/dist/scp/typed-object.js.map +1 -1
  89. package/dist/scp/write-bundle.d.ts +21 -70
  90. package/dist/scp/write-bundle.d.ts.map +1 -1
  91. package/dist/scp/write-bundle.js +16 -80
  92. package/dist/scp/write-bundle.js.map +1 -1
  93. package/dist/types.d.ts +12 -0
  94. package/dist/types.d.ts.map +1 -1
  95. package/dist/types.js.map +1 -1
  96. package/docs/BENCHMARK.md +26 -22
  97. package/docs/COMPARISON.md +8 -4
  98. package/docs/api/README.md +1 -1
  99. package/docs/api/classes/Conditions.md +10 -10
  100. package/docs/api/classes/DBHandler.md +17 -17
  101. package/docs/api/classes/DBModel.md +111 -104
  102. package/docs/api/classes/LimitExceededError.md +8 -8
  103. package/docs/api/classes/Middleware.md +18 -18
  104. package/docs/api/classes/SqlRaw.md +6 -6
  105. package/docs/api/classes/SqlRef.md +7 -7
  106. package/docs/api/classes/Values.md +6 -6
  107. package/docs/api/classes/WriteInReadOnlyContextError.md +5 -5
  108. package/docs/api/classes/WriteOutsideTransactionError.md +5 -5
  109. package/docs/api/functions/belongsTo.md +2 -2
  110. package/docs/api/functions/closeAllPools.md +2 -2
  111. package/docs/api/functions/condsToRecord.md +2 -2
  112. package/docs/api/functions/createMiddleware.md +2 -2
  113. package/docs/api/functions/createPostgresDriver.md +2 -2
  114. package/docs/api/functions/createSqliteDriver.md +2 -2
  115. package/docs/api/functions/dbCast.md +3 -3
  116. package/docs/api/functions/dbCastIn.md +2 -2
  117. package/docs/api/functions/dbDynamic.md +3 -3
  118. package/docs/api/functions/dbFalse.md +2 -2
  119. package/docs/api/functions/dbImmediate.md +2 -2
  120. package/docs/api/functions/dbIn.md +2 -2
  121. package/docs/api/functions/dbNotNull.md +2 -2
  122. package/docs/api/functions/dbNow.md +2 -2
  123. package/docs/api/functions/dbNull.md +2 -2
  124. package/docs/api/functions/dbRaw.md +2 -2
  125. package/docs/api/functions/dbTrue.md +2 -2
  126. package/docs/api/functions/dbUuid.md +3 -3
  127. package/docs/api/functions/dbUuidIn.md +2 -2
  128. package/docs/api/functions/formatLocalDate.md +25 -0
  129. package/docs/api/functions/formatUTCDate.md +25 -0
  130. package/docs/api/functions/getDBConfig.md +2 -2
  131. package/docs/api/functions/getDBHandler.md +2 -2
  132. package/docs/api/functions/getTransactionConnection.md +2 -2
  133. package/docs/api/functions/getTransactionContext.md +2 -2
  134. package/docs/api/functions/hasMany.md +2 -2
  135. package/docs/api/functions/hasOne.md +2 -2
  136. package/docs/api/functions/initDBHandler.md +2 -2
  137. package/docs/api/functions/isAnySqlFragment.md +2 -2
  138. package/docs/api/functions/isConnectionError.md +26 -0
  139. package/docs/api/functions/isSqlCondition.md +2 -2
  140. package/docs/api/functions/isSqlFragment.md +2 -2
  141. package/docs/api/functions/isSqlRaw.md +2 -2
  142. package/docs/api/functions/isSqlRef.md +2 -2
  143. package/docs/api/functions/isSqlTypedFragment.md +2 -2
  144. package/docs/api/functions/model.md +6 -42
  145. package/docs/api/functions/parentRef.md +2 -2
  146. package/docs/api/functions/sql.md +7 -7
  147. package/docs/api/globals.md +57 -22
  148. package/docs/api/interfaces/Column.md +31 -31
  149. package/docs/api/interfaces/ColumnOptions.md +5 -4
  150. package/docs/api/interfaces/CreatedMiddlewareClass.md +7 -7
  151. package/docs/api/interfaces/DBConfig.md +13 -11
  152. package/docs/api/interfaces/DBConfigOptions.md +8 -8
  153. package/docs/api/interfaces/DeleteOptions.md +4 -4
  154. package/docs/api/interfaces/InsertOptions.md +8 -8
  155. package/docs/api/interfaces/LimitConfig.md +4 -4
  156. package/docs/api/interfaces/MiddlewareConfig.md +15 -15
  157. package/docs/api/interfaces/ModelOptions.md +8 -7
  158. package/docs/api/interfaces/PkeyResult.md +4 -4
  159. package/docs/api/interfaces/SelectOptions.md +17 -16
  160. package/docs/api/interfaces/SqlCondition.md +6 -6
  161. package/docs/api/interfaces/SqlFragment.md +5 -5
  162. package/docs/api/interfaces/SqlTypedFragment.md +7 -7
  163. package/docs/api/interfaces/TransactionOptions.md +7 -7
  164. package/docs/api/interfaces/UpdateManyOptions.md +4 -4
  165. package/docs/api/interfaces/UpdateOptions.md +4 -4
  166. package/docs/architecture.md +452 -0
  167. package/docs/benchmark-chart.svg +225 -249
  168. package/docs/design/phase-a-execution-context.md +2 -0
  169. package/package.json +20 -5
  170. package/dist/scp/writes.d.ts +0 -251
  171. package/dist/scp/writes.d.ts.map +0 -1
  172. package/dist/scp/writes.js +0 -176
  173. package/dist/scp/writes.js.map +0 -1
  174. package/docs/proposal/contracts-architecture-feasibility.md +0 -328
  175. package/docs/proposal/contracts-architecture.md +0 -579
  176. package/docs/proposal/litedbmodel-v2-scp-architecture.md +0 -433
  177. package/docs/proposal/sql-dynamic-expansion-spec.md +0 -175
  178. package/docs/proposal/v0-coverage-matrix.md +0 -157
  179. package/docs/proposal/v2-completeness-plan.md +0 -90
  180. package/docs/proposal/v2-sql-parity-checklist.md +0 -162
@@ -1,433 +0,0 @@
1
- # litedbmodel v2.0 アーキテクチャ仕様案 — SCP / 多言語 CQRS
2
-
3
- > **Status:** Draft(breaking / v2.0 系)。
4
- > 先行文書 [`contracts-architecture.md`](./contracts-architecture.md)(原案)と
5
- > [`contracts-architecture-feasibility.md`](./contracts-architecture-feasibility.md)(訂正第2版・実現可能性分析)を前提とする。
6
- > 本書はそれらを、**behavior-contracts の汎用 SCP レイヤを consume する SQL バックエンド**という形に確定させた v2.0 の仕様案。
7
-
8
- > **スコープ確定(2026-07-09 オーナー承認):**
9
- > - **対象言語 = TS + Rust + Python + Go + PHP の5言語 parity**(graphddb と同一。PHP は本書中の「(PHP)」括弧つき任意扱いを撤廃し first-class 化)。リリースは **5レジストリ**(npm / crates.io / PyPI / Go-tag / Packagist)。
10
- > - **リポ構成 = モノレポ統合**。`litedbmodel.ts` を単一モノレポとし、graphddb と同形に `src/`(TS SSoT) + `python/ go/ php/ rust/` を同居。**現 `litedbmodel.rs` の runtime を monorepo `rust/` へ移行**、旧 `foo-ogawa/litedbmodel.rs` リポは archive/crate-mirror のみ。単一 `conformance/` + 単一 CI + 単一 `sync-versions.mjs` で C2 実証。
11
- > - **ブレッドス = 全 CRUD(Select/Insert/Update/Delete) × 全方言(PG/MySQL/SQLite)**。§14 の v2 α(SQLite+TS Query 縦1本 = bc#1)を基盤に横展開。
12
- > - **ブランチ = `litedbmodel-scp`**(全 issue 同一ブランチ・rebase 禁止・merge 統合、`gh pr merge --merge`)。
13
-
14
- ---
15
-
16
- ## 0. 要約(TL;DR)
17
-
18
- - litedbmodel v2 は **新しい DSL を作らない**。**behavior-contracts の汎用 SCP レイヤ(Behavior/Component/Port/Wire + Expression IR + runtime-core)を consume する "SQL バックエンド consumer"** になる。graphddb(DynamoDB backend)と同型で、**差分は Catalog(SQL 操作群)と Backend Compile(IR→dialect SQL)と Handler(SQL 実行)だけ**(behavior-contracts の原則 **C2: difference is catalog only**)。
19
- - **コンパイル経路は1本、実行モードが3つ**(単一意味論、feasibility §2 / §9)。公開 API も SCP 宣言も**同一の Authoring Parse → 内部 IR** を通り、内部 IR 以降は全モード共通:
20
- 1. **TS 直接利用(eager)** — 公開 API 呼び出しを**同一コンパイラで動的に内部 IR 化**(キャッシュ)→ 共通 Runtime で実行(別解釈系は持たない)。
21
- 2. **SCP 宣言ブロック** — **`SemanticBehavior` クラスで Behavior を宣言**(effect 非依存。Query/Command は SCP の責務外で component graph から CQRS 層が導出)→ **ビルド時に事前コンパイル**して IR(dialect SQL + 動的 condition の fragment)/ 各言語コードへ変換。
22
- 3. **多言語利用** — publish された IR を各言語 runtime から呼ぶ(同一 IR)。
23
- - **Relation は Read 系だけでなく Write 系(write-time relations)**を持つ(graphddb 同型)。書込時に関連エンティティの整合・cascade・edge・counter・outbox を**1つの SQL トランザクションに導出**する。
24
- - **多言語 CQRS 対応**: 公開境界は CQRS(Query/Command)契約のみ。TS/Python/Rust/Go/PHP の薄い runtime が**同一 IR から同一 SQL・同一結果**を出す(conformance)。
25
- - リファクタ後は **v2.0 系**(破壊的変更)。v1.x は別ブランチ(`v1.x`)で保全済み。
26
-
27
- ---
28
-
29
- ## 1. 位置づけ — litedbmodel は SCP の SQL バックエンド consumer
30
-
31
- behavior-contracts は SCP を**汎用化**し、consumer が差し込む拡張点を確定させている(bc `runtime-boundary.md` / `concept.md`)。litedbmodel v2 は graphddb と並ぶ **2番目の consumer**(bc issue #1 の RDB プロファイルの本体)。
32
-
33
- ```
34
- behavior-contracts(汎用 SCP レイヤ・DSL 非依存)
35
- ├─ Behavior / Component / Port / Wire(意味の合成)
36
- ├─ Expression IR(閉じたオペレータ集合)
37
- ├─ Execution Plan(stage groups / skip 伝播 / Error Policy Kind)
38
- └─ runtime-core(validateEnvelope / evaluateExpression / renderTemplate / runPlan / canonicalValue / …)を TS/Python/Rust/Go で提供
39
- ▲ consume ▲ consume
40
- graphddb(DynamoDB backend) litedbmodel v2(SQL backend) ← 本書
41
- Catalog=GetItem/Query/… Catalog=Select/Insert/Update/Delete/Fragment/Tx
42
- Backend=DynamoDB API Backend=dialect SQL(PG/MySQL/SQLite)
43
- ```
44
-
45
- **litedbmodel v2 が実装する拡張点(これ以外は behavior-contracts から得る):**
46
-
47
- | 拡張点 | litedbmodel v2 での中身 |
48
- |---|---|
49
- | **Catalog 定義** | `Select` / `Insert` / `Update` / `Delete` / `Fragment`(動的 WHERE/SET 断片木)/ `Tx`(多文トランザクション)。各に Port schema(table・where(ExprIR)・set・limit・order 等) |
50
- | **Authoring Parse** | 公開 API(`User.find(...)`)と SCP 宣言(`SemanticBehavior` クラスのメソッド)を **Component-graph IR** へ落とす |
51
- | **Backend Compile** | IR → **dialect SQL テキスト + `?` パラメータ + fragment 木**(動的部)。`?`→`$N`(PG のみ・最終1パス) |
52
- | **Handler 実装** | 各 Catalog 名 → 実行関数(driver で SQL 実行 + 行→論理モデル assembly) |
53
- | **Error Mapping** | driver エラー(UNIQUE 制約違反・FK 違反・deadlock 等)→ SCP Failure(`constraint_violation` / `retryable` 等) |
54
-
55
- **帰結:** litedbmodel v2 は「SQL に特化した薄い層」であり、意味論・IR 構造・多言語 conformance・codegen は behavior-contracts と共有する。
56
-
57
- ---
58
-
59
- ## 2. 利用モード(本仕様の中核)
60
-
61
- 利用者が求める4つの姿を単一意味論(feasibility §2: 宣言→IR→Runtime が唯一の経路、消費モードが複数)で同居させる。
62
-
63
- ### 2.1 モデル定義(TS・schema.sql から生成可)
64
-
65
- 物理スキーマ(`schema.sql`)から **litedbmodel-gen** 等で TS のモデル定義(列 + 型 + 主キー)を生成する。手書き部(リレーション・メソッド・SCP ブロック)は保持(embedoc/gen のマーカー方式、v1 と同様)。
66
-
67
- ```ts
68
- // gen 生成部(schema.sql 由来)
69
- @model('users')
70
- export class UserModel extends DBModel {
71
- @column({ primaryKey: true }) id!: number;
72
- @column() name!: string;
73
- @column() post_count!: number; // derive 対象の counter
74
- }
75
- export const User = UserModel.asModel();
76
- ```
77
-
78
- ### 2.2 リレーション定義(Read 系 + Write 系)
79
-
80
- Read 系(従来の関連取得)に加え、**Write 系(write-time relations)をオプションで**持つ(graphddb 同型・§6)。
81
-
82
- ```ts
83
- @model('posts')
84
- export class PostModel extends DBModel {
85
- @column({ primaryKey: true }) id!: number;
86
- @column() author_id!: number;
87
- @column() title!: string;
88
-
89
- // ── Read-side(宣言 select または lazy)──
90
- @belongsTo(() => [Post.author_id, User.id]) declare author: User | null;
91
-
92
- // ── Write-side(書込時リレーション・任意)──
93
- static readonly writes = entityWrites<PostModel>((w) => ({
94
- create: w.lifecycle({
95
- requires: [ w.exists(() => User, { id: '$.input.author_id' }) ], // 参照整合
96
- unique: [ w.unique({ name: 'title_per_author', scope: ['$.input.author_id'], fields: ['$.input.title'] }) ],
97
- derive: [ w.increment(() => User, { id: '$.input.author_id' }, 'post_count', +1) ], // cascade counter
98
- emits: [ w.event('PostCreated', { postId: '$.entity.id', userId: '$.input.author_id' }) ], // outbox
99
- idempotency: w.idempotentBy('$.input.request_id'),
100
- }),
101
- remove: w.lifecycle({
102
- derive: [ w.increment(() => User, { id: '$.entity.author_id' }, 'post_count', -1) ],
103
- }),
104
- }));
105
- }
106
- ```
107
-
108
- ### 2.3 TS 直接利用(公開 API・eager)
109
-
110
- 従来どおり公開 API を**直接**呼ぶ。**経路は分岐させない**: 公開 API 呼び出しは
111
- **必ず「Authoring Parse → 内部 IR」という単一のコンパイル経路**を通り(AOT と**同一ロジック**)、
112
- 生成された内部 IR を**共通 Runtime**で即時実行する(結果はキャッシュ可)。
113
- 「eager だけ別の解釈系(メタデータを直に解釈する経路)」は持たない — **内部 IR 以降は全モードで完全に共通**(§9)。
114
- 書込は write-time relations があれば自動で1トランザクションに束ねられる(§6)。
115
-
116
- ```ts
117
- // Read(eager)
118
- const post = await Post.findById(1, { with: { author: true } }); // 宣言 select → staged batch + assembly
119
- const author = await post.author; // lazy(宣言しなければアクセス時解決・§9)
120
-
121
- // Write(write-time relations が自動導出 → 1 tx)
122
- await Post.create({ author_id: 7, title: 'Hello', request_id: 'r-123' });
123
- // → BEGIN; (author 存在チェック); (unique guard); INSERT post; UPDATE users.post_count+1; INSERT outbox; (idempotency); COMMIT
124
- ```
125
-
126
- ### 2.4 SCP 宣言ブロック(SCP 語彙で宣言 → ビルド時事前コンパイル → IR/TS コード)
127
-
128
- **litedbmodel 独自の宣言語彙(`defineQuery` 等)も、`query()`/`command()` のようなラッパも導入しない。**
129
- SCP の宣言が表すのは**「Component を合成して名前を与える=Behavior」だけ**。litedbmodel が供給するのは
130
- **Catalog(leaf Component: `Select`/`Insert`/… §11)だけ**(C2: 差分は Catalog のみ)。
131
- **Query / Command(read-only か write か)は SCP の責務ではない。** これは Behavior の component graph
132
- (write-Catalog を含むか)から**導出される CQRS 層の分類**であり、**SCP 宣言・authoring には一切書かない**。
133
-
134
- > **ルート指定(「全 export/全メソッドを publish するのか?」への回答):**
135
- > root Behavior は**クラス単位で指定**する。**`SemanticBehavior` を継承した(または `@behavior` を付けた)クラスの各 public メソッドが、そのまま root Behavior**。
136
- > publish しないヘルパは**そもそもこのクラスに入れない**(どうしても要るなら `private`/`#`)。**per-method のマーカーは不要**。
137
- > マーカーはクラスに1つ・effect 非依存(query/command を含まない)。builder(`behavior('name',{...})`)やメソッドごとのラッパ(`query(...)`)は採らない。
138
-
139
- ```ts
140
- // behaviors/posts.ts — このクラスの public メソッド = root Behavior。
141
- class PostBehaviors extends SemanticBehavior { // または: @behavior class PostBehaviors { … }
142
- PostSearch($: In<{ authorId: number; status?: string; since: string; limit?: number }>) {
143
- const posts = Select(Post, { // Catalog leaf component(litedbmodel 供給)
144
- where: [
145
- Post.author_id.eq($.in.authorId),
146
- $.in.status ? Post.status.eq($.in.status) : SKIP, // SKIP → fragment の存在規則(§8)
147
- Post.created_at.gte($.in.since),
148
- ],
149
- order: Post.created_at.desc(),
150
- limit: $.in.limit ?? 20,
151
- });
152
- const authors = posts.map((p) => // 構造化制御は SCP 共通(.map / ?: / &&)
153
- Select(User, { where: [User.id.eq(p.author_id)], select: ['id', 'name'] }));
154
- return { posts, authors }; // ← Output Port(ルートの出力)
155
- }
156
-
157
- CreatePost($: In<{ authorId: number; title: string; requestId: string }>) {
158
- return Insert(Post, { values: { author_id: $.in.authorId, title: $.in.title },
159
- onWrite: Post.writes.create, returning: ['id', 'title'] });
160
- }
161
-
162
- private helper(rows: Post[]) { return rows.filter((r) => !r.deleted); } // private → publish されない
163
- }
164
- ```
165
-
166
- **ルートの指定(直接回答):**
167
- - **ルート = `SemanticBehavior` 継承(既定)または `@behavior` 付きクラスの、各 public メソッド**。`private`/`#` はヘルパ扱いで publish されない。→ **マーカーはクラスに1個**、メソッドには不要。「大量の export を全部 component 化」はしない。
168
- - **Behavior 名** = メソッド名。**Input Port** = `$` の型 `In<...>`。**Output Port** = 返り値。内部 DAG は `$` 参照・`.map`・`?:` の**配線から Compiler が導出**(`await`/実行順は書かない)。
169
- - **Effect(Query/Command)は authoring に書かない。** component graph に write-Catalog(`Insert`/`Update`/`Delete`/`Tx`)が含まれるかで litedbmodel の **CQRS 層が導出**(含まなければ Query=read-only、含めば Command)。read-only 保証が要る箇所は Catalog 側の型で担保。クラス名(`XxxQueries`/`XxxCommands`)は命名慣習にすぎず意味を持たない。
170
- - **`extends SemanticBehavior` を既定**とする(デコレータ設定に依存せず、litedbmodel の `DBModel` / graphddb の `DDBModel` 基底クラス方式と一貫)。`@behavior` クラスデコレータも同義(有効な TS・同一 IR)。
171
- - leaf(Specialty Component)は Catalog(`Select`/`Insert`…)のみ。**SCP 宣言に litedbmodel 独自語彙は無い**。graphddb の `$`-rooted 束縛(`from("$.field")`)が同モデルの具体形。
172
-
173
- **注(エコシステム・§11/§15):** この authoring surface(`SemanticBehavior` 基底で root 指定・`$` 配線・structured control)は **effect 非依存で本来 behavior-contracts が持つべき SCP 層**。consumer は Catalog だけ供給し、Query/Command 分類は各 consumer の CQRS 層が graph から導出する(C2)。現状 bc 未実装・graphddb は独自 `publishQuery` のため、**共有 authoring(effect 非依存の `SemanticBehavior` 基底)を別 issue 化する**。
174
-
175
- - **公開されるのは Behavior 名**(`PostSearch` / `CreatePost`)のみ。SQL 種別(SELECT/INSERT)や effect 分類は runtime 不可視(分類は導出物)。
176
- - 入力の arity(単数/配列)・cardinality(one/many)は Catalog/Key から**導出され型で強制**(N+1 型安全)。
177
- - ビルド時に IR bundle(§8)へ落ち、`ir/` として publish される。
178
-
179
- ### 2.5 多言語利用(publish された宣言ブロックを各言語 runtime で)
180
-
181
- publish された IR bundle を、言語別の薄い runtime(behavior-contracts runtime-core + litedbmodel SQL runtime)が読み、**同一 SQL・同一結果**を実行する。
182
-
183
- ```python
184
- # Python(生成コード or IR dynamic)
185
- posts = PostQueries.search(db, author_id=7, since="2026-01-01", limit=10)
186
- ```
187
- ```go
188
- posts, _ := postQueries.Search(ctx, db, SearchInput{AuthorID: 7, Since: "2026-01-01"})
189
- ```
190
-
191
- ---
192
-
193
- ## 3. レイヤ構成
194
-
195
- ```
196
- 公開境界 = CQRS Contract(Query / Command)※ 多言語で共有
197
- ├─ TS 直接利用(eager)……… Authoring Parse → Component-graph IR → Handler(Native Interpret)
198
- └─ SCP 宣言ブロック(マーク付き関数)……… Component-graph IR(AOT)
199
- ↓ Backend Compile(litedbmodel)
200
- SQL IR = dialect SQL テキスト + fragment 木 + param slots(Expression IR) + assembly + relation ops + transaction plan
201
- ↓
202
- 薄い Runtime(TS/Python/Rust/Go): validate → 断片選択(SKIP) → 配列展開 → Expression 評価 → bind → SQL 実行 → assembly
203
- ```
204
-
205
- **generic(behavior-contracts)と SQL backend(litedbmodel)の分界:**
206
-
207
- | 汎用(behavior-contracts) | SQL backend(litedbmodel v2) |
208
- |---|---|
209
- | Component-graph IR 構造 / Execution Plan(groups・concurrency・skip 伝播・Policy Kind) | Catalog(Select/Insert/…)と各 Port schema |
210
- | Expression IR 評価(`evaluateExpression`)・閉じたオペレータ集合 | 条件/値の SQL 断片への **Backend Compile**(dialect SQL + `?` + fragment 木) |
211
- | `renderTemplate` / `canonicalValue` / `pyFloatRepr` / `validateEnvelope` / `runPlan` | Handler(driver で SQL 実行)・行→論理モデル **assembly** |
212
- | conformance runner 基盤・codegen 基盤(bc issue #13) | dialect 別 SQL 生成(PG/MySQL/SQLite)・型 hydration・接続/プール/tx |
213
- | Portability Guard | Error Mapping(driver エラー → Failure)・Raw SQL escape hatch |
214
-
215
- ---
216
-
217
- ## 4. モデル定義
218
-
219
- - `@model(table)` / `@column(opts)` で物理対応(v1 継承・gen 生成)。結果は **typed-object**(DBModel インスタンスではない・feasibility §9)。
220
- - 論理モデル ↔ 物理配置(table/column/PK/index)はモデル定義が吸収(原案 contracts-architecture.md「モデル定義」)。
221
- - litedbmodel-gen は `schema.sql` から列定義を生成し、**マーカー内は再生成・手書き(リレーション/writes/SCP ブロック)は保持**。
222
-
223
- ### 4.1 型システム(SQL 型ベース・SSoT)
224
-
225
- litedbmodel は SQL バックエンド consumer なので、**列型は SQL 型を SoT とする**(TS の `number` は int/real を区別できないため型の権威にしない — v1 の TS-型ベース `@column`(`design:type=Number`)が INTEGER/REAL を潰していたのを是正)。型は `schema.sql`(DDL)から確定し、**typed codegen(bc typed-raw 脱box)の `outType` 注記**に使う。interpret 経路(動的 Value)では列型は不要だったため v2 は本節を欠いていた。typed codegen が SQL レベルの型精度を要求するため、ここで規定する。
226
-
227
- **列型 → bc outType スカラ(正規対応表)**
228
-
229
- | SQL 型 | litedbmodel 列型 | bc outType | 備考 |
230
- |---|---|---|---|
231
- | INTEGER / INT / BIGINT | `int` | `int` | int は既定 **64bit (i64)**。狭いサイズ制限はデコレータオプション(制約)で表現し、**別型にしない**(int と bigint を分けない) |
232
- | REAL / FLOAT / DOUBLE | `real` | `float` | int と real は**明確に分離**(普通の処理系どおり) |
233
- | DECIMAL / NUMERIC | `decimal` | `string` | 精度保持のため文字列表現 |
234
- | TEXT / VARCHAR / CHAR / UUID | `text` | `string` | |
235
- | BOOLEAN | `bool` | `bool` | |
236
- | DATE / TIMESTAMP / DATETIME | `date` | `string` | **一旦 string**(オーナー決定)。bc の `date` scalar 新設(behavior-contracts#84)は将来の最適化として **defer**。litedbmodel 列型は `date` のまま(SQL/意味は保持)だが bc 表現は string。**TS のみ利便で `Date` へ de/serialize 可、他言語(Rust/Go/Py/PHP)は string 扱い**(JSON 列と同じ構図)。DB は行ごとに別 TZ を保存しない |
237
- | JSON / JSONB | `json` | `string` | 表現は JSON テキスト=**文字列**。TS のみ利便で object に de/serialize。列ごとに typed obj へ構造化しない |
238
-
239
- **構造型(クエリ由来。列型ではない)**
240
- - 行 = 列の **`obj{列: 型…}`**。
241
- - hasMany/list = **`arr<行obj>`**、belongsTo/hasOne = **`opt<行obj>`**、connection = `obj{items: arr<…>, cursor: opt<…>}`。
242
- - **object と array は明確に別**(一括の「json」型にしない)。JSON 列(上表)とは無関係。
243
-
244
- **3層(デコレータ型 ↔ SQL 型 ↔ bc outType)**
245
-
246
- 型は3層で一意対応させる。デコレータが authoring 面で列型を宣言し、SQL 型(DDL)が物理 SoT、bc outType が codegen 面。
247
-
248
- | litedbmodel 列型 | デコレータ | SQL 型 | bc outType |
249
- |---|---|---|---|
250
- | int | `@column.int()` | INTEGER/INT/BIGINT | `int` |
251
- | real | `@column.real()` | REAL/FLOAT/DOUBLE | `float` |
252
- | decimal | `@column.decimal()` | DECIMAL/NUMERIC | `string` |
253
- | text | `@column.text()`(既定)| TEXT/VARCHAR/CHAR/UUID | `string` |
254
- | bool | `@column.boolean()` | BOOLEAN | `bool` |
255
- | date | `@column.date()`/`.datetime()` | DATE/TIMESTAMP/DATETIME | `string`(一旦。bc date 型は #84 defer)|
256
- | json | `@column.json()` | JSON/JSONB | `string`(TS のみ object)|
257
-
258
- **デコレータ API 変更(v1 → v2 破壊的)**
259
- - **`@column.int()` / `@column.real()` を新設**。int は既定 **64bit(i64)**、狭いサイズ制限は `@column.int({ bits: 32 })` 等の**オプション制約**で表現(別型にしない)。
260
- - **`@column.bigint()` は廃止**(int が i64 なので冗長 → int に統合)。
261
- - **無印 `@column()`(`design:type=Number` 依存で int/real 曖昧)は禁止**。数値列は `.int()`/`.real()` を明示必須(未指定=error、no-assume)。他の型(string/bool/date/json)は design:type から一意なので従来どおり推論可。
262
- - object と array は別(json 列=string。行/リレーション構造の obj/arr とは無関係)。
263
-
264
- **規律**
265
- - 型が曖昧/未指定なら **error(no-assume・no-fallback)**。
266
- - SoT は `schema.sql` の SQL 型。`@column.*` は列型を宣言(gen は schema.sql → デコレータ型を生成)、converter(`src/scp/coltype.ts`)が上表で bc outType 記法へ**一意**変換する。
267
-
268
- **脱box は READ と WRITE の両方を覆う(§9 codegen 面)**
269
- - **READ**: SELECT projection を行 obj 型に型付け(`src/scp/makesql/outtype.ts`)→ bc typed-raw が具体的な行 struct を materialize。
270
- - **WRITE**: 書き込み出力=`TransactionResult` の typed shape(`src/scp/makesql/writeouttype.ts`):
271
- `obj{ committed:bool, executed:arr<string>, shortCircuit:opt<obj{statementId:string, reason:string}>, entity:opt<ROW>, returnedRows:opt<arr<arr<ROW>>> }`。
272
- `entity`/`returnedRows` の行(ROW)は、書き込み対象テーブル(tx plan の `body` statement の SQL から構造的に抽出)とその RETURNING 列を、**READ と同一の列型 resolver(本節の SoT)** で型付けする。脱box できない write shape(未知テーブル/列、`RETURNING *`、batch 群でテーブル/RETURNING が不一致 等)は **error(no-assume・no-fallback)** — 決してボックス化へ退避しない。READ の surrogate IR と同様、write bundle の `makeSqlComponentIR` の単一 node `outType` + component `outputType` に注記して bc typed-raw に渡す。
273
-
274
- ## 5. Relation — Read 系
275
-
276
- - Relation は SQL JOIN を**既定にしない**。graphddb / v1 LazyRelation と同型の **staged batch query-composition + object assembly**(feasibility §5・§9)。
277
- - `hasMany` → 親キー集合で `IN (...)` / `= ANY(...)`、per-parent limit は `LATERAL` / `ROW_NUMBER()`。
278
- - `belongsTo`/`hasOne` → 親から子キー収集 → 1回のバッチ SELECT。
279
- - Execution Plan は behavior-contracts の `deriveExecutionPlan`(result path → stage groups + concurrency)を流用。relation ops はモデル宣言から**方言別に事前コンパイル**して IR に同梱。
280
- - **宣言 select(推奨)** vs **lazy(fallback)** の2段(feasibility §9):
281
- - 宣言 select(`with:{...}`)… planner が兄弟をまとめて dedup 実行(先読み最適化)。
282
- - lazy(`await post.author`)… prototype getter + 非列挙 Symbol の batch context。事前コンパイル済み relation op を**実行時タイミングで起動**(IR 化可能)。
283
- - ホストオブジェクト化は graphddb と同形の `hydrate` factory(feasibility §9)。
284
- - JOIN は Backend Compile が特定形状に対して選ぶ**最適化**であって、意味論の既定ではない。
285
-
286
- ## 6. Relation — Write 系(write-time relations)
287
-
288
- 書込時に関連エンティティへ波及する宣言。graphddb の `entityWrites`/`edgeWrites` を **SQL 慣用へ翻訳**し、**1つの SQL トランザクションに導出**する。
289
-
290
- | 語彙 | 意味 | SQL への導出 |
291
- |---|---|---|
292
- | `requires` | 参照整合(関連が存在すること) | 先行 `SELECT ... FOR SHARE` / 存在ガード(FK 制約があれば併用) |
293
- | `unique` | フィールド一意性 | `UNIQUE` 制約 or ガード行 `INSERT ... ON CONFLICT DO NOTHING`+affected 検査 |
294
- | `edges` | 関連の書込側(多対多の中間表、1対多の FK 設定) | 中間表 `INSERT`/`DELETE`(M:N)/ FK 列 `UPDATE`(1:N) |
295
- | `derive` | 関連の派生値更新(counter 等) | cascade `UPDATE ... SET c = c ± n WHERE ...` |
296
- | `emits` | ドメインイベント | outbox テーブルへ `INSERT`(同一 tx) |
297
- | `idempotency` | クライアントトークン重複防止 | idempotency テーブルへ `INSERT`(`UNIQUE` 違反で重複検出) |
298
-
299
- **Command 導出(graphddb mutation-derivation 同型):** `CreatePost($: Command<...>) { return Insert(Post, { onWrite: Post.writes.create, ... }) }`(§2.4)の宣言的 intent から、コンパイラが `Post.writes.create` を展開して**順序付き多文トランザクション**を導出:
300
-
301
- ```
302
- BEGIN;
303
- -- Gate First(意味が変わらない限り早期に打ち切り。原案「Gate First」)
304
- requires: SELECT 1 FROM users WHERE id = :author_id; -- 無ければ即 fail・ROLLBACK
305
- idempotency: INSERT INTO idem(token) VALUES(:request_id); -- 重複なら短絡
306
- unique: INSERT INTO uniq(...) ON CONFLICT DO NOTHING; ... -- 衝突検査
307
- -- 本体
308
- INSERT INTO posts(author_id,title) VALUES(:author_id,:title) RETURNING id;
309
- derive: UPDATE users SET post_count = post_count + 1 WHERE id = :author_id;
310
- emits: INSERT INTO outbox(type,payload) VALUES('PostCreated', :payload);
311
- COMMIT;
312
- ```
313
-
314
- - **Transaction は公開仕様にしない**(原案)。公開されるのは Access Pattern(Command)のみ。tx DAG / 実行順(gate-first)は**導出される**。
315
- - 実行順・gate-first・依存は Execution Plan に反映。多言語 runtime は同一計画を honor(再導出しない)。
316
- - 多対多や nested write(親作成と同時に子作成)は `edges`/追加 write で表現し、同一 tx にまとめる。
317
-
318
- > **難所(正直な評価・§13):** SQL の tx 導出は DynamoDB の `TransactWriteItems`(≤25・read-your-writes 無し)と違い、対話的・ロック・read-after-write・gate-first 短絡を伴う。初期は「単文 Command + 明示 write-time relations(固定順)」に限定し、複雑な DAG 導出は後段(feasibility §7 と整合)。
319
- >
320
- > **後段の状況(WS8a・#28・§14 GA):** 複合 write(複数 base write / nested write)の **tx DAG 導出**を実装済み。各 write は名前を持ち、後続 write は先行 write の RETURNING 行を `$.ref.<writeName>.<field>` で参照する(例: 子 INSERT の `post_id` = 親の RETURNING id)。導出はデータ依存グラフ(statement → 参照する write)+ gate-first 制約(全 gate は全 body/derive/edge/emit に先行)を構築し、**トポロジカルソート**(安定した宣言順タイブレーク → 同一入力で byte-identical な SQL 列)で 1 tx の順序付き計画へ落とす。依存サイクル / 宙ぶらりんの `$.ref` / RETURNING 欠落は loud-reject(暗黙のフォールバック無し)。導出された DAG は純 JSON として §8 bundle に載り、各 statement の `binds` 名で RETURNING 行を scope に束縛するだけで 5 言語 runtime がそのまま実行する(再導出しない)。gate-first は DAG 全体で有効(任意 gate の短絡が下流の body/derive/edge/emit をすべて打ち切る)。
321
-
322
- ## 7. SCP 宣言ブロック → IR コンパイル
323
-
324
- `SemanticBehavior` クラス(または `@behavior`)の各 public メソッド(§2.4)の本体は **SCP の Composite Component** として扱われ、bc のコンパイルパイプラインで Component-graph IR へ落ちる。メソッド名=Behavior 名(ルート)、`$` の型引数=Input Port、返り値=Output Port、内部 DAG は配線から導出。effect(Query/Command)は SCP でなく CQRS 層が graph から導出(§2.4):
325
-
326
- ```
327
- TS 宣言(AST)
328
- → Authoring Parse(litedbmodel): 操作・relation・条件・projection を抽出
329
- → Component 化(Catalog 名 = Select/Insert/... + ports)
330
- → Wire / 依存抽出 / Cycle Check
331
- → 条件・値を **Expression IR** へ lower(bc `expression-ir.md`・閉集合・exprVersion 2)
332
- → Execution Plan 導出(stage groups / concurrency)
333
- → IR emit(portable JSON)or codegen(TS/他言語ソース)
334
- ```
335
-
336
- - 条件 `Post.author_id.eq($.authorId)` → `{eq:[{ref:["author_id"]},{ref:["input","authorId"]}]}`。
337
- - **動的条件は Expression でなく「fragment の存在規則」へ**: `cond ? [...] : SKIP` は値ではなく**断片の採否**(feasibility §4 / bc の SKIP は Expression 語彙外)。
338
- - lower 可能サブセット外(任意の自由 SQL)は **Raw SQL escape hatch**(契約付き・不透明・方言別同梱・§13)。
339
- - 制御構造は bc 準拠のネイティブ TS(`?:` / `&&` / `.map`)を lower(新語彙を足さない)。
340
-
341
- ## 8. IR の形(dialect 依存・feasibility §4 を確定)
342
-
343
- Component-graph IR(bc 汎用構造)の **litedbmodel Catalog** ノードが、Backend Compile で以下の SQL IR を持つ:
344
-
345
- - **dialect SQL テキスト**: 静的部は完全 flatten。動的部は **fragment 木**(事前コンパイル済み断片の順序木 + AND/OR 構造 + 各断片の存在規則)。
346
- - **placeholder は全方言 `?` 統一**(v1 内部表現と一致)。param 完全フラット化後に**最終1パスで `?`→`$N`**(PG のみ・左から機械変換)。番号振り直し問題は設計から消滅(feasibility §4)。
347
- - **params 配列**(`?` と 1:1): 各要素は ①入力参照 `prop.x` ②wire 参照 `{result.field}` ③**オペレータ IR** `{add:[prop.y,1]}`(bc Expression IR・閉集合)。
348
- - **assembly 仕様**: 行 → 論理モデル(relation items 付与位置含む)。
349
- - **relation ops**: モデル宣言から導出した全 relation バッチ SQL(方言別・§5)。
350
- - **transaction plan**: write-time relations の順序付き文 + gate-first + 依存(§6)。
351
-
352
- ```jsonc
353
- // 例: search クエリの Select ノード(Backend Compile 後)
354
- {
355
- "component": "Select",
356
- "sql": "SELECT id,author_id,title,created_at FROM posts WHERE author_id = ?{fragments}ORDER BY created_at DESC LIMIT ?",
357
- "fragments": [ // 動的 WHERE 断片(存在規則つき)
358
- { "when": {"present":["input","status"]}, "sql": " AND status = ?", "params": [{"ref":["input","status"]}] },
359
- { "always": true, "sql": " AND created_at >= ?", "params": [{"ref":["input","since"]}] }
360
- ],
361
- "params": [ {"ref":["input","authorId"]}, /* ...fragment 展開後... */ {"coalesce":[{"ref":["input","limit"]},20]} ],
362
- "assembly": { "shape": "items", "relations": { "author": { "op": "author__batch", "attach": "author" } } }
363
- }
364
- ```
365
-
366
- bundle 直列化は bc の envelope(`irVersion`/`exprVersion` + fail-closed)に従う(version skew は `validateEnvelope` で拒否)。
367
-
368
- ## 9. 実行経路 — コンパイル経路は1本、実行モードが3つ
369
-
370
- **コンパイル経路は単一**: 公開 API 呼び出しも SCP 宣言も、**同一の Authoring Parse → 内部 IR** を通る(同一ロジック・分岐なし)。内部 IR 以降(Backend Compile → Runtime)は**全モードで完全に共通**。異なるのは「その同一 IR を**いつ・どう実行するか**」の3モードだけ:
371
-
372
- 1. **動的コンパイル実行(TS・eager)** — 呼び出し時に**同じコンパイラで内部 IR を生成**(結果はキャッシュ)→ 共通 Runtime で in-process 実行。※ メタデータを直に解釈する別経路ではない(IR を必ず経由)。
373
- 2. **IR 参照・動的(全言語)** — publish 済み IR(JSON) を薄い runtime が `runPlan`/`evaluateExpression` で実行。
374
- 3. **Codegen・静的(全言語)** — IR → 各言語ソース生成(**ビルド時事前コンパイル**、runtime≈0)。litedbmodel の「ビルド時に TS コード/IR へ変換」はこれ(bc issue #13 の共有 generator に SQL catalog を供給)。
375
-
376
- 3モードとも**同一コンパイラ・同一内部 IR**を共有するため、経路差による意味論のズレは構造上生じない。eager で書いたロジックの SCP 化・多言語化は「配線を宣言に移す」リファクタで済む(feasibility §2)。
377
-
378
- ## 10. 多言語 Runtime と Conformance
379
-
380
- - 公開境界は CQRS のみ。各言語 runtime = **bc runtime-core(共有)+ litedbmodel SQL runtime(driver + assembly + dialect)**。
381
- - Conformance(bc の golden 方式): **同一 Contract + 同一入力 → 同一 SQL テキスト + 同一 assembly 結果**。
382
- - 分解: **方言軸はコンパイル時に TS 側で1回検証**(既存 SqlBuilder 資産 + golden SQL)。**言語軸は「同一 IR+入力 → 同一 SQL + 同一結果」の機械検証**。→ conformance 行列が「方言 3 × 言語 N」から「3 + N」に分解(feasibility §4)。
383
- - runtime が読むのは IR bundle(manifest 相当のスキーマ + operations 相当の契約/クエリ/コマンド/トランザクション + executionPlan)。
384
-
385
- ## 11. Consumer 実装点(litedbmodel が書くもの / bc から得るもの)
386
-
387
- **litedbmodel v2 が実装(§1 表の具体化):**
388
- 1. **Catalog**: `Select/Insert/Update/Delete/Fragment/Tx` の Port schema。
389
- 2. **Authoring Parse**: 公開 API・SCP 宣言(`SemanticBehavior` クラスのメソッド)→ Component-graph IR。
390
- 3. **Backend Compile**: IR → dialect SQL + fragment 木 + `?`→`$N`(既存 SqlBuilder を IR 消費型へ移植)。
391
- 4. **Handlers**: Catalog 名 → driver 実行 + assembly。
392
- 5. **Error Mapping**: driver エラー → SCP Failure(Policy Kind: fail/retry/continue)。
393
-
394
- **bc から得る(実装しない):** IR 構造・Expression 評価・Execution Plan 実行(skip 伝播/concurrency/Policy Kind)・`renderTemplate`/`canonicalValue`/`validateEnvelope`・Portability Guard・codegen 基盤・conformance runner。
395
-
396
- ## 12. TS 公開 API の v1 → v2 移行(破壊的)
397
-
398
- feasibility §9 で確定済み。要点のみ:
399
-
400
- | 面 | v1 | v2 | 破壊度 |
401
- |---|---|---|---|
402
- | CRUD + condition タプル + SKIP | Active Record | ほぼ不変(内部が IR 経路に) | 小 |
403
- | `await post.author`(lazy) | prototype getter(Promise) | 残す(getter → 事前コンパイル relation op 起動・§9) | 小 |
404
- | 結果オブジェクト | DBModel インスタンス | **typed-object**(own props はデータのみ) | **中〜大** |
405
- | インスタンスメソッド | クラスメソッド | typed-object には無い → `hydrate: (raw)=>new Domain(raw)` で回復 | 中 |
406
- | `sql`/dbDynamic/dbRaw | 実行時文字列 | Dynamic Slot 語彙(lower 可能サブセット内) | 小 |
407
- | 完全動的 Raw SQL | `execute`/`query` | 契約付き Raw SQL(方言別 SQL 同梱・IR 不透明) | 小 |
408
- | Middleware / TypeCast | 実行時 | Runtime 関心事として存続 | 小 |
409
-
410
- 破壊の中心は「結果がインスタンス → typed-object」。メソッドは `hydrate` で回復。**v1.x はメンテブランチ `v1.x` で保全**。
411
-
412
- ## 13. リスク / 難所
413
-
414
- 1. **SQL IR / lower 可能サブセットの線引き**(最重要・feasibility §4/§6): SQL-first の自由度 と 多言語決定的 lower は部分対立。Raw SQL は「不透明だが契約付き(I/O・Effect 宣言、SQL 文字列は方言別同梱、コンパイルしない)」に隔離。
415
- 2. **オペレータ/断片の決定性**: SKIP 合成順序・AND/OR 構造木・空 WHERE 縮退・`?`→`$N`・配列展開を多言語 byte 一致で仕様化(bc `expression-ir.md` の語彙を共有)。
416
- 3. **write-time relations の tx 導出 + gate-first**(§6): 研究的。初期は固定順・単文 Command 先行。
417
- 4. **意味的等価の限界**: SQL テキスト一致でも DB 挙動差(NULL 順序・照合・timezone・浮動小数)。conformance は「同一 SQL + 同一 assembly」を保証し、DB 差は方言コンパイル時規約(`ORDER BY ... NULLS` 強制等)で潰せる範囲に線引き。
418
-
419
- ## 14. 段階化 / ロードマップ
420
-
421
- bc の migration(litedbmodel は Phase 4 = 統合 generator + C2 実証で参入)と bc issue #1(RDB PoC)に整合。
422
-
423
- 1. **v2 α**: **SQLite + TS**・Query 契約のみで縦1本(公開 API/マーク付き関数 → Component-graph IR → Backend Compile → 薄い Runtime)。golden = 同一入力→同一 SQL + 同一結果。動的展開仕様(SKIP/fragment 木)を確定。← bc issue #1 の本体。
424
- 2. **v2 β**: Postgres/MySQL 方言追加(SqlBuilder 資産を IR 消費型へ移植)。write-side(単文 Command + write-time relations)。typed-object + hydrate + lazy 確定。
425
- 3. **v2 RC**: Python/Rust/Go/PHP runtime + conformance(言語軸)。Rust は現 `litedbmodel.rs` runtime を monorepo `rust/` へ移行。codegen(bc 共有 generator に SQL catalog 供給)。
426
- 4. **v2.0 GA**: 多言語 CQRS 公開・**5レジストリ** publish 基盤(npm/crates.io/PyPI/Go-tag/Packagist、behavior-contracts 方式)。write-time tx DAG 導出・gate-first 最適化。
427
-
428
- ## 15. バージョニング / エコシステム位置づけ
429
-
430
- - **v2.0 系(破壊的)**。v1.x はメンテブランチ `v1.x` で保全(別トラック)。
431
- - litedbmodel v2 = behavior-contracts の **SQL バックエンド consumer**(graphddb=DynamoDB と対)。IR/Expression/runtime-core/codegen/conformance を共有し、Catalog + Backend Compile + Handler + Error Mapping のみを供給。
432
- - litedbmodel-gen は SQL 側の Authoring 入口(`schema.sql` → モデル生成)として位置づけ。
433
- - 関連: behavior-contracts issue #1(RDB PoC)/ #13(共有 codegen)、[`contracts-architecture-feasibility.md`](./contracts-architecture-feasibility.md)(設計判断の根拠)。
@@ -1,175 +0,0 @@
1
- # SQL 動的展開仕様(Dynamic-Expansion Spec) — litedbmodel v2 SCP
2
-
3
- > **Status:** Normative Draft(WS1 / #21)。
4
- > 対象: `litedbmodel-v2-scp-architecture.md` §7/§8/§13 と `contracts-architecture-feasibility.md` §4 が要求する
5
- > 「動的断片の存在規則・AND/OR 構造木・空 WHERE 縮退・括弧規則・IN-list 配列展開・`?` 番号付け」の**規範**定義。
6
- > 本書は**多言語 byte 一致**(TS/Rust/Python/Go/PHP が同一 IR + 同一入力から同一 SQL テキストを出す)の基準文書である。
7
- > TS 参照実装は `src/scp/render.ts`(`renderOperation`)。golden ベクタは `test/scp/golden.test.ts` が pin する。
8
-
9
- 本書は Backend Compile(`src/scp/compile-sqlite.ts`)が生成した **SQL IR**(`CompiledOperation`・§8)を、
10
- 薄い runtime が**実行時入力**(`input` scope)とともに最終 SQL テキストへ展開する規則を定義する。方言は SQLite。
11
- placeholder は全て `?` に統一する(`?`→`$N` の PG パスは WS1 の範囲外)。
12
-
13
- ---
14
-
15
- ## 1. SQL IR の構成要素(`CompiledOperation`)
16
-
17
- 各 CRUD ノードは Backend Compile 後に以下を持つ(`src/scp/ir.ts`):
18
-
19
- - `sql`: 静的に確定する部分を完全 flatten した SQLite SQL テキスト。動的 WHERE を持つ操作は、
20
- WHERE が展開される 1 箇所に**スプライスマーカー `{where}`**(`WHERE_SLOT`)を含む。
21
- - `where`: 動的 WHERE の **fragment tree**(`FragmentTree | null`)。`null` は「動的 WHERE なし」。
22
- - `params`: **静的 param slot** 配列(fragment tree の外にある `?`。Insert values / Update SET / LIMIT / OFFSET)。
23
- 各要素は **Expression IR ノード(bc 閉集合のみ)**: ①入力参照 `{ref:["x"]}` ②wire 参照 `{ref:["<nodeId>","field"]}`
24
- ③演算子ノード `{coalesce:[{ref:["limit"]},20]}`。litedbmodel 独自オペコードは**一切**生成しない(§13 Raw SQL 隔離)。
25
- - `assembly`: 行→論理モデルの shape(WS1 は shape のみ)。
26
-
27
- **fragment(`Fragment`)**:
28
-
29
- | フィールド | 意味 |
30
- |---|---|
31
- | `always: true` | 無条件に存在する断片。`always` か `when` の**いずれか一方**を持つ。 |
32
- | `when: <Expr>` | **存在ガード**(Expression IR)。§2 の存在規則で採否を決める。 |
33
- | `sql` | 断片の SQL テキスト(`?` 込み。**先頭コネクタを含まない**)。 |
34
- | `params` | 断片の param slot(この断片の `?` と 1:1)。**存在するときだけ**組立配列へ追加。 |
35
- | `expand: <index>` | IN-list 展開スロット(§5)。`params[expand]` が配列で、`(?)` を `(?, …)` に開く。 |
36
-
37
- **fragment tree(`FragmentTree`)**:
38
-
39
- ```
40
- FragmentTree = { connector: "AND" | "OR", fragments: (Fragment | FragmentTree)[] }
41
- ```
42
-
43
- ---
44
-
45
- ## 2. SKIP → 断片の存在規則(fragment existence)
46
-
47
- authoring の `cond ? [<condition>] : SKIP`(spec §2.4)は **値ではなく断片の採否**である
48
- (feasibility §4 / bc `expression-ir.md` §4: **SKIP は Expression 語彙の外**)。Backend Compile はこれを
49
- 「`when` ガード付き fragment」に落とす。runtime の存在判定は:
50
-
51
- - `always === true` → **常に存在**。
52
- - `when` あり → `evaluateExpression(when, input)` を評価し、結果が **present** なら存在、**absent** なら**脱落**。
53
- - **absent** = `null` または `false`。
54
- - **present** = それ以外すべて(`0`・`""`・空配列 `[]` も present。空配列の IN-list 展開は §5 で別途縮退)。
55
- - `always` も `when` も無い fragment は**不正 IR**(fail-closed で脱落)。
56
-
57
- **脱落した fragment は SQL テキストにも params 配列にも一切寄与しない。** これが「SKIP された任意条件が
58
- present のときだけ現れ、absent のとき完全に消える」規則。`when` は明示的な presence/bool Expression を想定する
59
- (例: `{ne:[{refOpt:["status"]}, null]}`)。非 bool を返す `when` は evaluateExpression の strict 規律で
60
- fail-closed になる(暗黙 truthiness は存在しない)。
61
-
62
- **合成順序(決定性):** fragment は tree の `fragments` **配列順**にのみ評価・連結される。挿入順に依存しない。
63
- `when` の評価に副作用はない(純 Expression)。したがって同一 IR + 同一入力 → 同一採否集合 → 同一テキスト。
64
-
65
- ---
66
-
67
- ## 3. 空 WHERE の縮退(empty-WHERE degeneration)
68
-
69
- fragment tree を展開した結果、**present な fragment が 0 個**なら WHERE 本体は空文字列になる。このとき:
70
-
71
- - `SELECT` / `UPDATE` / `DELETE` いずれも **` WHERE ` キーワードごと脱落**する。
72
- すなわち `{where}` スプライスは**空文字列**に置換される(`SELECT … FROM t{where} ORDER BY …`
73
- → `SELECT … FROM t ORDER BY …`)。
74
- - 逆に present が 1 個以上なら `{where}` は ` WHERE <body>`(先頭に半角スペース + `WHERE` + スペース)に置換される。
75
-
76
- これは litedbmodel v1 `DBConditions.compile` の「`parts.length === 0` → `''`」および `_buildSelectSQL` の
77
- 「`if (whereClause) sql += ' WHERE ' + whereClause`」と byte 一致する。
78
-
79
- > **注(UPDATE/DELETE):** WS1 の compile は UPDATE/DELETE で WHERE が静的に空だと**コンパイル時に拒否**する
80
- > (v1 の `throw 'UPDATE requires conditions'` と同じ)。ただし全条件が `when` ガードで実行時に脱落し得る場合、
81
- > runtime 側の縮退規則は SELECT と同一(keyword ごと脱落)。安全な WHERE を保証するのは authoring/compile の責務。
82
-
83
- ---
84
-
85
- ## 4. AND/OR 構造木と括弧規則(parenthesization)
86
-
87
- - 1 つの tree の present fragment は ` <connector> `(前後に半角スペース)で連結する。
88
- top-level tree の connector は `AND`(litedbmodel v1 の `DBConditions` 既定と一致)。
89
- - **入れ子 tree は括弧で囲む**: 親 tree の要素が `FragmentTree` の場合、その展開結果が空でなければ
90
- `(<inner-body>)` として親へ寄与する(v1 の `(${nested.compile(...)})` と一致)。inner が空なら親へ寄与しない。
91
- - top-level tree 自身は括弧で囲まない。fragment の `sql` は**先頭コネクタを含まない**(連結はこの規則が行う)。
92
-
93
- 例(top=AND、入れ子 OR):
94
-
95
- ```
96
- [ eq(a), group(OR, [ eq(b), eq(c) ]) ] → "a = ? AND (b = ? OR c = ?)"
97
- ```
98
-
99
- 入れ子 OR の 1 要素だけ present:
100
-
101
- ```
102
- group(OR, [ present(b), skip(c) ]) → "(b = ?)"
103
- ```
104
-
105
- ---
106
-
107
- ## 5. IN-list 配列展開(array expansion)
108
-
109
- `col IN (?)` の fragment は `expand` スロットを 1 つ持つ。runtime は `params[expand]` を評価し:
110
-
111
- - **配列(長さ N ≥ 1)**: `(?)` を `(?, ?, …)`(`, ` 区切り・要素数 N)に置換し、各要素を params 配列へ順に push する。
112
- → `col IN (?, ?, ?)`。
113
- - **空配列(N = 0)**: fragment 全体(`col IN (?)`)を**常偽の番兵 `1 = 0`** に置換し、**params は push しない**。
114
- → litedbmodel v1 `DBConditions` の「`value.length === 0` → `1 = 0`」と byte 一致。
115
- - **非配列**: fail-closed(IR/入力不整合。runtime エラー)。
116
-
117
- `, ` 区切り・`( )` の付き方・空配列の `1 = 0` 番兵は全て v1 golden に固定する。
118
-
119
- ---
120
-
121
- ## 6. `?` 番号付け(placeholder numbering / param order)
122
-
123
- 全 placeholder は `?`。**番号は振らない**(`?`→`$N` は PG 専用の最終 1 パスで WS1 範囲外・feasibility §4)。
124
- params 配列は最終 SQL テキストの `?` 出現順(左→右)と**厳密に 1:1**。順序規則:
125
-
126
- 1. `{where}` マーカーより**前**にある静的 `?`(Insert values / Update SET)→ `params` の先頭から順に。
127
- 2. `{where}` 本体の fragment params → tree の配列順(present 分のみ、IN 展開後の要素順)。
128
- 3. `{where}` マーカーより**後**にある静的 `?`(LIMIT / OFFSET)→ 残りの静的 params。
129
-
130
- `renderOperation` は `{where}` 前の静的セグメントの `?` 個数で `params` を pre/post に分割し、この順で組み立てる。
131
- 静的部が全て WHERE の前後どちらかに寄る WS1 の CRUD 形状では、**spliced SQL の左→右走査**がそのまま canonical な
132
- `?` 順序になる。
133
-
134
- ---
135
-
136
- ## 7. CRUD ごとの生成形(golden 基準)
137
-
138
- placeholder `?`、`[ ]` は省略可能部。全て v1 SQLite builder / `_buildSelectSQL` / `DBConditions` と byte 一致。
139
-
140
- | 操作 | テンプレート |
141
- |---|---|
142
- | `Select` | `SELECT <cols> FROM <t>{where}[ GROUP BY <g>][ ORDER BY <o>][ LIMIT ?][ OFFSET ?]` |
143
- | `Insert` | `INSERT INTO <t> (<c1, c2, …>) VALUES (?, ?, …)[ ON CONFLICT (<k>) DO UPDATE SET <c = excluded.c, …>][ RETURNING <r>]` |
144
- | `Insert`(ignore) | `INSERT OR IGNORE INTO <t> (<cols>) VALUES (?, …)[ RETURNING <r>]` |
145
- | `Update` | `UPDATE <t> SET <c1 = ?, c2 = ?, …>{where}[ RETURNING <r>]` |
146
- | `Delete` | `DELETE FROM <t>{where}[ RETURNING <r>]` |
147
-
148
- 条件断片の形(`sql`・先頭コネクタなし):
149
-
150
- | 条件 | fragment `sql` | params |
151
- |---|---|---|
152
- | 等値 | `<col> = ?` | `[value]` |
153
- | 比較 | `<col> <op> ?`(`op ∈ {<,<=,>,>=,<>}`) | `[value]` |
154
- | NULL | `<col> IS NULL` | `[]` |
155
- | IN | `<col> IN (?)`(`expand:0`) | `[arrayValue]` |
156
- | group | (§4 で連結・括弧) | — |
157
-
158
- `<cols>` は `select` を `, ` で join(単一 `['*']` は `*`)。RETURNING columns も `, ` join。
159
-
160
- > **列順は canonical(辞書順)— v2 破壊的変更(v1.x は保全):** `Insert` の列順・`Update` の SET 列順は
161
- > **辞書順(alphabetical)に正規化**する。これは言語非依存で、多言語 byte-identical conformance(WS7)に
162
- > **必須**の決定性要件。v1.x の単一レコード INSERT は author 挿入順を保持していた(`DBModel._insert` 高速パス)が、
163
- > v2 はこれを廃し、imperative パス(`DBModel._insert`)も SCP Backend-Compile パス(bc-canonical port キー)も
164
- > **同一の辞書順**に揃える(「現行 v2 直接実行 == SCP」を構造的に保証)。旧 author 順が必要な利用は v1.x ブランチに残す。
165
-
166
- ---
167
-
168
- ## 8. 決定性チェックリスト(多言語実装の受け入れ基準)
169
-
170
- - [ ] fragment 採否は `fragments` 配列順に評価し、`when` present/absent のみで決める(§2)。
171
- - [ ] present 0 個 → ` WHERE ` キーワードごと脱落(§3)。
172
- - [ ] 入れ子 tree は空でなければ `( )` で囲む。top は囲まない。連結子は ` AND `/` OR `(§4)。
173
- - [ ] IN 空配列 → `1 = 0`(params push なし)。長さ N → `(?, …)` N 個(§5)。
174
- - [ ] `?` は params と左→右で 1:1。番号なし(§6)。
175
- - [ ] 全 param slot は bc 閉集合の Expression IR のみ(`src/scp/guard.ts` `assertOperationPortable` が fail-closed 検査)。