litedbmodel 2.2.2 → 2.2.3
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.
- package/README.md +2 -1
- package/dist/DBModel.d.ts.map +1 -1
- package/dist/DBModel.js +3 -0
- package/dist/DBModel.js.map +1 -1
- package/dist/Middleware.d.ts +19 -2
- package/dist/Middleware.d.ts.map +1 -1
- package/dist/Middleware.js +1 -0
- package/dist/Middleware.js.map +1 -1
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +8 -0
- package/dist/decorators.js.map +1 -1
- package/dist/drivers/MysqlHelper.d.ts +1 -1
- package/dist/drivers/MysqlHelper.d.ts.map +1 -1
- package/dist/drivers/MysqlHelper.js.map +1 -1
- package/dist/drivers/PostgresHelper.d.ts +1 -1
- package/dist/drivers/PostgresHelper.d.ts.map +1 -1
- package/dist/drivers/PostgresHelper.js.map +1 -1
- package/dist/drivers/SqliteHelper.d.ts +1 -1
- package/dist/drivers/SqliteHelper.d.ts.map +1 -1
- package/dist/drivers/SqliteHelper.js.map +1 -1
- package/dist/index.cjs +99 -33
- package/dist/scp/connection-routing.d.ts +38 -1
- package/dist/scp/connection-routing.d.ts.map +1 -1
- package/dist/scp/connection-routing.js +64 -5
- package/dist/scp/connection-routing.js.map +1 -1
- package/dist/scp/dbmodel-runtime.d.ts +2 -1
- package/dist/scp/dbmodel-runtime.d.ts.map +1 -1
- package/dist/scp/dbmodel-runtime.js +2 -1
- package/dist/scp/dbmodel-runtime.js.map +1 -1
- package/dist/scp/decorator-adapter.d.ts +22 -0
- package/dist/scp/decorator-adapter.d.ts.map +1 -1
- package/dist/scp/decorator-adapter.js +26 -0
- package/dist/scp/decorator-adapter.js.map +1 -1
- package/dist/scp/emit/emitter.d.ts +21 -10
- package/dist/scp/emit/emitter.d.ts.map +1 -1
- package/dist/scp/emit/emitter.js +119 -51
- package/dist/scp/emit/emitter.js.map +1 -1
- package/dist/scp/emit/endpoint.d.ts +3 -8
- package/dist/scp/emit/endpoint.d.ts.map +1 -1
- package/dist/scp/exec-context.d.ts +39 -17
- package/dist/scp/exec-context.d.ts.map +1 -1
- package/dist/scp/exec-context.js +69 -35
- package/dist/scp/exec-context.js.map +1 -1
- package/dist/scp/grouping.d.ts.map +1 -1
- package/dist/scp/grouping.js +0 -0
- package/dist/scp/grouping.js.map +1 -1
- package/dist/scp/index.cjs +251 -629
- package/dist/scp/index.d.ts +6 -11
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +10 -28
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +251 -617
- package/dist/scp/leaf-transport.d.ts +127 -39
- package/dist/scp/leaf-transport.d.ts.map +1 -1
- package/dist/scp/leaf-transport.js +30 -24
- package/dist/scp/leaf-transport.js.map +1 -1
- package/dist/scp/leaves.d.ts +57 -20
- package/dist/scp/leaves.d.ts.map +1 -1
- package/dist/scp/leaves.js +224 -65
- package/dist/scp/leaves.js.map +1 -1
- package/dist/scp/makesql/compile-select.d.ts +28 -1
- package/dist/scp/makesql/compile-select.d.ts.map +1 -1
- package/dist/scp/makesql/compile-select.js +19 -17
- package/dist/scp/makesql/compile-select.js.map +1 -1
- package/dist/scp/makesql/index.d.ts +2 -2
- package/dist/scp/makesql/index.d.ts.map +1 -1
- package/dist/scp/makesql/index.js +4 -6
- package/dist/scp/makesql/index.js.map +1 -1
- package/dist/scp/makesql/tx.d.ts +27 -69
- package/dist/scp/makesql/tx.d.ts.map +1 -1
- package/dist/scp/makesql/tx.js +26 -448
- package/dist/scp/makesql/tx.js.map +1 -1
- package/dist/scp/middleware.d.ts +4 -4
- package/dist/scp/middleware.d.ts.map +1 -1
- package/dist/scp/middleware.js +2 -2
- package/dist/scp/middleware.js.map +1 -1
- package/dist/scp/relation.d.ts +31 -12
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +16 -6
- package/dist/scp/relation.js.map +1 -1
- package/dist/scp/typed-object.d.ts +23 -19
- package/dist/scp/typed-object.d.ts.map +1 -1
- package/dist/scp/typed-object.js +24 -30
- package/dist/scp/typed-object.js.map +1 -1
- package/dist/scp/write-bundle.d.ts +21 -70
- package/dist/scp/write-bundle.d.ts.map +1 -1
- package/dist/scp/write-bundle.js +15 -79
- package/dist/scp/write-bundle.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/docs/architecture.md +452 -0
- package/docs/design/phase-a-execution-context.md +2 -0
- package/package.json +14 -3
- package/dist/scp/writes.d.ts +0 -251
- package/dist/scp/writes.d.ts.map +0 -1
- package/dist/scp/writes.js +0 -176
- package/dist/scp/writes.js.map +0 -1
- package/docs/proposal/contracts-architecture-feasibility.md +0 -328
- package/docs/proposal/contracts-architecture.md +0 -579
- package/docs/proposal/litedbmodel-v2-scp-architecture.md +0 -433
- package/docs/proposal/sql-dynamic-expansion-spec.md +0 -175
- package/docs/proposal/v0-coverage-matrix.md +0 -157
- package/docs/proposal/v2-completeness-plan.md +0 -90
- package/docs/proposal/v2-sql-parity-checklist.md +0 -162
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
# V0 — litedbmodel v2 completeness coverage matrix
|
|
2
|
-
|
|
3
|
-
> Step 1 of `v2-completeness-plan.md` §5. The definitive op × dialect × {byte-match?, live-exec on PG+MySQL?}
|
|
4
|
-
> matrix for the whole read+write makeSQL surface, plus the confirmed/adjusted R1–R8 gap list against reality.
|
|
5
|
-
>
|
|
6
|
-
> Definitions (plan §1/§3):
|
|
7
|
-
> - **byte** = a `makesql-golden.test.ts` assert that the compiled makeSQL reproduces the v1 builder's direct
|
|
8
|
-
> output byte-for-byte, on every dialect where the dialect text differs. `(P/M/S)` = postgres/mysql/sqlite.
|
|
9
|
-
> - **live** = the op is exercised on real dockerized PG **and** MySQL through the `conformance/vectors-livedb`
|
|
10
|
-
> corpus (all 5 language runtimes replay the same bundle). SQLite is the in-proc reference.
|
|
11
|
-
> - **done** = byte (all differing dialects) **AND** live (PG+MySQL). Byte-only / PG-only / SQLite-mock-only ≠ done.
|
|
12
|
-
|
|
13
|
-
## Reachability model (the load-bearing V0 finding)
|
|
14
|
-
|
|
15
|
-
There are TWO distinct compile entry points, and they do **not** cover the same construct set:
|
|
16
|
-
|
|
17
|
-
1. **`compileWhere` / `compileSelect` / `compile*` (direct v1-parity functions)** — take a v1-shaped
|
|
18
|
-
`ConditionObject` / `SelectDesc` and reproduce the v1 builder byte-for-byte. `makesql-golden.test.ts` drives
|
|
19
|
-
these directly (hand-building `DBSubquery`/`DBExists`/`dbCast`/`__raw__`/`SelectDesc.join|cte|append`). This
|
|
20
|
-
is where BYTE coverage lives.
|
|
21
|
-
|
|
22
|
-
2. **The authoring→bundle path (`L.Select(...)` → `compileBundle` → `compileSelectNode`)** — the ONLY path that
|
|
23
|
-
produces a live-executable bundle the language runtimes replay. Its WHERE comes from the bc closed-set
|
|
24
|
-
fragment IR (`whereEq/whereNe/whereLt/whereLe/whereGt/whereGe/whereIsNull/whereIn` — `authoring-sql.ts`), and
|
|
25
|
-
`compileSelectNode` wires only the `SELECT_PORTS` catalog surface: `table, select, where, order, limit,
|
|
26
|
-
offset, group` (`catalog.ts` `SELECT_PORTS`). This is where LIVE coverage lives.
|
|
27
|
-
|
|
28
|
-
**Consequence:** a construct is live-reachable only if it can be expressed through the fixed §2 authoring surface
|
|
29
|
-
(the bc where-primitives + the `SELECT_PORTS`). Constructs that exist ONLY in the v1 `DBConditions`/`DBValues`
|
|
30
|
-
layer or in `SelectDesc` fields with no catalog port are byte-provable but **cannot be placed in a live bundle**
|
|
31
|
-
without adding a new authoring primitive / catalog port — a §2 interface change (invariant #2 → escalate, do not
|
|
32
|
-
hand-roll).
|
|
33
|
-
|
|
34
|
-
## Matrix — READ surface
|
|
35
|
-
|
|
36
|
-
| Op / construct | byte (v1) | live PG+MySQL | Reachable via §2 authoring? | Status |
|
|
37
|
-
|---|---|---|---|---|
|
|
38
|
-
| Select head `SELECT <cols> FROM <t>` | ✅ P/M/S | ✅ (Feed/Posts/ByIds…) | yes | **done** |
|
|
39
|
-
| WHERE eq / ne / lt / le / gt / ge | ✅ P/M/S | ✅ (Feed whereEq/whereGe; when/ne SKIP) | yes (`whereEq`…) | **done** |
|
|
40
|
-
| WHERE IS NULL / IS NOT NULL | ✅ P/M/S | ⚠️ IS NULL via `whereIsNull` reachable; not in a live vector | partial (isNull yes; NOT NULL = `dbNotNull`, not authorable) | byte done; live gap is cosmetic (eq-null path already live via SKIP) |
|
|
41
|
-
| IN-list (single col, non-empty) | ✅ P/M/S (M/S = JSON deviation) | ✅ (ByIds/ByUuids/ByBig/ByTxt/ByFlag/ByTs/ByAmt) | yes (`whereIn`+`inColumn`) | **done** |
|
|
42
|
-
| IN-list empty → `1 = 0` | ✅ P/M/S | ✅ (ByIds empty) | yes | **done** |
|
|
43
|
-
| COUNT(*) [+ WHERE] | ✅ P/M/S | ✅ (CountAll/CountByAuthor/empty) | yes (`L.Count`) | **done** |
|
|
44
|
-
| GROUP BY | ✅ P/M/S (SELECT-tail golden) | ✅ (GroupByAuthor) | yes (`group` port) | **done** |
|
|
45
|
-
| ORDER BY / LIMIT / OFFSET | ✅ P/M/S | ✅ (Feed order/limit) | yes | **done** |
|
|
46
|
-
| FOR UPDATE | ✅ P/M/S (SELECT-tail + §D authored) | ✅ (ForUpdate, PG+MySQL) | yes (`forUpdate` port added) | **done (R3)** |
|
|
47
|
-
| BETWEEN | ✅ P/M/S (custom-op + §D authored) | ✅ (IdBetween) | yes (`whereBetween` added) | **done (R3)** |
|
|
48
|
-
| LIKE | ✅ P/M/S (custom-op + §D authored) | ✅ (TitleLike) | yes (`whereLike` added) | **done (R3)** |
|
|
49
|
-
| ILIKE | ✅ P/M/S (§D authored) | PG-only construct (see note) | yes (`whereILike` added) | byte done; live = PG-only (v1 `ILIKE` keyword errors on MySQL/SQLite — reproducing it live there = a v1 error, not a gap) |
|
|
50
|
-
| tuple / composite IN (`__tuple__`) | ✅ P/M/S (§D authored) | ✅ (TupleIn) | yes (`whereTupleIn`, literal tuples) | **done (R3)** |
|
|
51
|
-
| dbCast `::type` (dialect-gated) | ✅ P/M/S (§D authored) | ✅ (DocByCast: `::uuid` PG / no-cast MySQL·SQLite) | yes (`whereCast` added) | **done (R3)** |
|
|
52
|
-
| dbDynamic `fn(?)` | ✅ P/M/S (§D authored) | portable-fn only (see note) | yes (`whereDynamic` added) | byte done; live for a PORTABLE fn is addable; the byte exemplar `to_tsvector` is PG-only (a v1 PG construct, not a gap) |
|
|
53
|
-
| dbImmediate (inline SQL, no bind) | ✅ P/M/S (§D authored) | ✅ (ImmediateEq: `author_id = 7`) | yes (`whereImmediate` added) | **done (R3)** |
|
|
54
|
-
| IN(subquery) single key | ✅ P/M/S (§D authored) | ✅ (InSubquery) | yes (`whereInSubquery`, nested Fragment) | **done (R2)** |
|
|
55
|
-
| NOT IN(subquery) | ✅ P/M/S (§D authored) | ✅ (NotInSubquery) | yes (`whereInSubquery(…, true)`) | **done (R2)** |
|
|
56
|
-
| composite (a,b) IN(subquery) | ✅ P (byte, existing) | authorable via nested Fragment `sql` | yes (`whereInSubquery` w/ composite lhs+sql) | byte done; live addable (same nested-Fragment path) |
|
|
57
|
-
| EXISTS / NOT EXISTS (correlated) | ✅ P/M/S (§D authored) | ✅ (ExistsComment / NotExistsComment) | yes (`whereExists`, nested Fragment) | **done (R2)** |
|
|
58
|
-
| `= ANY(?::type[])` scalar-array (PG) | ✅ P (raw) | ✅ (relation batches = ANY($1::int[])) | via relation compile | **done** (as relation batch) |
|
|
59
|
-
| CTE / WITH | ✅ P/M/S (§D authored) | ✅ (CteLive) | yes (`cte`/`cteParams` ports added) | **done (R4)** |
|
|
60
|
-
| JOIN | ✅ P/M/S (§D authored) | ✅ (JoinAuthor) | yes (`join`/`joinParams` ports added) | **done (R5)** |
|
|
61
|
-
| append / HAVING | ✅ P/M/S (§D authored) | ✅ (HavingAuthor) | yes (`append` port added) | **done (R6)** |
|
|
62
|
-
| findByPkeys / findById (`compileFindByPkeys`) | n/a (dead) | n/a | DELETED (redundant) | **done (R7 — dead SCP export removed; v1 `buildFindByPkeys` kept)** |
|
|
63
|
-
| FIND_FILTER (per-model soft filter) | ✅ = authored WHERE (no-op proof) | ✅ via authored WHERE | folded into WHERE upstream | **done (R8 — evidence: merged filter == authored WHERE, byte+live done)** |
|
|
64
|
-
|
|
65
|
-
## Matrix — RELATIONS
|
|
66
|
-
|
|
67
|
-
| Relation shape | byte (v1 LazyRelation) | live PG+MySQL | Status |
|
|
68
|
-
|---|---|---|---|
|
|
69
|
-
| single-key belongsTo unlimited | ✅ P/M/S | ✅ (Posts author) | **done** |
|
|
70
|
-
| single-key hasMany unlimited (+order+where) | ✅ P/M/S | ✅ (Posts comments) | **done** |
|
|
71
|
-
| single-key hasMany + per-parent LIMIT (LATERAL / ROW_NUMBER) | ✅ P/M/S | ✅ (Posts tags limit=2; Feed) | **done** |
|
|
72
|
-
| composite-key hasMany unlimited | ✅ P/M/S | ✅ (Docs revisions) | **done** |
|
|
73
|
-
| composite-key hasMany + LIMIT (STATIC LATERAL / ROW_NUMBER) | ✅ P/M/S (byte + negative; PG byte-identical to v1 LATERAL, M/S sanctioned static JSON-predicate deviation) | ✅ (Docs latestRev limit=1 — livedb vector + TS ScpDialect PG+MySQL) all 5 runtimes | **done (#47 last gap)** |
|
|
74
|
-
| composite-key belongsTo | ✅ (composite unnest) | ✅ (Docs owner) | **done** |
|
|
75
|
-
| composite STATIC unnest form (PG) | ✅ P (byte + negative) | ✅ | **done** |
|
|
76
|
-
| composite STATIC limited form (`compileCompositeKeyStaticLimited`) | ✅ P (byte-identical to v1) / M/S (window byte-identical, JSON key-set predicate) + negative | ✅ all 5 runtimes | **done** |
|
|
77
|
-
| **cross-DB relations (target driver/connection)** | ✅ (SQL = target's dialect, byte-identical) | ✅ ALL 5 runtimes — TS two-DB (SQLite×2); py/php/go/rust live PG↔MySQL cross-DB vector | **done (R1 — per-statement `connection` tag + per-runtime routing; live cross-DB run across all 5 runtimes)** |
|
|
78
|
-
|
|
79
|
-
## Matrix — WRITE surface
|
|
80
|
-
|
|
81
|
-
| Op | byte (v1) | live PG+MySQL | Status |
|
|
82
|
-
|---|---|---|---|
|
|
83
|
-
| INSERT single | ✅ P/M/S (M/S JSON deviation) | ✅ (tx Create) | **done** |
|
|
84
|
-
| createMany homogeneous | ✅ P/M/S | ✅ | **done** |
|
|
85
|
-
| createMany heterogeneous (column-set groups) | ✅ P/M/S | ✅ | **done** |
|
|
86
|
-
| createMany DBToken(NOW()) → v1 fallback | ✅ M/S (byte-pinned) | ✅ (dbRaw NOW()) | **done** |
|
|
87
|
-
| upsert ON CONFLICT DO UPDATE / IGNORE | ✅ P/M/S | ✅ (tx idempotency/unique ON CONFLICT/IGNORE) | **done** |
|
|
88
|
-
| updateMany (per-row) | ✅ P/M/S | ✅ | **done** |
|
|
89
|
-
| **deleteMany (single + composite PK)** | ✅ P/M/S **(ADDED in V0)** | ✅ (deleteMany {1,3}) | **done (this increment)** |
|
|
90
|
-
| single UPDATE (+per-col PG cast) | ✅ P/M/S | ✅ (bare UPDATE) | **done** |
|
|
91
|
-
| single DELETE (IN-list) | ✅ P/M/S | ✅ (bare DELETE) | **done** |
|
|
92
|
-
| RETURNING forms (bare / t.col / table.col / MySQL none) | ✅ P/M/S | ✅ (across tx) | **done** |
|
|
93
|
-
| DELETE…RETURNING (MySQL emul → []) | ✅ | ✅ (per-dialect vector) | **done** |
|
|
94
|
-
| INSERT RETURNING uuid-PK / composite-PK (MySQL re-select) | ✅ | ✅ | **done** |
|
|
95
|
-
| tx gate spine (requires/idempotency/unique/derive/emits) | ✅ (mock corpus) | ✅ (commit + ROLLBACK) | **done** |
|
|
96
|
-
| tx edge (m2m link + fk claim) | ✅ | ✅ | **done** |
|
|
97
|
-
| tx composite multi-write DAG | ✅ | ✅ | **done** |
|
|
98
|
-
|
|
99
|
-
## Confirmed/adjusted gap list (R1–R8)
|
|
100
|
-
|
|
101
|
-
The plan assumed R2/R3 were "byte-only, live-zero" additive-only fills. V0 confirmed the byte side but found the
|
|
102
|
-
LIVE side was **blocked by the fixed §2 authoring surface**. Per plan §2 補足 the surface was then extended
|
|
103
|
-
ADDITIVELY (new where-primitives + new `SELECT_PORTS` ports — no signature change, no new bundle kind, SQL still
|
|
104
|
-
v1-sourced), closing R2/R3-remainder/R4/R5/R6 to byte+live. R1's bundle-shape tag landed additively; its
|
|
105
|
-
per-language LIVE cross-DB run is the one escalation.
|
|
106
|
-
|
|
107
|
-
| Item | Final state | How |
|
|
108
|
-
|---|---|---|
|
|
109
|
-
| **R1** cross-DB relations | **done (byte + live, all 5 runtimes)** | `RelationDecl.connection?`/`RelationOp.connection?` (additive, untagged = byte-unchanged); the relation SQL compiles for the TARGET model's own `dialect` (byte-identical to v1). Each runtime's read path (`read_bundle`/`ReadBundle`/`readBundle`/`read_bundle_pooled`) takes a connection REGISTRY and routes a tagged relation to `registry[tag]` (untagged → primary; loud-fail on a missing tag). Live: a `kind:'crossdb'` corpus vector runs the parent on connection A and the tagged `author` batch on connection B — pg leg parent=PG + author→MySQL, mysql leg parent=MySQL + author→PG (the parent table lives ONLY on A, the target ONLY on B, so a mis-route fails loudly ⇒ a green hydrate is unforgeable). TS reference = two separate SQLite DBs (`cross-db-relation.test.ts`). |
|
|
110
|
-
| **R2** subquery / EXISTS / NOT IN / NOT EXISTS | **done (byte + live PG+MySQL)** | `whereInSubquery`(+NOT) / `whereExists`(+NOT) — subquery as a nested makeSQL Fragment in the param slot. Live: InSubquery/NotInSubquery/ExistsComment/NotExistsComment. |
|
|
111
|
-
| **R3** GROUP BY / FOR UPDATE / BETWEEN / LIKE / tuple-IN / dbCast / dbImmediate | **done (byte + live)** | `group` (already) + new `forUpdate` port + `whereBetween`/`whereLike`/`whereTupleIn`/`whereCast`/`whereImmediate`. Live: GroupByAuthor/ForUpdate/IdBetween/TitleLike/TupleIn/DocByCast/ImmediateEq. ILIKE + dbDynamic(to_tsvector) are byte-done + PG-only-by-nature (see notes in the READ matrix — not live gaps). |
|
|
112
|
-
| **R4** CTE / WITH | **done (byte + live)** | `cte`/`cteParams` ports added; `compileSelect` WITH-wrap is v1-sourced. Live: CteLive. |
|
|
113
|
-
| **R5** JOIN | **done (byte + live)** | `join`/`joinParams` ports added; `compileSelect` JOIN position is v1-sourced. Live: JoinAuthor. |
|
|
114
|
-
| **R6** append / HAVING | **done (byte + live)** | `append` port added (raw trailing clause). Live: HavingAuthor. |
|
|
115
|
-
| **R7** findById/findByPkeys | **done — DELETED** | `compileFindByPkeys` + `FindByPkeysOptions` import + 2 re-exports removed (dead SCP export, redundant with live IN-list/composite-relation reads). v1 `buildFindByPkeys` kept (DBModel.findById). |
|
|
116
|
-
| **R8** FIND_FILTER | **done — no-op with evidence (variant b)** | v1 merges FIND_FILTER into the WHERE `DBConditions` BEFORE compiling (`DBModel.ts:596-604`/`:858-866`); the SCP compile reimplements the WHERE text from explicit `conditions` and never reads FIND_FILTER. `find-filter-noop.test.ts` proves (3 dialects) the merged filter == the authored WHERE byte-for-byte — folded upstream, a genuine no-op for makeSQL. |
|
|
117
|
-
|
|
118
|
-
### R1 — per-language LIVE cross-DB relation run (LANDED)
|
|
119
|
-
|
|
120
|
-
The escalated 5-runtime change was approved and landed as thin, SQL-independent routing plumbing (no SQL
|
|
121
|
-
touched — the relation SQL is v1-identical, compiled for whichever dialect its target DB speaks):
|
|
122
|
-
|
|
123
|
-
1. **Corpus mode.** A `kind:'crossdb'` read vector (`conformance/gen-livedb.ts`) whose parent `posts` read runs
|
|
124
|
-
on the leg's PRIMARY DB and whose tagged `author` belongsTo runs on the SECONDARY (OTHER) live DB. Per leg
|
|
125
|
-
the tagged relation op is compiled for the SECONDARY dialect: pg leg parent=PG + author→MySQL-dialect batch
|
|
126
|
-
(routed to `my`); mysql leg parent=MySQL + author→PG-dialect batch (routed to `pg`). The parent table lives
|
|
127
|
-
ONLY on the primary and the target `users` table ONLY on the secondary, so a mis-route hits a MISSING table
|
|
128
|
-
(loud error) — a green hydrate is unforgeable proof the tag routed the batch to connection B. The reference
|
|
129
|
-
is captured on two separate SQLite DBs via the TS `buildResultSet` two-DB path (byte-true, in-generator).
|
|
130
|
-
2. **Per-runtime routing.** Each runtime's read entry point now takes a connection REGISTRY (name→driver) and
|
|
131
|
-
routes a tagged relation to `registry[op.connection]`, untagged → primary, loud-fail on a missing tag:
|
|
132
|
-
TS `buildResultSet`/`readBundle` (`connections`), Python `read_bundle(…, connections)`, Go
|
|
133
|
-
`ReadBundle(…, connections map[string]SQLDB)`, Rust `read_bundle_pooled(…, connections)`, PHP
|
|
134
|
-
`Relation::readBundle(…, $connections)`. Each `livedb_runner` opens BOTH pools in `main()` already; the
|
|
135
|
-
crossdb leg resets the secondary DB with the vector's `secondarySchema*` and passes `{tag: otherDriver}`.
|
|
136
|
-
|
|
137
|
-
**Result:** `conformance:livedb` green on all 4 non-TS runtimes at 53/53 pg + 53/53 mysql INCLUDING the crossdb
|
|
138
|
-
vector on BOTH legs; TS two-DB reference green (`test/scp/cross-db-relation.test.ts`). Untagged relations are
|
|
139
|
-
byte- and behaviour-unchanged (the field is optional/additive; existing 52 relation+exec+tx vectors still
|
|
140
|
-
green). eager path unchanged.
|
|
141
|
-
|
|
142
|
-
## Net V0 outcome (final)
|
|
143
|
-
|
|
144
|
-
- **Done byte + live (PG+MySQL, all 5 runtimes):** GROUP BY, FOR UPDATE, BETWEEN, LIKE, tuple-IN, dbCast
|
|
145
|
-
(dialect-gated), dbImmediate, IN/NOT-IN subquery, EXISTS/NOT EXISTS, CTE, JOIN, append/HAVING — via the
|
|
146
|
-
additive where-primitives + `SELECT_PORTS` ports (plan §2 補足), every construct's SQL still v1-sourced.
|
|
147
|
-
Golden 139→176 (+37 authored-path byte-asserts, incl. a golden-from-originals negative). livedb 39→52 vectors
|
|
148
|
-
(all 4 language legs 52/52 pg + 52/52 mysql).
|
|
149
|
-
- **R1:** DONE. Per-statement `connection` tag + per-runtime connection-registry routing across ALL 5 runtimes;
|
|
150
|
-
a `kind:'crossdb'` live vector runs the parent on connection A and the tagged relation on connection B — green
|
|
151
|
-
on live PG↔MySQL in py/php/go/rust (53/53 both legs) + the TS two-DB reference. SQL v1-identical; untagged
|
|
152
|
-
relations byte/behaviour-unchanged; eager path unchanged.
|
|
153
|
-
- **R7:** dead `compileFindByPkeys` deleted (v1 `buildFindByPkeys` retained).
|
|
154
|
-
- **R8:** FIND_FILTER proven a makeSQL no-op (variant b) with a 3-dialect byte test — not a waive.
|
|
155
|
-
- **Byte-only-by-nature (not gaps):** ILIKE (v1 `ILIKE` keyword is PG-only; errors on MySQL/SQLite) and
|
|
156
|
-
dbDynamic with a PG-only function (`to_tsvector`) — the AUTHORING primitives are added and byte-proven; a
|
|
157
|
-
portable-function dbDynamic is live-reachable through the same primitive.
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# litedbmodel v2 completeness — 実行計画(全サブエージェントの SSoT)
|
|
2
|
-
|
|
3
|
-
> 唯一の正典。全サブエージェントはこのファイルを渡され従う。個別プロンプトで「雰囲気」の指示を出さない。
|
|
4
|
-
> (このファイルは別エージェント監査でIF誤り・欠落 op・live 盲点を是正済み。)
|
|
5
|
-
|
|
6
|
-
## 0. モデル
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
DSL contract
|
|
10
|
-
→ compile(TS のみ。v1 builder を駆動して最適化 SQL を生成)
|
|
11
|
-
→ 可搬 makeSQL bundle(SQL文字 + params + skip。純JSON。方言別)
|
|
12
|
-
→ bc(全5言語 published)が bundle を汎用実行(runBehavior/runPlan)
|
|
13
|
-
→ 各言語 runtime は bundle を replay するだけ(SQL を生成しない)
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
- **最適化 SQL は TS で1回だけ作る**(v1 builder から verbatim コピー)。
|
|
17
|
-
- **多言語展開は bc の共通処理**。言語ごとに SQL/ロジックを作り直さない。
|
|
18
|
-
- **言語別 runtime = SQL 非依存の薄い plumbing だけ**。必要に応じてのみ。
|
|
19
|
-
|
|
20
|
-
## 1. タスクは足し算。掛け算のチェックは自動試験だけ。
|
|
21
|
-
|
|
22
|
-
- **作業は足し算**: 「op を1つ追加」= TS で SQL を1回作る(v1 コピー)+ bundle/catalog に載せる = 1タスク。方言・言語の掛け算タスクは存在しない。
|
|
23
|
-
- **掛け算(op × 方言 × 言語)は自動試験が回す**:
|
|
24
|
-
- `test/scp/makesql-golden.test.ts` — compile 出力 SQL を **v1 builder の直接出力に byte 一致** assert(`DBConditions.compile`/`LazyRelationContext`/`*SqlBuilder`/実 `DBModel._insert` capture を直接呼ぶ)。SQL 正しさの正典ゲート。
|
|
25
|
-
- `conformance/vectors/*.json` + drift gate。
|
|
26
|
-
- `conformance/vectors-livedb/livedb.json` — 実 PG+MySQL+SQLite 実行で結果一致(`::text[]` 級を捕まえる)。
|
|
27
|
-
- cross-lang agreement — 全5言語が同一 bundle を同一 replay。
|
|
28
|
-
- **各 op は「byte-match 試験(全方言)」と「live 実行(PG+MySQL)」の両方**を満たして初めて done。byte だけ/PG だけ/SQLite mock だけ は不可(不変条件#3)。
|
|
29
|
-
|
|
30
|
-
## 2. モジュール IF(固定。逸脱禁止。拡張は「ケース追加」であって署名変更ではない)
|
|
31
|
-
|
|
32
|
-
- `src/scp/makesql/compile-crud.ts`: `builderFor` / `compileInsert` / `compileInsertMany` / `compileUpdateSingle` / `compileUpdateMany` / `compileDelete` / `compileDeleteMany` / `compileFindByPkeys`(+ `*BuildOptions`)。`(dialect, options) → MakeSQL|MakeSQL[]`、SQL は v1 builder 由来。
|
|
33
|
-
- `src/scp/makesql/compile-relation.ts`: `compileSingleKeyUnlimited` / `compileSingleKeyLimited` / `compileCompositeKeyStaticUnlimited` / `compileCompositeKeyUnlimited` / `compileCompositeKeyLimited` / `inferPgArrayType` / `resolvePgArrayCast` / `PG_ARRAY_CAST_TOKEN` / `RelationCompileBase`。
|
|
34
|
-
- `src/scp/relation.ts`: `RelationOp` / `RelationDecl` / `compileRelationOp`(compile 入口)/ `runRelationOp` / `distributeToParent`(single+composite 対応済)。
|
|
35
|
-
- `src/scp/makesql/compile-select.ts`: `compileSelect` / `SelectDesc`(head/GROUP BY/ORDER BY/LIMIT/OFFSET/FOR UPDATE/join/cte/append/HAVING を担う)。
|
|
36
|
-
- `src/scp/makesql/tx.ts`: `TxOp`/`TxStatement`/`TransactionPlan`/`GateRule`/`StatementRole`/`compileWriteNode`/`deriveTransactionPlan`/`deriveBatchPlan`/`executeTransaction`/`renderTxStatement`/`mysqlPkHint`/`stripMysqlPkHint`/`literalize`。
|
|
37
|
-
- `src/scp/catalog.ts`: `CatalogName = Select | Count | Insert | Update | Delete | Fragment | Tx`、`LITEDBMODEL_CATALOG`。(**relations は catalog entry でなく `compileRelationOp`。makeSQL は bundle 成果物で catalog 名ではない。**)
|
|
38
|
-
- `src/scp/makesql/static-bundle.ts`: `compileStaticBundle` / `compileReadGraph` / `compileSelectNode` / `executeReadGraph` / `executeReadGraphAsync` / `executeStaticBundle` / `executeStaticWrite` / `renderReadPrimary`。
|
|
39
|
-
- **bundle JSON shape**: `{ readGraph, statementsById, relations, transaction, optionalHeads }`(方言別)。新「kind」を勝手に増やさない(増やすならエスカレーション)。
|
|
40
|
-
- **runtime handler seam**(全言語): bundle replay のみ。SQL 生成禁止。言語差は param bind のみ。
|
|
41
|
-
|
|
42
|
-
### §2 補足(監査 + V0 で判明・確定)
|
|
43
|
-
compile 入口は2つ: **byte 経路**(`compileWhere/compileSelect`、多構文が v1 一致検証可)と **live 経路**(`L.Select→compileBundle→compileSelectNode`、replay 可能 bundle を生む唯一路。WHERE=bc closed-set、SELECT=`SELECT_PORTS`)。**構文を live 到達可能にするには authoring 面を拡張する** — これは「足し算」として**承認済み**(署名を壊さず口/primitive を追加する。逸脱ではない):
|
|
44
|
-
- **where-primitive 追加**(`authoring-sql.ts`): `whereExists`/`whereInSubquery`(+NOT)/`whereBetween`/`whereLike`(ILIKE)/`whereTupleIn`/raw-cast 等。subquery は「param slot の入れ子 makeSQL/Fragment」として符号化(`src/scp/index.ts` header の想定通り)。
|
|
45
|
-
- **`SELECT_PORTS` 口追加**(`catalog.ts` + `compileSelectNode` 配線): `forUpdate`/`cte`/`join`/`joinParams`/`append`(`SelectDesc` に既にフィールドあり、SQL は v1 `_buildSelectSQL` 由来)。
|
|
46
|
-
- **bundle shape 追加**(R1): relation statement に per-statement の dialect/connection タグ。
|
|
47
|
-
|
|
48
|
-
## 3. 不変条件(非交渉)
|
|
49
|
-
|
|
50
|
-
1. SQL は v1 原本 verbatim コピー(golden-from-originals)。手書き・再発明禁止。
|
|
51
|
-
2. bc プリミティブで表現。できない構造は hand-roll せず**エスカレーション**。
|
|
52
|
-
3. **live-DB ゲート必須(実 PG+MySQL+SQLite)。byte 一致だけ・PG だけ・SQLite mock だけ は不可。**
|
|
53
|
-
4. 検証は bundle レベル(§1 試験群)。言語ごとに SQL 再実装・再検証しない。
|
|
54
|
-
5. 中途半端禁止(byte 全方言 + live 実行で初めて done)。
|
|
55
|
-
6. eager 経路無改変。
|
|
56
|
-
7. ゴミなし。
|
|
57
|
-
|
|
58
|
-
## 4. 作業項目(足し算リスト)と状態
|
|
59
|
-
|
|
60
|
-
### DONE(commit 済・現行スイート全 green: golden 135/135・conformance 5×36/36・livedb 76/76・ScpDialect 35/35)
|
|
61
|
-
- Write: createMany/updateMany(golden byte-assert 済); deleteMany(**live-assert のみ**=ScpDialect+livedb corpus。golden byte-assert は V0 で追加); tx variants(idempotency/unique/edge/composite-DAG); MySQL RETURNING 実 PK 基準。
|
|
62
|
-
- Read: `count()`(golden §B COUNT); composite-key relation(golden §C); PG 配列全型 live; hand-roll 除去(golden §B); relations belongsTo/hasOne/hasMany/hasMany-limit(golden §C, live)。
|
|
63
|
-
|
|
64
|
-
### REMAINING(足し算。各 = TS で v1 SQL 1回 + byte-assert 追加 + live vector 追加)
|
|
65
|
-
- **V0. coverage matrix(最初)**: 全 op × 全方言について `{byte-match?, live-exec on PG+MySQL?}` の網羅マトリクスを作り、欠けを列挙。これが以降の確定スコープ。byte だけ/PG だけ/SQLite だけ を全て gap 認定。
|
|
66
|
-
- **R1. cross-DB relations**: v1 は relation を target の driver/接続で実行(`LazyRelation.ts:236`)。**bundle-shape を先に確定**: 「per-statement に dialect/connection タグを付す」方式を採る(bundle 分割でなく relation statement に接続タグ)。SQL は TS で1回 v1 由来、言語側は接続ルーティング(SQL 非依存 plumbing)。
|
|
67
|
-
- **R2. subquery / EXISTS / NOT EXISTS / NOT IN(subquery) / composite IN(subquery)**: 現状 PG byte のみ・live ゼロ。MySQL+SQLite の byte-assert 追加 + PG+MySQL live 実行。
|
|
68
|
-
- **R3. GROUP BY / FOR UPDATE / BETWEEN / ILIKE / tuple-IN / dbCast / dbDynamic / dbImmediate**: render byte はあるが live 未実行。PG+MySQL live 実行を追加。
|
|
69
|
-
- **R4. CTE / query-based model reads**(`withQuery`/`cte` port, `_buildSelectSQL` WITH ラップ): byte+live 追加。
|
|
70
|
-
- **R5. JOIN(`join`/`joinParams`)**: byte+live 追加。
|
|
71
|
-
- **R6. append / HAVING**: byte+live 追加。
|
|
72
|
-
- **R7.(決定済)** `compileFindByPkeys` は dead SCP export → **削除**(+ 2 re-export)。single-key は `whereIn`+`inColumn` IN-list、composite は composite relation batch で live 済=機能重複。v1 `buildFindByPkeys` は `DBModel.findById` が使うので残す。
|
|
73
|
-
- **R8. FIND_FILTER(モデル毎グローバル soft filter, `DBModel.ts:597/859`)**: **silent waive 禁止**(filtered モデルで soft-delete/tenant scope が漏れる correctness 影響)。v1 の適用点を特定し、(a) SCP compile 経路で auto-適用して v1 再現、または (b) SCP contract の上流で既に WHERE に畳まれている(=makeSQL では no-op)ことを**証拠付きで**確定。どちらかを実証してから done。
|
|
74
|
-
- **V1. comprehensive 監査**(別エージェント): 全 op が byte(全方言)+ live(PG+MySQL)green、golden-from-originals negative test、hand-roll 残存ゼロ、cross-lang 一致、ゴミなし。
|
|
75
|
-
- **D1. bc 依存 → 最新 0.2.3**(0.2.2 でない): TS `^0.2.0`→`^0.2.3`、go/rust/python `0.2.0`→`0.2.3`、PHP は vendored 再取り込み。conformance green 維持。最終ベンチ前必須。
|
|
76
|
-
- **REL. release**: `fix43-sql-parity` → main PR/merge、**2.0.0 → 2.0.1**、npm/PyPI/crates/go publish(承認済・都度確認不要)。
|
|
77
|
-
- **BENCH(#44)**: cross-lang 実行サーフェス(sql/codegen/ir/dynamic/prepared)+ v1 比較(TS 1.2.10・可能なら旧 .rs)。graphddb#307 同型。完走。
|
|
78
|
-
|
|
79
|
-
## 5. 実行順序
|
|
80
|
-
|
|
81
|
-
1. **V0**: coverage matrix 作成(byte × live × 全方言 × 全 op)。確定スコープを出す。
|
|
82
|
-
2. **R1–R8**: matrix の欠けを足し算で埋める(各 TS 1回 + byte-assert + live vector)。
|
|
83
|
-
3. **V1**: comprehensive 監査(FAIL→修正→再監査)。
|
|
84
|
-
4. **D1**: bc 0.2.3。
|
|
85
|
-
5. **REL**: 2.0.1。
|
|
86
|
-
6. **BENCH**: #44 完走。
|
|
87
|
-
|
|
88
|
-
## 6. サブエージェント運用
|
|
89
|
-
|
|
90
|
-
各エージェントに本ファイルを渡す。担当は「§4 の項目 X を足す」だけ指定。**§2 の IF に適合・§1 の試験に byte-assert と live vector を足す・bundle レベル検証・hand-roll せずエスカレーション・green 増分 commit・ゴミなし。**
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# v2 SQL パリティ チェックリスト(epic #43 — PostgreSQL アンカー)
|
|
2
|
-
|
|
3
|
-
> 目的: v2 SCP の生成 SQL を、旧 `litedbmodel`(.ts v1) と旧 `litedbmodel.rs` の **tuned SQL** と一致させる。v2 の conformance を縮小基準(.ts v1 SqlBuilder の単一行 CRUD / SQLite-first 形)にアンカーしたため、このライブラリの核心である tuned SQL を取りこぼし、**旧 .ts・.rs の両方より SQL が退行**した(超デグレ)。本書はその全差分の作業チェックリスト。
|
|
4
|
-
|
|
5
|
-
## 原則
|
|
6
|
-
- **アンカー = PostgreSQL。** PG の最も tuned な SQL を正とし、MySQL/SQLite は PG 意味論に各 DB の能力範囲で導出する(PG を lowest-common-denominator に落とさない)。
|
|
7
|
-
- **conformance golden を旧 tuned 実装の PG SQL に対して作り直す**(縮小基準は破棄)。
|
|
8
|
-
- **原実装のバグは踏襲しない**(後述の「原実装バグ」参照)。3実装で挙動が割れる箇所は PG アンカーを選ぶ。
|
|
9
|
-
- 凡例 `差分`: ✅同一 / ⚠️差あり / ❌v2欠落。`優先度`: 高(correctness) / 中(perf/parity) / 低(cosmetic/edge)。
|
|
10
|
-
|
|
11
|
-
---
|
|
12
|
-
|
|
13
|
-
## 0. 構造的所見(最重要 — 単なる runtime 修正でなく §8 IR の破壊的変更が要る)
|
|
14
|
-
|
|
15
|
-
1. **§8 可搬 bundle が batch write を表現できない。** `compileInsertFor`/UPDATE 経路は単一行のみ。batch INSERT(UNNEST)/UPDATE(UNNEST/JOIN-VALUES/CASE-WHEN)/SKIP-column が bundle に無い → **Python/Go/Rust/PHP の thin runtime は batch write を実行不可**。多言語の約束の穴。
|
|
16
|
-
2. **§8 IR が複合キー relation を表現できない。** `RelationOp` が `parentKey`/`targetKey` を単一 `string` でハードコード(`src/scp/relation.ts:64-69,90-92`)。複合 FK の relation が構造的に不可能。
|
|
17
|
-
3. **relation IN に dialect 別 fragment 種が無い。** `= ANY(?::type[])` / UNNEST / `::type` cast / `CROSS JOIN LATERAL` を表す fragment kind が無く、`Dialect` strategy(`dialect.ts`) は INSERT-conflict/placeholder/NULLS しか扱わない(relation に未関与)。
|
|
18
|
-
4. **WHERE 語彙が閉集合に縮小。** v2 は eq/ne/lt/le/gt/ge/isNull/in/and/or/cond のみ(`bridge.ts:208` が他を loud-reject)。subquery/EXISTS/cast/LIKE/raw/BETWEEN 等が全欠落。
|
|
19
|
-
5. **hardLimit / relation where-filter / FOR UPDATE / HAVING / APPEND** が §8/runtime から欠落。
|
|
20
|
-
|
|
21
|
-
→ #43 は「IR 契約の作り直し(relation 複合キー・dialect relation fragment・batch write・条件語彙拡張)+ 全言語 render + conformance 再構築」を含む。**リリース済み 2.0.0 はこの点で超デグレ。**
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## 1. 原実装バグ(PG アンカー統一時に *踏襲しない*)
|
|
26
|
-
- **.rs cast が dialect 非対応**: `condition.rs:97-116` が `?::type` をハードコードし SQLite/MySQL にも `::uuid` を漏らす。v1 は `SqlCastFormatter` で dialect gate。→ **v1 の dialect-aware を採用**。
|
|
27
|
-
- **.rs `Immediate`(`NOW()`) が条件で inline されず文字列を param bind**(`condition.rs` catch-all、test :613-622)。v1 は inline。→ **v1 の inline を採用**。
|
|
28
|
-
- **.rs 空 `IntArray/TextArray/FloatArray/BoolArray` → 不正な `IN ()`**(空ガードが `ValueArray` のみ)。v1/v2 は `1 = 0`。→ **`1 = 0` を採用**。
|
|
29
|
-
- **.rs per-parent-limit が PG でも `ROW_NUMBER()`**(LATERAL でない)。v1 は `CROSS JOIN LATERAL`。→ **v1 LATERAL を PG アンカーに採用**。
|
|
30
|
-
- **.rs PG 型推論が粗い**(uuid/timestamp/date/json を text に畳む、`model.rs:730`)。v1 は sqlCast 駆動で精緻。→ **v1 の sqlCast 駆動を採用**。
|
|
31
|
-
|
|
32
|
-
## 2. 3実装割れ(PG アンカーで確定すべき判断)
|
|
33
|
-
- **OR / 空OR / 単一要素の括弧規則**が v1/`.rs`/v2 で不一致(.rs は top-level OR を `(…)` 包む・単一要素 unwrap、v2 は top-level OR を包まず nested 単一 fragment を `(x)` 包む、v1 `__or__` は二重包み)。空OR は .rs=`1=0` / v1・v2=空文字(AND 合流時に TRUE 相当=意味が違う)。→ **PG アンカー形を1つ決めて pin**。
|
|
34
|
-
- **boolean を WHERE で literal(`= TRUE`) か bound(`= ?`) か**(v1=literal / .rs・v2=bound)。→ 要決定(tuned PG は literal `TRUE`)。
|
|
35
|
-
- **LIMIT/OFFSET を parameterize(`LIMIT ?`) か inline(`LIMIT 10`) か**(v2=param / v1・.rs=inline)。→ **byte-golden が v1 と不一致になる**ため要決定。
|
|
36
|
-
|
|
37
|
-
---
|
|
38
|
-
|
|
39
|
-
## A. WHERE / 条件 / 値
|
|
40
|
-
|
|
41
|
-
| 構文 | dialect | .ts v1 | .rs | v2現状 | PGアンカー目標 | 差分 | 優先度 | ☐ |
|
|
42
|
-
|---|---|---|---|---|---|---|---|---|
|
|
43
|
-
| equality `col = ?` | all | `col = ?`/`$1` | `col = ?` | `col = ?` | `col = $1` | ✅同一 | — | |
|
|
44
|
-
| `!= / <>` | PG | custom-op key のみ | `Raw` のみ | `col <> ?`(first-class) | `col <> $1` | ⚠️差あり | 中 | [ ] |
|
|
45
|
-
| `< <= > >=` | PG | custom-op key のみ | `Raw` のみ | first-class `cmp` | `col > $1` | ⚠️差あり(出力は一致) | 中 | [ ] |
|
|
46
|
-
| IN-list `col IN (?,?)` | all | array→IN | array→IN | `IN (?)`→展開 | `col IN ($1,$2)` | ✅同一 | — | |
|
|
47
|
-
| empty-IN | all | `1 = 0` no param | ⚠️IntArray等は `IN ()`(不正) / ValueArrayのみ`1=0` | `1 = 0` | `1 = 0` | ⚠️.rsバグ | 高 | [ ] |
|
|
48
|
-
| NOT IN (list) | PG | custom-op のみ | 無し | ❌欠落 | `col NOT IN ($1,$2)` | ❌ | 中 | [ ] |
|
|
49
|
-
| IS NULL | all | `col IS NULL` | `col IS NULL` | `isNull` | `col IS NULL` | ✅同一 | — | |
|
|
50
|
-
| **IS NOT NULL** | all | `dbNotNull` | `Value::NotNull` | ❌欠落(isNullのみ) | `col IS NOT NULL` | ❌ | 高 | [ ] |
|
|
51
|
-
| boolean | PG | `= TRUE`literal | `= ?`bound | `= ?`bound | 要決定(PG tuned=`= TRUE`) | ⚠️差あり | 中 | [ ] |
|
|
52
|
-
| LIKE/NOT LIKE | PG | custom-op/raw | `Raw` | ❌欠落 | `col LIKE $1` | ❌ | 中 | [ ] |
|
|
53
|
-
| ILIKE | PG | custom-op/raw | `Raw` | ❌欠落 | `col ILIKE $1`(MySQL/SQLiteは`LIKE`・blindに落とさない) | ❌ | 中 | [ ] |
|
|
54
|
-
| BETWEEN | PG | custom-op | `Raw` | ❌欠落 | `col BETWEEN $1 AND $2` | ❌ | 中 | [ ] |
|
|
55
|
-
| **IN(subquery)** | PG | `DBSubquery` | `InSubquery` | ❌欠落 | `col IN (SELECT …)`(複合`(a,b) IN (…)`) | ❌ | 高 | [ ] |
|
|
56
|
-
| **NOT IN(subquery)** | PG | `DBSubquery` | `NotInSubquery` | ❌欠落 | `col NOT IN (SELECT …)` | ❌ | 高 | [ ] |
|
|
57
|
-
| **EXISTS** | PG | `DBExists` | `Exists` | ❌欠落 | `EXISTS (SELECT 1 …)` | ❌ | 高 | [ ] |
|
|
58
|
-
| **NOT EXISTS** | PG | `DBExists` | `NotExists` | ❌欠落 | `NOT EXISTS (…)` | ❌ | 高 | [ ] |
|
|
59
|
-
| 相関 parentRef | PG | `DBParentRef`→`t.col`inline | `parent_ref`inline | ❌欠落 | inlined `outer.col` | ❌ | 高 | [ ] |
|
|
60
|
-
| **raw / `__raw__`** | PG | verbatim/tuple | `Condition::Raw` | ❌欠落(WHERE未配線) | raw predicate + params(§13) | ❌ | 高 | [ ] |
|
|
61
|
-
| custom-op (key-with-?) | PG | verbatim | 無し(Raw) | ❌欠落 | author operator | ❌ | 中 | [ ] |
|
|
62
|
-
| AND grouping | all | `join(' AND ')`,nested`(…)` | 同 | tree AND,nested`(inner)` | `a=$1 AND b=$2` | ✅同一 | — | |
|
|
63
|
-
| **OR / 括弧** | PG | `__or__`二重包み | top-OR`(…)`,単一unwrap | top-OR包まず/nested単一`(x)` | 要確定(§2) | ⚠️差あり | 高 | [ ] |
|
|
64
|
-
| **空AND** | all | `''`(節ドロップ) | `1=1` | `''`(節ドロップ) | 節ドロップ | ⚠️.rs差 | 中 | [ ] |
|
|
65
|
-
| **空OR** | all | `''` | `1=0` | `''` | 要確定(意味差) | ⚠️差あり | 高 | [ ] |
|
|
66
|
-
| SKIP/optional | all | 暗黙(key無) | caller filter | `when`/`skipWhen`明示 | 不在fragment→SQL/param無 | ✅同一 | — | |
|
|
67
|
-
| SKIP on GROUP | all | 暗黙可 | 可 | ❌loud-reject(`compile-sqlite.ts:103`) | group条件droppable | ⚠️差あり | 中 | [ ] |
|
|
68
|
-
| **cast `::type`単体** | PG | `dbCast`→`?::uuid`(formatter, dialect-gate) | ⚠️`?::uuid`hardcode(dialect非対応) | ❌欠落 | `$1::uuid`(MySQL/SQLite=`?`) | ❌+.rsバグ | 高 | [ ] |
|
|
69
|
-
| **cast array `IN(?::t,…)`** | PG | `dbCastIn`(formatter) | ⚠️hardcode | ❌欠落 | `IN ($1::uuid,…)`(他=`IN(?,…)`) | ❌ | 高 | [ ] |
|
|
70
|
-
| cast array empty | PG | `1 = 0` | `1 = 0` | ❌欠落 | `1 = 0` | ❌ | 中 | [ ] |
|
|
71
|
-
| dynamic `col = fn(?)` | PG | `DBDynamicValue` | `Value::Dynamic` | ❌欠落 | `col = fn($1)` | ❌ | 中 | [ ] |
|
|
72
|
-
| **immediate `col = NOW()`** | PG | inline no-param | ⚠️`?`にstring bind(バグ) | ❌欠落 | `col = NOW()`inline | ❌+.rsバグ | 高 | [ ] |
|
|
73
|
-
| **tuple/composite IN** | PG | `dbTupleIn`→`(a,b) IN ((?,?),…)` | `db_tuple_in_pg`→UNNEST | ❌欠落 | `(a,b) IN (SELECT * FROM UNNEST($1::t[],…))`(他=value-list) | ❌ | 高 | [ ] |
|
|
74
|
-
| `= ANY($1::t[])`(条件) | PG | PK-helperのみ | UNNEST-helperのみ | ❌欠落 | `col = ANY($1::t[])`(他=IN展開) | ❌ | 中 | [ ] |
|
|
75
|
-
| value: date/json | PG | driver formatter/`::jsonb` | typed(chrono/serde) | ⚠️bc Value untyped | typed bind | ⚠️差あり | 中 | [ ] |
|
|
76
|
-
|
|
77
|
-
## B. INSERT / UPDATE / DELETE / placeholder / tail
|
|
78
|
-
|
|
79
|
-
| 構文 | dialect | .ts v1 | .rs | v2現状 | PGアンカー目標 | 差分 | 優先度 | ☐ |
|
|
80
|
-
|---|---|---|---|---|---|---|---|---|
|
|
81
|
-
| single INSERT | PG | `VALUES(?::cast…)` | `VALUES($1…)`(cast無) | `VALUES(?…)`(cast無) | 単一行+per-col`?::sqlCast`保持 | ⚠️差あり | 中 | [ ] |
|
|
82
|
-
| column order | all | caller順(明示list) | caller順 | JS object挿入順(`compile-dialect.ts:46`) | caller宣言順 | ⚠️差あり | 中 | [ ] |
|
|
83
|
-
| **batch INSERT** | PG | `UNNEST($i::type[])`+型推論(array/json/DBToken) | `UNNEST`(型粗い) | ❌欠落(単一行のみ) | PG UNNEST batch(型推論)+SQLite/MySQL=multi VALUES | ❌(bundle欠落→多言語batch不可) | 高 | [ ] |
|
|
84
|
-
| ON CONFLICT DO NOTHING/UPDATE | all | `EXCLUDED`/`excluded`/`VALUES(c)` | 同 | 同(`dialect.ts`) | 各方言verb | ✅同一 | — | |
|
|
85
|
-
| empty DO UPDATE cols→all | all | (暗黙) | 明示fallback(`model.rs:524`) | ❌fallback無→`DO UPDATE SET `破損 | empty→全列 | ⚠️差あり | 中 | [ ] |
|
|
86
|
-
| RETURNING bare | PG | `RETURNING id` | 同 | `RETURNING cols` | bare | ✅同一 | — | |
|
|
87
|
-
| RETURNING `table.col`(SQLite/batch) | SQLite/PG | `table.col`/`t.col` | 同 | ❌bare のみ | SQLite=`table.col`/PG batch=`t.col`alias | ⚠️差あり | 中 | [ ] |
|
|
88
|
-
| RETURNING MySQL simulate | MySQL | append+driver sim | 同 | ❌無条件`RETURNING`(sim戦略無) | append+sim | ⚠️差あり | 中 | [ ] |
|
|
89
|
-
| single UPDATE | PG | `SET c=?::cast` | `SET c=?` | `SET c=?`(cast無) | per-col`?::sqlCast`保持 | ⚠️差あり | 中 | [ ] |
|
|
90
|
-
| **batch UPDATE** | PG | `SET c=v.c FROM UNNEST(?::t[],…) AS v(cols) WHERE t.k=v.k` | 同(型粗い) | ❌欠落 | PG UNNEST batch+MySQL JOIN-VALUES+SQLite CASE-WHEN | ❌(bundle欠落) | 高 | [ ] |
|
|
91
|
-
| **SKIP-column batch** | PG | `CASE WHEN v._skip_c…`+`?::bool[]` | 同 | ❌欠落 | PG CASE/MySQL IF/SQLite WHEN | ❌ | 高 | [ ] |
|
|
92
|
-
| batch RETURNING alias | PG | `RETURNING t.col` | 同 | ❌欠落 | `RETURNING t.col` | ❌ | 中 | [ ] |
|
|
93
|
-
| single DELETE | all | WHERE必須(throw) | WHERE任意(全消し可) | WHERE必須(throw) | WHERE必須(v1=PGアンカー) | ⚠️.rs外れ値 | 低 | [ ] |
|
|
94
|
-
| IN-list DELETE / empty | all | `IN(?,…)`,空`1=0` | 同 | 同 | 同 | ✅同一 | — | |
|
|
95
|
-
| `?`→`$N` | PG | naive replace | quote-aware(literalスキップ) | naive(`dialect.ts:129`) | quote-aware(#42) | ⚠️差あり(.rsが安全) | 中 | [ ] |
|
|
96
|
-
| **LIMIT/OFFSET** | all | inline literal `LIMIT 10` | inline | **param `LIMIT ?`**(`compile-sqlite.ts:158`) | 要確定(v1 goldenと text不一致) | ⚠️差あり | 高 | [ ] |
|
|
97
|
-
| NULLS FIRST/LAST | PG | 無(raw order) | 無 | `orderByNulls`定義済だが**未呼出(dormant)** | PG native/MySQL emulation | ⚠️差あり | 低 | [ ] |
|
|
98
|
-
| **FOR UPDATE** | all | `FOR UPDATE` | `FOR UPDATE` | ❌欠落 | `FOR UPDATE` | ❌ | 中 | [ ] |
|
|
99
|
-
| FOR SHARE | PG | 無 | `FOR SHARE`(.rsのみ) | ❌欠落 | `FOR SHARE` | ❌(.rsのみ) | 低 | [ ] |
|
|
100
|
-
| GROUP BY | all | 有 | 有 | 有 | 有 | ✅同一 | — | |
|
|
101
|
-
| HAVING | all | 無(v1core) | `HAVING`(.rsのみ) | ❌欠落 | `HAVING` | ⚠️差あり | 低 | [ ] |
|
|
102
|
-
| APPEND raw tail | all | 有 | 有 | ❌欠落 | raw append | ⚠️差あり | 低 | [ ] |
|
|
103
|
-
| identifier quoting | all | 無 | 無 | 無 | 無 | ✅同一 | — | |
|
|
104
|
-
|
|
105
|
-
## C. Relation batch-load / 型 binding
|
|
106
|
-
|
|
107
|
-
| 構文 | dialect | .ts v1 | .rs | v2現状 | PGアンカー目標 | 差分 | 優先度 | ☐ |
|
|
108
|
-
|---|---|---|---|---|---|---|---|---|
|
|
109
|
-
| single-key batch | PG | `= ANY(?::type[])`(1配列param) | `= ANY(?::t[])` | `IN (?,?,…)`(N param,cast無) | `= ANY($1::type[])` | ❌欠落 | 高 | [ ] |
|
|
110
|
-
| single-key batch | MySQL/SQLite | `IN (?,…)` | `IN (?,…)` | `IN (?,…)` | `IN (?,…)` | ✅同一 | — | |
|
|
111
|
-
| PG配列型推論 | PG | sqlCast駆動(uuid/ts/date/json) | ⚠️text畳み(粗) | 無(cast無) | sqlCast駆動 | ❌+.rsバグ | 高 | [ ] |
|
|
112
|
-
| per-parent-limit | PG | `CROSS JOIN LATERAL(…LIMIT n)` | ⚠️`ROW_NUMBER()`(LATERAL非) | `ROW_NUMBER()` | `CROSS JOIN LATERAL`(v1アンカー) | ❌+.rs退行 | 高 | [ ] |
|
|
113
|
-
| per-parent-limit | MySQL/SQLite | `ROW_NUMBER()` | 同 | 同 | `ROW_NUMBER()` | ✅同一 | — | |
|
|
114
|
-
| **複合キー(無limit)** | PG | `JOIN unnest(?::t[],…)`/`(cols) IN (SELECT * FROM UNNEST)` | `(cols) IN (SELECT * FROM UNNEST)` | ❌**表現不可**(単一key IR) | UNNEST tuple-IN | ❌(IR破壊的変更要) | 高 | [ ] |
|
|
115
|
-
| 複合キー(無limit) | MySQL/SQLite | `(a,b) IN ((?,?),…)` | 同 | ❌ | tuple-IN | ❌ | 高 | [ ] |
|
|
116
|
-
| 複合キー+limit | PG | LATERAL composite | ⚠️ROW_NUMBER composite | ❌ | LATERAL composite | ❌ | 高 | [ ] |
|
|
117
|
-
| 複合キー+limit | MySQL/SQLite | ROW_NUMBER composite | 同 | ❌ | ROW_NUMBER composite | ❌ | 高 | [ ] |
|
|
118
|
-
| 複合キー dedup | all | NUL-join tuple | 同 | ❌(複合経路無) | tuple dedup | ❌ | 中 | [ ] |
|
|
119
|
-
| 単一key dedup | all | Set/JSON | caller upstream | `dedupeKeys`Set | dedup | ✅同一 | — | |
|
|
120
|
-
| empty key set | all | query無 | query無 | query無(assert用`1=0`) | query無 | ✅同一 | — | |
|
|
121
|
-
| group by target key | hasMany | Map/NUL複合 | 同 | 単一keyのみ | 複合対応 | ⚠️差あり | 中 | [ ] |
|
|
122
|
-
| **hardLimit強制** | hasMany | 有(`DBModel.ts:1703`) | 有(`LIMIT n+1`+count) | ❌欠落 | hardLimit probe+throw | ❌ | 中 | [ ] |
|
|
123
|
-
| relation `where`filter | all | `config.conditions`merge | ⚠️decl有るが未配線 | ❌field無 | batch SELECTにmerge | ❌ | 中 | [ ] |
|
|
124
|
-
| ORDER BY(unlimited) | all | append | append | append | append | ✅同一 | — | |
|
|
125
|
-
| typed bind bool | PG | typed array | typed | plain`?` | typed | ⚠️差あり | 中 | [ ] |
|
|
126
|
-
| typed bind uuid | PG | `?::uuid`/`uuid[]` | `PgParam::Uuid` | text`?` | `?::uuid`/`=ANY(?::uuid[])` | ❌ | 高 | [ ] |
|
|
127
|
-
| typed bind timestamp/date | PG | typed | typed(chrono) | ISO string | typed | ⚠️差あり | 中 | [ ] |
|
|
128
|
-
| typed bind json | PG | `::jsonb` | `PgParam::Json` | 無 | `::jsonb` | ❌(PG) | 低 | [ ] |
|
|
129
|
-
| array param `::type[]` | PG | `= ANY`/unnest核 | `?::type[]` | 無(IN展開) | 1配列param`::type[]` | ❌ | 高 | [ ] |
|
|
130
|
-
| typed-IN helper `db_cast_in`/`db_uuid_in` | PG | (sqlCast) | `CastArray`→`?::type` | 無 | typed-cast IN | ❌ | 中 | [ ] |
|
|
131
|
-
| composite-IN helper `db_tuple_in_pg`(UNNEST) | PG | inline | `values.rs:439` | 無 | UNNEST composite-IN | ❌ | 高 | [ ] |
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
## 3. 高優先度 マスターロールアップ(実装の起点)
|
|
136
|
-
|
|
137
|
-
**IR 破壊的変更を伴う(§8 bundle + Dialect strategy + 全言語 render + conformance 再構築):**
|
|
138
|
-
1. 複合キー relation(`RelationOp` を複数キー化 + PG UNNEST tuple-IN / 他 tuple-IN)— C
|
|
139
|
-
2. PG 単一キー relation `= ANY(?::type[])`(dialect 別 relation-IN fragment + 配列 param slot)— C
|
|
140
|
-
3. PG per-parent-limit `CROSS JOIN LATERAL`(v1 アンカー、.rs の ROW_NUMBER は非採用)— C
|
|
141
|
-
4. batch INSERT(PG UNNEST+型推論 / SQLite・MySQL multi-VALUES)を bundle で表現 — B
|
|
142
|
-
5. batch UPDATE(PG UNNEST / MySQL JOIN-VALUES / SQLite CASE-WHEN)+ SKIP-column を bundle で表現 — B
|
|
143
|
-
6. WHERE 条件語彙拡張: subquery(IN/NOT IN/EXISTS/NOT EXISTS/相関)、IS NOT NULL、cast(`::uuid`/array/empty)、raw/§13 escape、tuple/composite IN、immediate inline — A
|
|
144
|
-
7. typed binding(uuid/timestamp/date/json/array/bool)を PG で型付き — A/C
|
|
145
|
-
|
|
146
|
-
**text-breaking(v1 golden と不一致・要確定):**
|
|
147
|
-
8. LIMIT/OFFSET を inline(v1) か param(v2) か — B
|
|
148
|
-
9. OR/空OR/単一要素の括弧規則、boolean literal vs bound を PG アンカーで pin — A
|
|
149
|
-
|
|
150
|
-
**中: per-col `?::sqlCast`、RETURNING `table.col`/MySQL sim、empty-DO-UPDATE→all fallback、`?`→`$N` quote-aware(#42)、FOR UPDATE、relation where-filter、hardLimit、NOT IN(list)/LIKE/BETWEEN/dynamic/custom-op、grouping 複合対応。**
|
|
151
|
-
|
|
152
|
-
**原実装バグは踏襲しない**(§1)。**3実装割れは PG アンカーで確定**(§2)。
|
|
153
|
-
|
|
154
|
-
---
|
|
155
|
-
|
|
156
|
-
## 進め方
|
|
157
|
-
1. 本チェックリストの各 ❌/⚠️ を issue のサブタスクに(#43 配下)。IR 破壊的変更(1-5)を先に設計確定。
|
|
158
|
-
2. **conformance golden を旧 tuned 実装(.ts LazyRelation/SqlBuilder + .rs、PG SQL)に対して再生成**(縮小基準を捨てる)。
|
|
159
|
-
3. PG から実装→MySQL/SQLite 導出→5言語 render→byte 一致・実 DB(docker) 検証。
|
|
160
|
-
4. #44 の 1.x 最終版 vs v2 ベンチで定量回帰確認。
|
|
161
|
-
|
|
162
|
-
*出所: 旧 `litedbmodel.rs/litedbmodel/src/{condition,values,model,relation,handler,driver/*}.rs`、旧 `litedbmodel/src/{DBConditions,DBValues,DBModel,LazyRelation,drivers/*}.ts`、v2 `litedbmodel/src/scp/{render,dialect,compile-sqlite,compile-dialect,bridge,relation,write-plan,runtime,ir}.ts`。file:line は各 issue に詳細。*
|