litedbmodel 2.0.2 → 2.2.0

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 (193) hide show
  1. package/README.md +7 -0
  2. package/dist/DBConditions.d.ts.map +1 -1
  3. package/dist/DBConditions.js +16 -1
  4. package/dist/DBConditions.js.map +1 -1
  5. package/dist/DBHandler.d.ts.map +1 -1
  6. package/dist/DBHandler.js +6 -0
  7. package/dist/DBHandler.js.map +1 -1
  8. package/dist/DBModel.d.ts +84 -2
  9. package/dist/DBModel.d.ts.map +1 -1
  10. package/dist/DBModel.js +312 -29
  11. package/dist/DBModel.js.map +1 -1
  12. package/dist/decorators.d.ts +52 -0
  13. package/dist/decorators.d.ts.map +1 -1
  14. package/dist/decorators.js +127 -24
  15. package/dist/decorators.js.map +1 -1
  16. package/dist/drivers/mysql.d.ts.map +1 -1
  17. package/dist/drivers/mysql.js +12 -2
  18. package/dist/drivers/mysql.js.map +1 -1
  19. package/dist/drivers/postgres.d.ts.map +1 -1
  20. package/dist/drivers/postgres.js +13 -2
  21. package/dist/drivers/postgres.js.map +1 -1
  22. package/dist/drivers/sqlite.d.ts +2 -0
  23. package/dist/drivers/sqlite.d.ts.map +1 -1
  24. package/dist/drivers/sqlite.js +49 -4
  25. package/dist/drivers/sqlite.js.map +1 -1
  26. package/dist/index.cjs +10741 -0
  27. package/dist/index.d.cts +1 -0
  28. package/dist/scp/coltype.d.ts +108 -0
  29. package/dist/scp/coltype.d.ts.map +1 -1
  30. package/dist/scp/coltype.js +293 -6
  31. package/dist/scp/coltype.js.map +1 -1
  32. package/dist/scp/connection-routing.d.ts +312 -0
  33. package/dist/scp/connection-routing.d.ts.map +1 -0
  34. package/dist/scp/connection-routing.js +394 -0
  35. package/dist/scp/connection-routing.js.map +1 -0
  36. package/dist/scp/dbmodel-runtime.d.ts +101 -0
  37. package/dist/scp/dbmodel-runtime.d.ts.map +1 -0
  38. package/dist/scp/dbmodel-runtime.js +174 -0
  39. package/dist/scp/dbmodel-runtime.js.map +1 -0
  40. package/dist/scp/decorator-adapter.d.ts +196 -0
  41. package/dist/scp/decorator-adapter.d.ts.map +1 -0
  42. package/dist/scp/decorator-adapter.js +286 -0
  43. package/dist/scp/decorator-adapter.js.map +1 -0
  44. package/dist/scp/emit/emitter.d.ts +108 -0
  45. package/dist/scp/emit/emitter.d.ts.map +1 -0
  46. package/dist/scp/emit/emitter.js +782 -0
  47. package/dist/scp/emit/emitter.js.map +1 -0
  48. package/dist/scp/emit/endpoint.d.ts +248 -0
  49. package/dist/scp/emit/endpoint.d.ts.map +1 -0
  50. package/dist/scp/emit/endpoint.js +20 -0
  51. package/dist/scp/emit/endpoint.js.map +1 -0
  52. package/dist/scp/emit/index.d.ts +11 -0
  53. package/dist/scp/emit/index.d.ts.map +1 -0
  54. package/dist/scp/emit/index.js +13 -0
  55. package/dist/scp/emit/index.js.map +1 -0
  56. package/dist/scp/errors.d.ts +45 -1
  57. package/dist/scp/errors.d.ts.map +1 -1
  58. package/dist/scp/errors.js +47 -1
  59. package/dist/scp/errors.js.map +1 -1
  60. package/dist/scp/exec-context.d.ts +390 -0
  61. package/dist/scp/exec-context.d.ts.map +1 -0
  62. package/dist/scp/exec-context.js +540 -0
  63. package/dist/scp/exec-context.js.map +1 -0
  64. package/dist/scp/grouping.d.ts +32 -0
  65. package/dist/scp/grouping.d.ts.map +1 -0
  66. package/dist/scp/grouping.js +0 -0
  67. package/dist/scp/grouping.js.map +1 -0
  68. package/dist/scp/index.cjs +6432 -17579
  69. package/dist/scp/index.d.cts +1 -0
  70. package/dist/scp/index.d.mts +1 -0
  71. package/dist/scp/index.d.ts +51 -52
  72. package/dist/scp/index.d.ts.map +1 -1
  73. package/dist/scp/index.js +189 -152
  74. package/dist/scp/index.js.map +1 -1
  75. package/dist/scp/index.mjs +6067 -3659
  76. package/dist/scp/leaf-transport.d.ts +50 -0
  77. package/dist/scp/leaf-transport.d.ts.map +1 -0
  78. package/dist/scp/leaf-transport.js +92 -0
  79. package/dist/scp/leaf-transport.js.map +1 -0
  80. package/dist/scp/leaves.d.ts +174 -0
  81. package/dist/scp/leaves.d.ts.map +1 -0
  82. package/dist/scp/leaves.js +287 -0
  83. package/dist/scp/leaves.js.map +1 -0
  84. package/dist/scp/limit-config.d.ts +118 -0
  85. package/dist/scp/limit-config.d.ts.map +1 -0
  86. package/dist/scp/limit-config.js +126 -0
  87. package/dist/scp/limit-config.js.map +1 -0
  88. package/dist/scp/makesql/compile-relation.d.ts +49 -23
  89. package/dist/scp/makesql/compile-relation.d.ts.map +1 -1
  90. package/dist/scp/makesql/compile-relation.js +101 -76
  91. package/dist/scp/makesql/compile-relation.js.map +1 -1
  92. package/dist/scp/makesql/compile-select.d.ts +45 -10
  93. package/dist/scp/makesql/compile-select.d.ts.map +1 -1
  94. package/dist/scp/makesql/compile-select.js +50 -10
  95. package/dist/scp/makesql/compile-select.js.map +1 -1
  96. package/dist/scp/makesql/handler.d.ts +1 -1
  97. package/dist/scp/makesql/handler.d.ts.map +1 -1
  98. package/dist/scp/makesql/index.d.ts +7 -10
  99. package/dist/scp/makesql/index.d.ts.map +1 -1
  100. package/dist/scp/makesql/index.js +19 -30
  101. package/dist/scp/makesql/index.js.map +1 -1
  102. package/dist/scp/makesql/json-array.d.ts +65 -0
  103. package/dist/scp/makesql/json-array.d.ts.map +1 -1
  104. package/dist/scp/makesql/json-array.js +99 -10
  105. package/dist/scp/makesql/json-array.js.map +1 -1
  106. package/dist/scp/makesql/json-batch.d.ts.map +1 -1
  107. package/dist/scp/makesql/json-batch.js +21 -19
  108. package/dist/scp/makesql/json-batch.js.map +1 -1
  109. package/dist/scp/makesql/makesql.d.ts +1 -1
  110. package/dist/scp/makesql/makesql.d.ts.map +1 -1
  111. package/dist/scp/makesql/mysql-returning.d.ts +93 -0
  112. package/dist/scp/makesql/mysql-returning.d.ts.map +1 -0
  113. package/dist/scp/makesql/mysql-returning.js +215 -0
  114. package/dist/scp/makesql/mysql-returning.js.map +1 -0
  115. package/dist/scp/makesql/outtype.d.ts +75 -34
  116. package/dist/scp/makesql/outtype.d.ts.map +1 -1
  117. package/dist/scp/makesql/outtype.js +91 -118
  118. package/dist/scp/makesql/outtype.js.map +1 -1
  119. package/dist/scp/makesql/pool-executor.d.ts +135 -13
  120. package/dist/scp/makesql/pool-executor.d.ts.map +1 -1
  121. package/dist/scp/makesql/pool-executor.js +229 -21
  122. package/dist/scp/makesql/pool-executor.js.map +1 -1
  123. package/dist/scp/makesql/tx.d.ts +107 -22
  124. package/dist/scp/makesql/tx.d.ts.map +1 -1
  125. package/dist/scp/makesql/tx.js +451 -97
  126. package/dist/scp/makesql/tx.js.map +1 -1
  127. package/dist/scp/makesql/writeouttype.d.ts +7 -8
  128. package/dist/scp/makesql/writeouttype.d.ts.map +1 -1
  129. package/dist/scp/makesql/writeouttype.js +8 -7
  130. package/dist/scp/makesql/writeouttype.js.map +1 -1
  131. package/dist/scp/middleware.d.ts +287 -0
  132. package/dist/scp/middleware.d.ts.map +1 -0
  133. package/dist/scp/middleware.js +358 -0
  134. package/dist/scp/middleware.js.map +1 -0
  135. package/dist/scp/relation.d.ts +80 -9
  136. package/dist/scp/relation.d.ts.map +1 -1
  137. package/dist/scp/relation.js +198 -78
  138. package/dist/scp/relation.js.map +1 -1
  139. package/dist/scp/tx-options.d.ts +136 -0
  140. package/dist/scp/tx-options.d.ts.map +1 -0
  141. package/dist/scp/tx-options.js +184 -0
  142. package/dist/scp/tx-options.js.map +1 -0
  143. package/dist/scp/write-bundle.d.ts +131 -0
  144. package/dist/scp/write-bundle.d.ts.map +1 -0
  145. package/dist/scp/write-bundle.js +160 -0
  146. package/dist/scp/write-bundle.js.map +1 -0
  147. package/dist/types.d.ts +7 -0
  148. package/dist/types.d.ts.map +1 -1
  149. package/dist/types.js.map +1 -1
  150. package/docs/BENCHMARK.md +58 -56
  151. package/docs/benchmark-chart.svg +166 -166
  152. package/docs/design/phase-a-execution-context.md +139 -0
  153. package/package.json +16 -9
  154. package/dist/scp/authoring-sql.d.ts +0 -134
  155. package/dist/scp/authoring-sql.d.ts.map +0 -1
  156. package/dist/scp/authoring-sql.js +0 -191
  157. package/dist/scp/authoring-sql.js.map +0 -1
  158. package/dist/scp/authoring.d.ts +0 -155
  159. package/dist/scp/authoring.d.ts.map +0 -1
  160. package/dist/scp/authoring.js +0 -162
  161. package/dist/scp/authoring.js.map +0 -1
  162. package/dist/scp/catalog.d.ts +0 -94
  163. package/dist/scp/catalog.d.ts.map +0 -1
  164. package/dist/scp/catalog.js +0 -234
  165. package/dist/scp/catalog.js.map +0 -1
  166. package/dist/scp/codegen.d.ts +0 -189
  167. package/dist/scp/codegen.d.ts.map +0 -1
  168. package/dist/scp/codegen.js +0 -421
  169. package/dist/scp/codegen.js.map +0 -1
  170. package/dist/scp/find-filter-guard.d.ts +0 -68
  171. package/dist/scp/find-filter-guard.d.ts.map +0 -1
  172. package/dist/scp/find-filter-guard.js +0 -88
  173. package/dist/scp/find-filter-guard.js.map +0 -1
  174. package/dist/scp/guard.d.ts +0 -43
  175. package/dist/scp/guard.d.ts.map +0 -1
  176. package/dist/scp/guard.js +0 -134
  177. package/dist/scp/guard.js.map +0 -1
  178. package/dist/scp/makesql/authoring-compile.d.ts +0 -90
  179. package/dist/scp/makesql/authoring-compile.d.ts.map +0 -1
  180. package/dist/scp/makesql/authoring-compile.js +0 -494
  181. package/dist/scp/makesql/authoring-compile.js.map +0 -1
  182. package/dist/scp/makesql/ir.d.ts +0 -42
  183. package/dist/scp/makesql/ir.d.ts.map +0 -1
  184. package/dist/scp/makesql/ir.js +0 -66
  185. package/dist/scp/makesql/ir.js.map +0 -1
  186. package/dist/scp/makesql/static-bundle.d.ts +0 -187
  187. package/dist/scp/makesql/static-bundle.d.ts.map +0 -1
  188. package/dist/scp/makesql/static-bundle.js +0 -1113
  189. package/dist/scp/makesql/static-bundle.js.map +0 -1
  190. package/dist/scp/runtime.d.ts +0 -242
  191. package/dist/scp/runtime.d.ts.map +0 -1
  192. package/dist/scp/runtime.js +0 -400
  193. package/dist/scp/runtime.js.map +0 -1
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ /**
3
+ * litedbmodel v2 SCP — **MySQL has no `RETURNING`**: the one place that knowledge lives.
4
+ *
5
+ * A write compiled for the `mysql` dialect carries the SAME ` RETURNING <cols>` tail as the PG /
6
+ * SQLite bundles (the compilers are dialect-neutral about it), plus a strip-before-execute
7
+ * `/*scp:pk=…;ai=…[;conflict=…]* /` comment naming the target's REAL primary key. MySQL parses
8
+ * neither, so the driver seam STRIPS both and recovers the written rows with a SELECT keyed on
9
+ * whatever identifies them:
10
+ *
11
+ * | write | the rows are recovered by | when |
12
+ * |------------------|--------------------------------------------------------------------|-----------|
13
+ * | create | the AUTO_INCREMENT range `[LAST_INSERT_ID, +affected)`, or the | after |
14
+ * | | client-supplied PK values pulled from the INSERT params by position | |
15
+ * | createMany | the same AUTO_INCREMENT range (N consecutive ids) | after |
16
+ * | upsert / …Many | the CONFLICT key (MySQL does not report the conflicted-row id, so | after |
17
+ * | | the AUTO_INCREMENT range is wrong when a row was UPDATED) | |
18
+ * | update | the write's OWN WHERE predicate | after |
19
+ * | updateMany | the batch JOIN key, re-bound from the SAME JSON payload | after |
20
+ * | delete/deleteMany| the write's OWN WHERE predicate — **before** the write, since the | BEFORE |
21
+ * | | rows no longer exist once the DELETE has run | |
22
+ *
23
+ * The recovering SELECT runs on the SAME connection as the write, so inside a transaction it sees
24
+ * the not-yet-committed rows — and a `DELETE`'s pre-image SELECT is inside the same transaction as
25
+ * the delete it describes.
26
+ *
27
+ * This module is the TypeScript member of a 5-language SSoT: `rust/litedbmodel_runtime/src/livedb.rs`
28
+ * (`build_mysql_reselect`), `go/litedbmodel_runtime/livedb.go`, `python/litedbmodel_runtime/driver.py`
29
+ * and `php/src/LiveDb.php` derive the identical write/select/bind triple. Both TS execution paths —
30
+ * the generated (codegen) leaf `executeSQL` and the mode-2 transaction plan — reach it through the
31
+ * ONE mysql connection adapter (`pool-executor.mysqlConnection`), so they cannot return different rows.
32
+ */
33
+ Object.defineProperty(exports, "__esModule", { value: true });
34
+ exports.mysqlPkHint = mysqlPkHint;
35
+ exports.stripMysqlPkHint = stripMysqlPkHint;
36
+ exports.buildMysqlReselect = buildMysqlReselect;
37
+ exports.bindReselect = bindReselect;
38
+ /** The strip-before-execute PK-hint comment marker. */
39
+ const MYSQL_PK_HINT_RE = /\s*\/\*scp:pk=[^*]*\*\//;
40
+ /**
41
+ * Serialize a {@link TxOp.pk} descriptor into a strip-before-execute SQL comment appended to an
42
+ * INSERT…RETURNING op, so the MySQL driver emulation can re-select by the REAL primary key. The
43
+ * comment is STRIPPED (with the RETURNING clause) before the write executes, so the executed SQL
44
+ * stays byte-clean; it is emitted ONLY into the mysql-dialect bundle (PG/SQLite keep native
45
+ * RETURNING and never see it). Format: ` /*scp:pk=col1,col2;ai=<autoIncCol|>[;conflict=<cols>]* /`.
46
+ *
47
+ * `conflict` (the upsert conflict-target column list) is added when the write is an upsert — the mysql
48
+ * driver re-selects the upserted row(s) by that key. Its source is `op.writeMeta.onConflict` (single
49
+ * writes carry it); an ad-hoc TxOp built without writeMeta (the batch createMany/upsertMany path)
50
+ * passes the columns via `onConflict`.
51
+ */
52
+ function mysqlPkHint(op, onConflict) {
53
+ if (op.pk === undefined)
54
+ return op;
55
+ if (!/\breturning\b/i.test(op.sql))
56
+ return op;
57
+ if (MYSQL_PK_HINT_RE.test(op.sql))
58
+ return op; // idempotent: never append a second hint
59
+ const conflict = onConflict ?? op.writeMeta?.onConflict;
60
+ const conflictPart = conflict !== undefined && conflict.length > 0 ? `;conflict=${conflict}` : '';
61
+ const hint = ` /*scp:pk=${op.pk.columns.join(',')};ai=${op.pk.autoInc ?? ''}${conflictPart}*/`;
62
+ return { ...op, sql: op.sql + hint };
63
+ }
64
+ /** Strip a trailing MySQL PK-hint comment from a rendered SQL (defensive; runtimes strip too). */
65
+ function stripMysqlPkHint(sql) {
66
+ return sql.replace(MYSQL_PK_HINT_RE, '');
67
+ }
68
+ /** The `,`-separated INSERT column list (`INSERT [IGNORE] INTO t (a, b, c)` → `['a','b','c']`). */
69
+ function insertCols(writeSql) {
70
+ const m = /\bINSERT\s+(?:IGNORE\s+)?INTO\s+[A-Za-z0-9_."`]+\s*\(([^)]*)\)/i.exec(writeSql);
71
+ return m === null ? [] : m[1].split(',').map((c) => c.trim());
72
+ }
73
+ /** The target table — the identifier after `INSERT INTO` / `UPDATE` / `DELETE FROM`. */
74
+ function tableOf(writeSql) {
75
+ const m = /\b(?:INSERT\s+(?:IGNORE\s+)?INTO|UPDATE|DELETE\s+FROM)\s+([A-Za-z0-9_."`]+)/i.exec(writeSql);
76
+ return m === null ? null : m[1];
77
+ }
78
+ /** The JOIN key column of an updateMany (`ON <alias>.<col> = JSON_UNQUOTE(…)`). */
79
+ function updateBatchKey(writeSql) {
80
+ const m = /\sON\s+[A-Za-z0-9_]*\.?([A-Za-z0-9_]+)\s*=/i.exec(writeSql);
81
+ return m === null ? null : m[1];
82
+ }
83
+ /** The hint's `pk=` columns + `ai=` AUTO_INCREMENT column (absent hint ⇒ `[]` / `''`). */
84
+ function parsePkHint(hintRegion) {
85
+ const m = /\/\*scp:pk=([^;*]*);ai=([^;*]*)/i.exec(hintRegion);
86
+ if (m === null)
87
+ return { pk: [], autoInc: '' };
88
+ return { pk: m[1].split(',').map((c) => c.trim()).filter((c) => c.length > 0), autoInc: m[2].trim() };
89
+ }
90
+ /** The hint's `conflict=` columns (the upsert conflict target). Empty ⇒ not an upsert. */
91
+ function parseConflictHint(hintRegion) {
92
+ const m = /;conflict=([^*]*)\*\//i.exec(hintRegion);
93
+ if (m === null)
94
+ return [];
95
+ return m[1].split(',').map((c) => c.trim()).filter((c) => c.length > 0);
96
+ }
97
+ /** The `?` count of a SQL fragment (the write's params are positional, so a count IS an index). */
98
+ function placeholders(fragment) {
99
+ return (fragment.match(/\?/g) ?? []).length;
100
+ }
101
+ /**
102
+ * Derive the MySQL RETURNING recovery for `sql`, or `null` when the statement declares no RETURNING
103
+ * (a plain write / a SELECT — the caller runs it unchanged).
104
+ *
105
+ * Fail-closed: a RETURNING write whose key cannot be identified (an upsert with no `conflict=` hint,
106
+ * an INSERT whose declared PK column is not in its column list, an UPDATE/DELETE with no WHERE)
107
+ * THROWS rather than silently returning no rows — returning `[]` for a write the caller asked to
108
+ * describe is exactly the defect this module exists to remove.
109
+ */
110
+ function buildMysqlReselect(sql) {
111
+ const retPos = sql.toLowerCase().lastIndexOf(' returning ');
112
+ if (retPos < 0)
113
+ return null;
114
+ const hintRegion = sql.slice(retPos);
115
+ const cols = stripMysqlPkHint(sql.slice(retPos + ' returning '.length)).trim();
116
+ const { pk, autoInc } = parsePkHint(hintRegion);
117
+ const conflict = parseConflictHint(hintRegion);
118
+ const writeSql = stripMysqlPkHint(sql.slice(0, retPos)).trim();
119
+ const lower = writeSql.toLowerCase();
120
+ const table = tableOf(writeSql);
121
+ if (table === null)
122
+ throw new Error(`scp write(mysql): cannot parse the target table of '${writeSql.slice(0, 60)}…'`);
123
+ // Order by the DECLARED pk so MySQL matches the pg/sqlite RETURNING order (§10 all-dialect parity).
124
+ const orderBy = pk.length > 0 ? ` ORDER BY ${pk.join(', ')}` : '';
125
+ const isBatch = lower.includes('json_table(');
126
+ const done = (selectSql, binds, before = false) => ({ writeSql, selectSql, binds, before });
127
+ /** Recover a BATCH write's rows by reading its key set back out of the SAME JSON payload it bound. */
128
+ const jsonSelect = (key) => `SELECT ${cols} FROM ${table} WHERE ${key} IN ` +
129
+ `(SELECT JSON_UNQUOTE(jt.${key}) FROM JSON_TABLE(?, '$[*]' COLUMNS(${key} JSON PATH '$.${key}')) jt)${orderBy}`;
130
+ // upsert / upsertMany — by the CONFLICT key. MySQL does not report which row an ON DUPLICATE KEY
131
+ // UPDATE touched, so the AUTO_INCREMENT range is wrong as soon as a row was updated, not inserted.
132
+ if (lower.startsWith('insert') && lower.includes('on duplicate key update')) {
133
+ const key = conflict[0];
134
+ if (key === undefined) {
135
+ throw new Error(`scp write(mysql): an upsert…RETURNING needs its conflict key in the pk hint ('${writeSql.slice(0, 60)}…')`);
136
+ }
137
+ if (isBatch)
138
+ return done(jsonSelect(key), [{ kind: 'json' }]);
139
+ const index = insertCols(writeSql).indexOf(key);
140
+ if (index < 0)
141
+ throw new Error(`scp write(mysql): conflict key '${key}' is not among the INSERT columns of '${writeSql.slice(0, 60)}…'`);
142
+ return done(`SELECT ${cols} FROM ${table} WHERE ${key} = ?${orderBy}`, [{ kind: 'param', index }]);
143
+ }
144
+ // create / createMany — by the AUTO_INCREMENT range [LAST_INSERT_ID, +affected), or by the
145
+ // client-supplied PK values pulled from the INSERT params by column position (UUID / composite).
146
+ if (lower.startsWith('insert')) {
147
+ if (autoInc !== '' && pk.length === 1 && pk[0] === autoInc) {
148
+ return done(`SELECT ${cols} FROM ${table} WHERE ${autoInc} >= ? AND ${autoInc} < ?${orderBy}`, [
149
+ { kind: 'lastId' },
150
+ { kind: 'highId' },
151
+ ]);
152
+ }
153
+ if (pk.length === 0) {
154
+ throw new Error(`scp write(mysql): an INSERT…RETURNING carries no pk hint, so its written rows cannot be identified ` +
155
+ `('${writeSql.slice(0, 60)}…'). The producer must pass the model's declared primary key.`);
156
+ }
157
+ if (isBatch) {
158
+ // createMany with a CLIENT-supplied key: the statement binds ONE JSON payload, not one param per
159
+ // key, so the keys are read back out of that SAME payload — as the upsertMany arm does. (An
160
+ // AUTO_INCREMENT batch never reaches here; its N consecutive ids are the range above.)
161
+ if (pk.length !== 1) {
162
+ throw new Error(`scp write(mysql): a batch INSERT…RETURNING on the COMPOSITE key (${pk.join(', ')}) cannot be ` +
163
+ `recovered from its JSON payload ('${writeSql.slice(0, 60)}…').`);
164
+ }
165
+ return done(jsonSelect(pk[0]), [{ kind: 'json' }]);
166
+ }
167
+ const cols4 = insertCols(writeSql);
168
+ const binds = [];
169
+ const conds = [];
170
+ for (const c of pk) {
171
+ const index = cols4.indexOf(c);
172
+ if (index < 0)
173
+ throw new Error(`scp write(mysql): PK column '${c}' is not among the INSERT columns of '${writeSql.slice(0, 60)}…'`);
174
+ conds.push(`${c} = ?`);
175
+ binds.push({ kind: 'param', index });
176
+ }
177
+ return done(`SELECT ${cols} FROM ${table} WHERE ${conds.join(' AND ')}${orderBy}`, binds);
178
+ }
179
+ // updateMany — by the batch JOIN key, re-selected from the SAME JSON payload the write bound.
180
+ if (lower.startsWith('update') && isBatch) {
181
+ const key = updateBatchKey(writeSql);
182
+ if (key === null)
183
+ throw new Error(`scp write(mysql): cannot parse the batch JOIN key of '${writeSql.slice(0, 60)}…'`);
184
+ return done(jsonSelect(key), [{ kind: 'json' }]);
185
+ }
186
+ // update / delete — by the write's OWN WHERE predicate, bound from the write's own params. The
187
+ // UPDATE re-selects AFTER the write (the rows carry their new values); the DELETE re-selects
188
+ // BEFORE it (afterwards there is nothing left to describe).
189
+ const wherePos = lower.lastIndexOf(' where ');
190
+ if (wherePos < 0) {
191
+ throw new Error(`scp write(mysql): a ${lower.startsWith('delete') ? 'DELETE' : 'UPDATE'}…RETURNING needs a WHERE to recover its rows ('${writeSql.slice(0, 60)}…')`);
192
+ }
193
+ const whereSql = writeSql.slice(wherePos + ' where '.length).trim();
194
+ const before = placeholders(writeSql.slice(0, wherePos));
195
+ const binds = [];
196
+ for (let i = 0; i < placeholders(whereSql); i++)
197
+ binds.push({ kind: 'param', index: before + i });
198
+ return done(`SELECT ${cols} FROM ${table} WHERE ${whereSql}${orderBy}`, binds, lower.startsWith('delete'));
199
+ }
200
+ /** Bind {@link MysqlReselect.binds} against the write's params + the write's own result. */
201
+ function bindReselect(binds, params, lastInsertId, affected) {
202
+ return binds.map((b) => {
203
+ switch (b.kind) {
204
+ case 'lastId':
205
+ return lastInsertId;
206
+ case 'highId':
207
+ return lastInsertId + Math.max(1, affected);
208
+ case 'json':
209
+ return params[0];
210
+ case 'param':
211
+ return params[b.index];
212
+ }
213
+ });
214
+ }
215
+ //# sourceMappingURL=mysql-returning.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql-returning.js","sourceRoot":"","sources":["../../../src/scp/makesql/mysql-returning.ts"],"names":[],"mappings":"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"}
@@ -1,44 +1,85 @@
1
1
  /**
2
- * litedbmodel v2 SCP — codegen `outType` derivation (spec §4.1; issues #58/#56).
2
+ * litedbmodel v2 SCP — read-row de-box type derivation (spec §4.1; issues #58/#59).
3
3
  *
4
- * bc's TYPED-RAW de-box emitters (`rust-typed-raw` / `go-typed-raw` / `typescript-typed`) require
5
- * each read node to carry an `outType` and the component an `outputType` (bc portable type
6
- * notation). WITHOUT them the emitter hard-fails ("nothing to de-box") — the correct fail-closed
7
- * behavior. This module derives those annotations for a READ component from:
4
+ * The read-column coverage + de-box SSoT for the op-independent leaf path. It resolves a read's
5
+ * projection ONCE into two projections of the SAME column-type resolution:
8
6
  *
9
- * 1. the per-node SELECT projection (`table` + `select` ports) → a row `obj` type, each column's
10
- * bc scalar resolved from its SQL type (the schema/DDL SoT) via {@link sqlTypeToBcScalar};
11
- * 2. the node cardinality (a Select node returns a row LIST → `{arr:rowObj}`; a Count → `int`; a
12
- * `.map` node wraps its per-element handler result → `{arr:<elem>}`);
13
- * 3. the component `output` Φ-expression, walked to compose the `outputType` from the node types
14
- * (`{ref:[nodeId]}` → that node's outType; `{obj:{…}}`/`{arr:…}`/`{opt:…}` recurse).
7
+ * - {@link deriveReadRow} — the read ROW `{arr:{obj:{outputKey: <read-de-boxed scalar>}}}` (the
8
+ * bc `outType` bc's native emitter reads for the typed de-box, #154) PLUS the TS-leaf
9
+ * `materializers` coercion map. Array columns read as `{arr:<element>}` (the SSoT array split
10
+ * {@link import('../coltype').arrayElementType}); the element rides the SAME de-box scalar.
11
+ * - {@link outputType} — composes the component `outputType` by walking the `output` Φ-expression
12
+ * against a `nodeId → outType` map.
15
13
  *
16
- * Spec §4.1 discipline: an ambiguous/unmappable projection is a HARD ERROR (no-assume,
17
- * no-fallback) — a `*` projection, a computed/aliased column, or a column with no schema type
18
- * THROWS. It is NEVER degraded to a boxed/untyped output; that would silently defeat the de-box.
14
+ * Spec §4.1 discipline: an ambiguous/unmappable projection is a HARD ERROR (no-assume, no-fallback)
15
+ * — a `*` projection, a computed column (typed reads only), or an undeclared/unknown-type column
16
+ * THROWS via {@link parseProjectionColumn} + the coltype SSoT; never a boxed/untyped output.
19
17
  */
20
- import type { PortableType } from 'behavior-contracts';
21
- import type { Component, ComponentRefNode, MapNode } from '../authoring';
22
- import { type ColumnTypeResolver } from '../coltype';
23
- /** A body node that is a component ref or a map (a cond node has no SELECT projection). */
24
- type RefLike = ComponentRefNode | MapNode;
18
+ import type { PortableType } from 'behavior-contracts/runtime';
19
+ import { type MaterializeClass, type ColumnTypeResolver } from '../coltype';
25
20
  /**
26
- * The `outType` of ONE read body node. A `Select` returns a ROW LIST → `{arr:rowObj}`. A `Count`
27
- * returns a single `int`. A `.map` node runs its inner component PER element of the iterated list,
28
- * so its result is `{arr:<per-element outType>}` (the per-element type being the inner node's own
29
- * outType — for a `.map` over a Select-per-element that is `{arr:{arr:rowObj}}`). An unrecognized
30
- * component throws (typed codegen covers only the SQL read catalog).
21
+ * A parsed `select`-list entry (issue #59). Either a SCHEMA-COLUMN reference (bare / qualified /
22
+ * aliased — resolvable to a declared column type + a driver-row output key) OR a COMPUTED expression
23
+ * (a function / aggregate / literal / cast — no underlying schema column). `*` is neither; it throws
24
+ * (the concrete column list is unknown, so nothing can be typed).
31
25
  */
32
- export declare function nodeOutType(node: RefLike, resolveColumnType: ColumnTypeResolver): PortableType;
26
+ export type ProjectionEntry = {
27
+ readonly kind: 'column';
28
+ /** The bare column name (qualifier + alias stripped) — what the type resolver is keyed on. */
29
+ readonly underlying: string;
30
+ /** The property name the driver row carries (the alias, else the bare column). */
31
+ readonly outputKey: string;
32
+ /** The qualifier table if the projection was `qual.col` (a JOIN column), else `undefined` — the
33
+ * column's type resolves against THIS table, not the node's base table. */
34
+ readonly qualifier?: string;
35
+ } | {
36
+ readonly kind: 'computed';
37
+ readonly text: string;
38
+ };
33
39
  /**
34
- * Per-node `outType` (keyed by body node id) + the component `outputType`, derived for a READ
35
- * component (spec §4.1). `cond` nodes are skipped for the per-node map but a `cond` at the OUTPUT
36
- * position (a shared-branch merge) would be handled by {@link outputType} if reached — the bench's
37
- * read outputs are ref / obj compositions of Select nodes. Any ambiguity throws (fail-closed).
40
+ * The SINGLE projection-column parser (issue #59) — shared by BOTH the codegen `outType` derivation
41
+ * and the TS read-path materializer derivation so the two CANNOT diverge (the recurring source of
42
+ * silent-rounding leaks). Classifies ONE `select`-list entry:
43
+ * - bare `col` → `{ kind:'column', underlying:'col', outputKey:'col' }`.
44
+ * - qualified `t.col` → strip qualifier → `underlying:'col', outputKey:'col'`.
45
+ * - aliased `col AS b` / `t.col AS b` → `underlying:'col', outputKey:'b'` (the driver row key).
46
+ * - computed `COUNT(*)`, `NOW()`, `x::uuid`, `x+1`, aliased or not → `{ kind:'computed', text }`.
47
+ * - `*` / `t.*` → HARD ERROR (the concrete column list is unknown).
48
+ * A SCHEMA-column entry MUST be typed against the declaration (a caller resolves + fail-closes on an
49
+ * undeclared column → never a silent rounded-i64 leak). A COMPUTED entry has no schema column to
50
+ * round: the read path leaves it raw; the codegen path (which needs every struct field typed) rejects
51
+ * it. This ONE parser guarantees every shape is classified identically for both paths.
38
52
  */
39
- export declare function deriveReadOutTypes(component: Component, resolveColumnType: ColumnTypeResolver): {
40
- byNode: Map<string, PortableType>;
41
- outputType: PortableType;
42
- };
43
- export {};
53
+ export declare function parseProjectionColumn(col: string, table: string, at: string): ProjectionEntry;
54
+ /** The read row TYPE (SSoT) + its TS-leaf coercion map — two projections of ONE column resolution. */
55
+ export interface ReadRow {
56
+ /** The read row `{arr:{obj:{outputKey: readScalar}}}` — the SINGLE row-type representation (#141). */
57
+ readonly outType: PortableType;
58
+ /** `outputKey → MaterializeClass` (non-passthrough only) — the TS-leaf coercion derived from the SAME row. */
59
+ readonly materializers: Record<string, MaterializeClass>;
60
+ }
61
+ /**
62
+ * Derive the read ROW from a projection (#141 SSoT + #59 op-builder guard). Resolves each projected
63
+ * column ONCE via the SHARED {@link parseProjectionColumn} + the coltype SSoT, producing BOTH:
64
+ * - `outType` — `{arr:{obj:{outputKey: keyArrayElemScalar(sqlType)}}}`, the READ-DE-BOXED scalar
65
+ * (int32→`float`(number) / BIGINT→`string` / BOOLEAN→`bool` / DATE→`string` / text/uuid→`string`).
66
+ * This is the ONE row-type representation: bc's native emitter reads it for the typed de-box
67
+ * (#154), and it is the conform target the TS leaf coercion below produces.
68
+ * - `materializers` — the TS-leaf coercion map ({@link sqlTypeToMaterializeClass}); the leaf coerces
69
+ * each driver cell (BIGINT→exact string / DATE→string / BOOLEAN→boolean) so the row MATCHES `outType`
70
+ * before bc conforms it (bc conform VALIDATES, does not coerce). Derived from the SAME resolution as
71
+ * `outType`, so the coerced JS form always equals the declared read scalar — no second row type.
72
+ * This IS the #59 read-column coverage guard: `*`/`t.*` throws (unknown column list), an undeclared
73
+ * column or unknown SQL type throws. A COMPUTED projection (`COUNT(*)`, cast, literal) is left RAW
74
+ * (omitted from BOTH) — #59: "the read path leaves it raw".
75
+ */
76
+ export declare function deriveReadRow(table: string, projection: readonly string[], resolveColumnType: ColumnTypeResolver, at: string): ReadRow;
77
+ /**
78
+ * Derive the component `outputType` by walking the `output` Φ-expression against a `nodeId →
79
+ * outType` map. `{ref:[id]}` resolves to that node's outType; `{obj:{k:expr,…}}` /
80
+ * `{arr:expr}` / `{opt:expr}` recurse structurally. A `ref` to an unknown id, or an operator the
81
+ * typed output path does not cover, is a HARD ERROR (no-assume, no-fallback) — the output must be
82
+ * a pure composition of typed node results, never a boxed dynamic tree.
83
+ */
84
+ export declare function outputType(output: unknown, byNode: Map<string, PortableType>, at: string): PortableType;
44
85
  //# sourceMappingURL=outtype.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"outtype.d.ts","sourceRoot":"","sources":["../../../src/scp/makesql/outtype.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;AAC3E,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAoC,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAcvF,2FAA2F;AAC3F,KAAK,OAAO,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAwE1C;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,GAAG,YAAY,CAmB9F;AAwDD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,SAAS,EACpB,iBAAiB,EAAE,kBAAkB,GACpC;IAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAAC,UAAU,EAAE,YAAY,CAAA;CAAE,CAOjE"}
1
+ {"version":3,"file":"outtype.d.ts","sourceRoot":"","sources":["../../../src/scp/makesql/outtype.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAqF,KAAK,gBAAgB,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAW/J;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB;IACE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,8FAA8F;IAC9F,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,kFAAkF;IAClF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;gFAC4E;IAC5E,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;CAC7B,GACD;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,eAAe,CA8B7F;AAGD,sGAAsG;AACtG,MAAM,WAAW,OAAO;IACtB,sGAAsG;IACtG,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,8GAA8G;IAC9G,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;CAC1D;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,iBAAiB,EAAE,kBAAkB,EACrC,EAAE,EAAE,MAAM,GACT,OAAO,CAyBT;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,YAAY,CA6CvG"}