litedbmodel 2.0.0 → 2.0.2
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/dist/scp/authoring-sql.d.ts +78 -0
- package/dist/scp/authoring-sql.d.ts.map +1 -1
- package/dist/scp/authoring-sql.js +110 -2
- package/dist/scp/authoring-sql.js.map +1 -1
- package/dist/scp/catalog.d.ts +5 -2
- package/dist/scp/catalog.d.ts.map +1 -1
- package/dist/scp/catalog.js +32 -1
- package/dist/scp/catalog.js.map +1 -1
- package/dist/scp/codegen.d.ts +143 -85
- package/dist/scp/codegen.d.ts.map +1 -1
- package/dist/scp/codegen.js +374 -90
- package/dist/scp/codegen.js.map +1 -1
- package/dist/scp/coltype.d.ts +41 -0
- package/dist/scp/coltype.d.ts.map +1 -0
- package/dist/scp/coltype.js +188 -0
- package/dist/scp/coltype.js.map +1 -0
- package/dist/scp/dialect.d.ts +12 -3
- package/dist/scp/dialect.d.ts.map +1 -1
- package/dist/scp/dialect.js +12 -3
- package/dist/scp/dialect.js.map +1 -1
- package/dist/scp/find-filter-guard.d.ts +68 -0
- package/dist/scp/find-filter-guard.d.ts.map +1 -0
- package/dist/scp/find-filter-guard.js +88 -0
- package/dist/scp/find-filter-guard.js.map +1 -0
- package/dist/scp/fragment/assemble.d.ts +61 -0
- package/dist/scp/fragment/assemble.d.ts.map +1 -0
- package/dist/scp/fragment/assemble.js +156 -0
- package/dist/scp/fragment/assemble.js.map +1 -0
- package/dist/scp/fragment/compile-pg.d.ts +107 -0
- package/dist/scp/fragment/compile-pg.d.ts.map +1 -0
- package/dist/scp/fragment/compile-pg.js +142 -0
- package/dist/scp/fragment/compile-pg.js.map +1 -0
- package/dist/scp/fragment/index.d.ts +16 -0
- package/dist/scp/fragment/index.d.ts.map +1 -0
- package/dist/scp/fragment/index.js +34 -0
- package/dist/scp/fragment/index.js.map +1 -0
- package/dist/scp/fragment/model.d.ts +119 -0
- package/dist/scp/fragment/model.d.ts.map +1 -0
- package/dist/scp/fragment/model.js +66 -0
- package/dist/scp/fragment/model.js.map +1 -0
- package/dist/scp/fragment/pg-array-type.d.ts +13 -0
- package/dist/scp/fragment/pg-array-type.d.ts.map +1 -0
- package/dist/scp/fragment/pg-array-type.js +34 -0
- package/dist/scp/fragment/pg-array-type.js.map +1 -0
- package/dist/scp/guard.d.ts +2 -6
- package/dist/scp/guard.d.ts.map +1 -1
- package/dist/scp/guard.js +19 -22
- package/dist/scp/guard.js.map +1 -1
- package/dist/scp/index.cjs +18695 -3921
- package/dist/scp/index.d.ts +41 -24
- package/dist/scp/index.d.ts.map +1 -1
- package/dist/scp/index.js +134 -49
- package/dist/scp/index.js.map +1 -1
- package/dist/scp/index.mjs +4830 -1432
- package/dist/scp/makesql/authoring-compile.d.ts +90 -0
- package/dist/scp/makesql/authoring-compile.d.ts.map +1 -0
- package/dist/scp/makesql/authoring-compile.js +494 -0
- package/dist/scp/makesql/authoring-compile.js.map +1 -0
- package/dist/scp/makesql/compile-crud.d.ts +133 -0
- package/dist/scp/makesql/compile-crud.d.ts.map +1 -0
- package/dist/scp/makesql/compile-crud.js +288 -0
- package/dist/scp/makesql/compile-crud.js.map +1 -0
- package/dist/scp/makesql/compile-relation.d.ts +157 -0
- package/dist/scp/makesql/compile-relation.d.ts.map +1 -0
- package/dist/scp/makesql/compile-relation.js +425 -0
- package/dist/scp/makesql/compile-relation.js.map +1 -0
- package/dist/scp/makesql/compile-select.d.ts +52 -0
- package/dist/scp/makesql/compile-select.d.ts.map +1 -0
- package/dist/scp/makesql/compile-select.js +58 -0
- package/dist/scp/makesql/compile-select.js.map +1 -0
- package/dist/scp/makesql/compile.d.ts +63 -0
- package/dist/scp/makesql/compile.d.ts.map +1 -0
- package/dist/scp/makesql/compile.js +99 -0
- package/dist/scp/makesql/compile.js.map +1 -0
- package/dist/scp/makesql/handler.d.ts +58 -0
- package/dist/scp/makesql/handler.d.ts.map +1 -0
- package/dist/scp/makesql/handler.js +113 -0
- package/dist/scp/makesql/handler.js.map +1 -0
- package/dist/scp/makesql/index.d.ts +33 -0
- package/dist/scp/makesql/index.d.ts.map +1 -0
- package/dist/scp/makesql/index.js +114 -0
- package/dist/scp/makesql/index.js.map +1 -0
- package/dist/scp/makesql/ir.d.ts +42 -0
- package/dist/scp/makesql/ir.d.ts.map +1 -0
- package/dist/scp/makesql/ir.js +66 -0
- package/dist/scp/makesql/ir.js.map +1 -0
- package/dist/scp/makesql/json-array.d.ts +84 -0
- package/dist/scp/makesql/json-array.d.ts.map +1 -0
- package/dist/scp/makesql/json-array.js +118 -0
- package/dist/scp/makesql/json-array.js.map +1 -0
- package/dist/scp/makesql/json-batch.d.ts +102 -0
- package/dist/scp/makesql/json-batch.d.ts.map +1 -0
- package/dist/scp/makesql/json-batch.js +269 -0
- package/dist/scp/makesql/json-batch.js.map +1 -0
- package/dist/scp/makesql/makesql.d.ts +99 -0
- package/dist/scp/makesql/makesql.d.ts.map +1 -0
- package/dist/scp/makesql/makesql.js +122 -0
- package/dist/scp/makesql/makesql.js.map +1 -0
- package/dist/scp/makesql/outtype.d.ts +44 -0
- package/dist/scp/makesql/outtype.d.ts.map +1 -0
- package/dist/scp/makesql/outtype.js +197 -0
- package/dist/scp/makesql/outtype.js.map +1 -0
- package/dist/scp/makesql/pool-executor.d.ts +47 -0
- package/dist/scp/makesql/pool-executor.d.ts.map +1 -0
- package/dist/scp/makesql/pool-executor.js +50 -0
- package/dist/scp/makesql/pool-executor.js.map +1 -0
- package/dist/scp/makesql/static-bundle.d.ts +187 -0
- package/dist/scp/makesql/static-bundle.d.ts.map +1 -0
- package/dist/scp/makesql/static-bundle.js +1113 -0
- package/dist/scp/makesql/static-bundle.js.map +1 -0
- package/dist/scp/makesql/tx.d.ts +216 -0
- package/dist/scp/makesql/tx.d.ts.map +1 -0
- package/dist/scp/makesql/tx.js +788 -0
- package/dist/scp/makesql/tx.js.map +1 -0
- package/dist/scp/makesql/writeouttype.d.ts +66 -0
- package/dist/scp/makesql/writeouttype.d.ts.map +1 -0
- package/dist/scp/makesql/writeouttype.js +195 -0
- package/dist/scp/makesql/writeouttype.js.map +1 -0
- package/dist/scp/relation.d.ts +86 -74
- package/dist/scp/relation.d.ts.map +1 -1
- package/dist/scp/relation.js +186 -120
- package/dist/scp/relation.js.map +1 -1
- package/dist/scp/runtime.d.ts +158 -211
- package/dist/scp/runtime.d.ts.map +1 -1
- package/dist/scp/runtime.js +250 -515
- package/dist/scp/runtime.js.map +1 -1
- package/dist/scp/typed-object.d.ts +12 -1
- package/dist/scp/typed-object.d.ts.map +1 -1
- package/dist/scp/typed-object.js +25 -4
- package/dist/scp/typed-object.js.map +1 -1
- package/docs/proposal/litedbmodel-v2-scp-architecture.md +51 -0
- package/docs/proposal/v0-coverage-matrix.md +157 -0
- package/docs/proposal/v2-completeness-plan.md +90 -0
- package/docs/proposal/v2-sql-parity-checklist.md +162 -0
- package/package.json +3 -3
- package/dist/scp/bridge.d.ts +0 -64
- package/dist/scp/bridge.d.ts.map +0 -1
- package/dist/scp/bridge.js +0 -329
- package/dist/scp/bridge.js.map +0 -1
- package/dist/scp/compile-dialect.d.ts +0 -39
- package/dist/scp/compile-dialect.d.ts.map +0 -1
- package/dist/scp/compile-dialect.js +0 -62
- package/dist/scp/compile-dialect.js.map +0 -1
- package/dist/scp/compile-sqlite.d.ts +0 -114
- package/dist/scp/compile-sqlite.d.ts.map +0 -1
- package/dist/scp/compile-sqlite.js +0 -149
- package/dist/scp/compile-sqlite.js.map +0 -1
- package/dist/scp/ir.d.ts +0 -92
- package/dist/scp/ir.d.ts.map +0 -1
- package/dist/scp/ir.js +0 -28
- package/dist/scp/ir.js.map +0 -1
- package/dist/scp/render.d.ts +0 -40
- package/dist/scp/render.d.ts.map +0 -1
- package/dist/scp/render.js +0 -170
- package/dist/scp/render.js.map +0 -1
- package/dist/scp/write-plan.d.ts +0 -161
- package/dist/scp/write-plan.d.ts.map +0 -1
- package/dist/scp/write-plan.js +0 -528
- package/dist/scp/write-plan.js.map +0 -1
- package/dist/scp/write-runtime.d.ts +0 -80
- package/dist/scp/write-runtime.d.ts.map +0 -1
- package/dist/scp/write-runtime.js +0 -167
- package/dist/scp/write-runtime.js.map +0 -1
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — the single-JSON-param array forms for MySQL 8 + SQLite
|
|
3
|
+
* (epic #43 / #45).
|
|
4
|
+
*
|
|
5
|
+
* ## Why this exists — the intentional deviation from v1 text
|
|
6
|
+
*
|
|
7
|
+
* v1's MySQL/SQLite builders expand an array into N placeholders — `col IN (?, ?, …)`
|
|
8
|
+
* for an IN-list, `VALUES (?,?),(?,?),…` for a batch INSERT, `VALUES ROW(…)` /
|
|
9
|
+
* `CASE WHEN` for a batch UPDATE — binding ONE param per element. PostgreSQL already
|
|
10
|
+
* avoids that explosion (`= ANY(?::t[])` / `UNNEST(?::t[])` binds the whole array as a
|
|
11
|
+
* SINGLE param). This module gives MySQL/SQLite the same one-param property by moving
|
|
12
|
+
* the expansion SERVER-side: the array (or the batch rows) is encoded as ONE JSON
|
|
13
|
+
* string param and expanded inside the engine with a `JSON_TABLE` subquery
|
|
14
|
+
* (MySQL 8.0.4+) and `json_each` / `json_extract` (SQLite json1, always present via
|
|
15
|
+
* better-sqlite3). The IN-list uses a `col IN (SELECT … JSON_TABLE …)` SUBQUERY (NOT
|
|
16
|
+
* `MEMBER OF`) precisely so it inherits v1's `col IN (list)` type coercion — `MEMBER OF`
|
|
17
|
+
* does strict JSON-type comparison and DIVERGES from v1 on cross-type IN-lists.
|
|
18
|
+
*
|
|
19
|
+
* This is an owner-approved improvement OVER v1 — so the emitted SQL TEXT for
|
|
20
|
+
* MySQL/SQLite array/batch surfaces intentionally differs from v1. The correctness bar
|
|
21
|
+
* is RESULT PARITY (same rows / same post-write state on a real MySQL 8 + SQLite),
|
|
22
|
+
* proven by `test/scp/json-array-parity.test.ts`. **PostgreSQL is untouched** — it keeps
|
|
23
|
+
* `= ANY` / `UNNEST` / LATERAL and stays byte-identical to v1.
|
|
24
|
+
*
|
|
25
|
+
* Everything here is still just TEXT inside a `makeSQL` `sql` port plus ONE JSON param —
|
|
26
|
+
* no new IR kind, no new catalog leaf. The driver-side placeholder-count-expansion that
|
|
27
|
+
* v1 relied on for MySQL/SQLite arrays is GONE: every dialect now binds an array as one
|
|
28
|
+
* param with static SQL text.
|
|
29
|
+
*/
|
|
30
|
+
import { DBConditions, type ConditionObject, type ConditionValue } from '../../DBConditions';
|
|
31
|
+
import type { SqlCastFormatter } from '../../DBValues';
|
|
32
|
+
import type { Dialect } from './handler';
|
|
33
|
+
/** Dialects that use the single-JSON-param array forms (everything except PostgreSQL). */
|
|
34
|
+
export type JsonArrayDialect = 'mysql' | 'sqlite';
|
|
35
|
+
/**
|
|
36
|
+
* The MySQL/SQLite IN-list JSON form for a plain `col IN [values]` condition — a
|
|
37
|
+
* SUBQUERY form so it inherits the SAME comparison rules as v1's `col IN (list)`
|
|
38
|
+
* (crucially, MySQL's/SQLite's implicit type coercion), guaranteeing RESULT PARITY.
|
|
39
|
+
*
|
|
40
|
+
* - MySQL: `col IN (SELECT JSON_UNQUOTE(v) FROM JSON_TABLE(?, '$[*]' COLUMNS(v JSON PATH
|
|
41
|
+
* '$')) jt)`. `col IN (subquery)` uses the SAME type-coercion rules as `col IN (list)`
|
|
42
|
+
* — so e.g. an int column compared against JSON string values `["1","10"]` matches v1
|
|
43
|
+
* exactly (whereas `MEMBER OF` does STRICT JSON-type comparison and DIVERGES — proven
|
|
44
|
+
* on real MySQL 8). The `v JSON PATH '$'` + `JSON_UNQUOTE` extraction preserves
|
|
45
|
+
* arbitrary-length text (avoiding CHAR's 255 truncation-to-NULL) and full bigint /
|
|
46
|
+
* decimal precision, while yielding a value MySQL then coerces to the column type just
|
|
47
|
+
* like a literal in the IN-list. NO column type is required (right, since the IN
|
|
48
|
+
* condition path `DBConditions` carries none).
|
|
49
|
+
* - SQLite: `col IN (SELECT value FROM json_each(?))` — `json_each` yields each element
|
|
50
|
+
* with its natural (dynamic) type and the IN-subquery inherits SQLite's affinity
|
|
51
|
+
* comparison, matching v1 (incl. cross-type int-col × string values).
|
|
52
|
+
*
|
|
53
|
+
* The single param is the JSON-encoded array string (`[1,2,3]`). Empty arrays are NOT
|
|
54
|
+
* routed here — the caller keeps v1's `1 = 0` for the empty case (no param, exact v1
|
|
55
|
+
* empty semantics). Cross-type / bigint / decimal parity vs v1 is proven on real
|
|
56
|
+
* MySQL 8 + SQLite in `test/scp/json-array-parity.test.ts`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function inListJson(dialect: JsonArrayDialect, col: string, values: unknown[]): {
|
|
59
|
+
sql: string;
|
|
60
|
+
param: string;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* A dialect-aware `DBConditions` for MySQL/SQLite that replaces the v1 N-placeholder
|
|
64
|
+
* IN-list (`col IN (?, ?, …)`) with the single-JSON-param form (see {@link inListJson}).
|
|
65
|
+
* Every OTHER construct — eq/ne/cmp, custom-op, IS NULL, boolean literal, raw, subquery,
|
|
66
|
+
* EXISTS, cast, tuple-IN, nested AND/OR — is delegated UNCHANGED to the base
|
|
67
|
+
* `DBConditions`, so only the plain-array membership case deviates from v1.
|
|
68
|
+
*
|
|
69
|
+
* The empty-array case is left to the base class (`1 = 0`), matching v1 exactly.
|
|
70
|
+
*/
|
|
71
|
+
export declare class JsonArrayConditions extends DBConditions {
|
|
72
|
+
private readonly _dialect;
|
|
73
|
+
constructor(conditions: ConditionObject, dialect: JsonArrayDialect, operator?: 'AND' | 'OR');
|
|
74
|
+
protected compileCondition(key: string, value: ConditionValue, params: unknown[], formatter?: SqlCastFormatter): string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build a `DBConditions` appropriate for the dialect: PostgreSQL gets the base class
|
|
78
|
+
* (v1 byte-identical IN-list), MySQL/SQLite get {@link JsonArrayConditions} (single-JSON
|
|
79
|
+
* IN-list). Nested `DBConditions` inside a condition object stay base-class (they are
|
|
80
|
+
* authored by callers via `new DBConditions`), so only TOP-LEVEL plain-array IN-lists on
|
|
81
|
+
* MySQL/SQLite take the JSON form — which is the surface v1 expanded.
|
|
82
|
+
*/
|
|
83
|
+
export declare function conditionsFor(conditions: ConditionObject, dialect: Dialect): DBConditions;
|
|
84
|
+
//# sourceMappingURL=json-array.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-array.d.ts","sourceRoot":"","sources":["../../../src/scp/makesql/json-array.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,YAAY,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAE7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,0FAA0F;AAC1F,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAElD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAapH;AAED;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,SAAQ,YAAY;IACnD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAmB;gBAEhC,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,gBAAgB,EAAE,QAAQ,GAAE,KAAK,GAAG,IAAY;IAKlG,SAAS,CAAC,gBAAgB,CACxB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,EACrB,MAAM,EAAE,OAAO,EAAE,EACjB,SAAS,CAAC,EAAE,gBAAgB,GAC3B,MAAM;CAiBV;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,UAAU,EAAE,eAAe,EAAE,OAAO,EAAE,OAAO,GAAG,YAAY,CAGzF"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — the single-JSON-param array forms for MySQL 8 + SQLite
|
|
4
|
+
* (epic #43 / #45).
|
|
5
|
+
*
|
|
6
|
+
* ## Why this exists — the intentional deviation from v1 text
|
|
7
|
+
*
|
|
8
|
+
* v1's MySQL/SQLite builders expand an array into N placeholders — `col IN (?, ?, …)`
|
|
9
|
+
* for an IN-list, `VALUES (?,?),(?,?),…` for a batch INSERT, `VALUES ROW(…)` /
|
|
10
|
+
* `CASE WHEN` for a batch UPDATE — binding ONE param per element. PostgreSQL already
|
|
11
|
+
* avoids that explosion (`= ANY(?::t[])` / `UNNEST(?::t[])` binds the whole array as a
|
|
12
|
+
* SINGLE param). This module gives MySQL/SQLite the same one-param property by moving
|
|
13
|
+
* the expansion SERVER-side: the array (or the batch rows) is encoded as ONE JSON
|
|
14
|
+
* string param and expanded inside the engine with a `JSON_TABLE` subquery
|
|
15
|
+
* (MySQL 8.0.4+) and `json_each` / `json_extract` (SQLite json1, always present via
|
|
16
|
+
* better-sqlite3). The IN-list uses a `col IN (SELECT … JSON_TABLE …)` SUBQUERY (NOT
|
|
17
|
+
* `MEMBER OF`) precisely so it inherits v1's `col IN (list)` type coercion — `MEMBER OF`
|
|
18
|
+
* does strict JSON-type comparison and DIVERGES from v1 on cross-type IN-lists.
|
|
19
|
+
*
|
|
20
|
+
* This is an owner-approved improvement OVER v1 — so the emitted SQL TEXT for
|
|
21
|
+
* MySQL/SQLite array/batch surfaces intentionally differs from v1. The correctness bar
|
|
22
|
+
* is RESULT PARITY (same rows / same post-write state on a real MySQL 8 + SQLite),
|
|
23
|
+
* proven by `test/scp/json-array-parity.test.ts`. **PostgreSQL is untouched** — it keeps
|
|
24
|
+
* `= ANY` / `UNNEST` / LATERAL and stays byte-identical to v1.
|
|
25
|
+
*
|
|
26
|
+
* Everything here is still just TEXT inside a `makeSQL` `sql` port plus ONE JSON param —
|
|
27
|
+
* no new IR kind, no new catalog leaf. The driver-side placeholder-count-expansion that
|
|
28
|
+
* v1 relied on for MySQL/SQLite arrays is GONE: every dialect now binds an array as one
|
|
29
|
+
* param with static SQL text.
|
|
30
|
+
*/
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.JsonArrayConditions = void 0;
|
|
33
|
+
exports.inListJson = inListJson;
|
|
34
|
+
exports.conditionsFor = conditionsFor;
|
|
35
|
+
const DBConditions_1 = require("../../DBConditions");
|
|
36
|
+
const DBValues_1 = require("../../DBValues");
|
|
37
|
+
/**
|
|
38
|
+
* The MySQL/SQLite IN-list JSON form for a plain `col IN [values]` condition — a
|
|
39
|
+
* SUBQUERY form so it inherits the SAME comparison rules as v1's `col IN (list)`
|
|
40
|
+
* (crucially, MySQL's/SQLite's implicit type coercion), guaranteeing RESULT PARITY.
|
|
41
|
+
*
|
|
42
|
+
* - MySQL: `col IN (SELECT JSON_UNQUOTE(v) FROM JSON_TABLE(?, '$[*]' COLUMNS(v JSON PATH
|
|
43
|
+
* '$')) jt)`. `col IN (subquery)` uses the SAME type-coercion rules as `col IN (list)`
|
|
44
|
+
* — so e.g. an int column compared against JSON string values `["1","10"]` matches v1
|
|
45
|
+
* exactly (whereas `MEMBER OF` does STRICT JSON-type comparison and DIVERGES — proven
|
|
46
|
+
* on real MySQL 8). The `v JSON PATH '$'` + `JSON_UNQUOTE` extraction preserves
|
|
47
|
+
* arbitrary-length text (avoiding CHAR's 255 truncation-to-NULL) and full bigint /
|
|
48
|
+
* decimal precision, while yielding a value MySQL then coerces to the column type just
|
|
49
|
+
* like a literal in the IN-list. NO column type is required (right, since the IN
|
|
50
|
+
* condition path `DBConditions` carries none).
|
|
51
|
+
* - SQLite: `col IN (SELECT value FROM json_each(?))` — `json_each` yields each element
|
|
52
|
+
* with its natural (dynamic) type and the IN-subquery inherits SQLite's affinity
|
|
53
|
+
* comparison, matching v1 (incl. cross-type int-col × string values).
|
|
54
|
+
*
|
|
55
|
+
* The single param is the JSON-encoded array string (`[1,2,3]`). Empty arrays are NOT
|
|
56
|
+
* routed here — the caller keeps v1's `1 = 0` for the empty case (no param, exact v1
|
|
57
|
+
* empty semantics). Cross-type / bigint / decimal parity vs v1 is proven on real
|
|
58
|
+
* MySQL 8 + SQLite in `test/scp/json-array-parity.test.ts`.
|
|
59
|
+
*/
|
|
60
|
+
function inListJson(dialect, col, values) {
|
|
61
|
+
if (dialect === 'mysql') {
|
|
62
|
+
return {
|
|
63
|
+
sql: `${col} IN (SELECT JSON_UNQUOTE(v) FROM JSON_TABLE(?, '$[*]' COLUMNS(v JSON PATH '$')) jt)`,
|
|
64
|
+
// A BOOLEAN element serializes to `1`/`0` in the MySQL JSON param (NOT JSON `true`/`false`):
|
|
65
|
+
// `JSON_UNQUOTE(v)` on JSON `true` yields the STRING `'true'`, which MySQL coerces to `0`
|
|
66
|
+
// against a TINYINT(1) — silently mismatching. `1`/`0` is exactly what v1's `col IN (?)`
|
|
67
|
+
// bound (the mysql2 driver sends a JS bool as `1`/`0`), so this keeps v1 RESULT parity. SQL
|
|
68
|
+
// text + PG are untouched; SQLite's `json_each` coerces JSON booleans natively (no change).
|
|
69
|
+
param: JSON.stringify(values.map((v) => (typeof v === 'boolean' ? (v ? 1 : 0) : v))),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return { sql: `${col} IN (SELECT value FROM json_each(?))`, param: JSON.stringify(values) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* A dialect-aware `DBConditions` for MySQL/SQLite that replaces the v1 N-placeholder
|
|
76
|
+
* IN-list (`col IN (?, ?, …)`) with the single-JSON-param form (see {@link inListJson}).
|
|
77
|
+
* Every OTHER construct — eq/ne/cmp, custom-op, IS NULL, boolean literal, raw, subquery,
|
|
78
|
+
* EXISTS, cast, tuple-IN, nested AND/OR — is delegated UNCHANGED to the base
|
|
79
|
+
* `DBConditions`, so only the plain-array membership case deviates from v1.
|
|
80
|
+
*
|
|
81
|
+
* The empty-array case is left to the base class (`1 = 0`), matching v1 exactly.
|
|
82
|
+
*/
|
|
83
|
+
class JsonArrayConditions extends DBConditions_1.DBConditions {
|
|
84
|
+
_dialect;
|
|
85
|
+
constructor(conditions, dialect, operator = 'AND') {
|
|
86
|
+
super(conditions, operator);
|
|
87
|
+
this._dialect = dialect;
|
|
88
|
+
}
|
|
89
|
+
compileCondition(key, value, params, formatter) {
|
|
90
|
+
// Intercept ONLY the plain-array IN-list case (non-empty, no custom-op `?` in key,
|
|
91
|
+
// not a DBToken). Everything else — including empty array (`1 = 0`), custom-op
|
|
92
|
+
// arrays, tuple-IN, raw — falls through to the base class untouched.
|
|
93
|
+
if (Array.isArray(value) &&
|
|
94
|
+
value.length > 0 &&
|
|
95
|
+
!key.includes('?') &&
|
|
96
|
+
!key.startsWith('__') &&
|
|
97
|
+
!(value instanceof DBValues_1.DBToken)) {
|
|
98
|
+
const { sql, param } = inListJson(this._dialect, key, value);
|
|
99
|
+
params.push(param);
|
|
100
|
+
return sql;
|
|
101
|
+
}
|
|
102
|
+
return super.compileCondition(key, value, params, formatter);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.JsonArrayConditions = JsonArrayConditions;
|
|
106
|
+
/**
|
|
107
|
+
* Build a `DBConditions` appropriate for the dialect: PostgreSQL gets the base class
|
|
108
|
+
* (v1 byte-identical IN-list), MySQL/SQLite get {@link JsonArrayConditions} (single-JSON
|
|
109
|
+
* IN-list). Nested `DBConditions` inside a condition object stay base-class (they are
|
|
110
|
+
* authored by callers via `new DBConditions`), so only TOP-LEVEL plain-array IN-lists on
|
|
111
|
+
* MySQL/SQLite take the JSON form — which is the surface v1 expanded.
|
|
112
|
+
*/
|
|
113
|
+
function conditionsFor(conditions, dialect) {
|
|
114
|
+
if (dialect === 'postgres')
|
|
115
|
+
return new DBConditions_1.DBConditions(conditions);
|
|
116
|
+
return new JsonArrayConditions(conditions, dialect);
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=json-array.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-array.js","sourceRoot":"","sources":["../../../src/scp/makesql/json-array.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;;;AAiCH,gCAaC;AAkDD,sCAGC;AAjGD,qDAA6F;AAC7F,6CAAyC;AAOzC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,SAAgB,UAAU,CAAC,OAAyB,EAAE,GAAW,EAAE,MAAiB;IAClF,IAAI,OAAO,KAAK,OAAO,EAAE,CAAC;QACxB,OAAO;YACL,GAAG,EAAE,GAAG,GAAG,qFAAqF;YAChG,6FAA6F;YAC7F,0FAA0F;YAC1F,yFAAyF;YACzF,4FAA4F;YAC5F,4FAA4F;YAC5F,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;SACrF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,GAAG,GAAG,sCAAsC,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9F,CAAC;AAED;;;;;;;;GAQG;AACH,MAAa,mBAAoB,SAAQ,2BAAY;IAClC,QAAQ,CAAmB;IAE5C,YAAY,UAA2B,EAAE,OAAyB,EAAE,WAAyB,KAAK;QAChG,KAAK,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAES,gBAAgB,CACxB,GAAW,EACX,KAAqB,EACrB,MAAiB,EACjB,SAA4B;QAE5B,mFAAmF;QACnF,+EAA+E;QAC/E,qEAAqE;QACrE,IACE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;YACpB,KAAK,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC;YAClB,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC;YACrB,CAAC,CAAC,KAAK,YAAY,kBAAO,CAAC,EAC3B,CAAC;YACD,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,UAAU,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;YAC7D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,GAAG,CAAC;QACb,CAAC;QACD,OAAO,KAAK,CAAC,gBAAgB,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IAC/D,CAAC;CACF;AA9BD,kDA8BC;AAED;;;;;;GAMG;AACH,SAAgB,aAAa,CAAC,UAA2B,EAAE,OAAgB;IACzE,IAAI,OAAO,KAAK,UAAU;QAAE,OAAO,IAAI,2BAAY,CAAC,UAAU,CAAC,CAAC;IAChE,OAAO,IAAI,mBAAmB,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;AACtD,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* litedbmodel v2 SCP — the single-JSON-param BATCH forms (createMany INSERT,
|
|
3
|
+
* updateMany UPDATE) for MySQL 8 + SQLite (epic #43 / #45).
|
|
4
|
+
*
|
|
5
|
+
* v1 emits a multi-VALUES INSERT / `VALUES ROW(…)`-join (MySQL) / `CASE WHEN` (SQLite)
|
|
6
|
+
* UPDATE, binding one param per cell. These forms instead encode the whole group's rows
|
|
7
|
+
* as ONE JSON array-of-objects param and expand it SERVER-side:
|
|
8
|
+
*
|
|
9
|
+
* MySQL INSERT `INSERT INTO t (cols) SELECT <exprs> FROM JSON_TABLE(?, '$[*]'
|
|
10
|
+
* COLUMNS(c <T> PATH '$.c', …)) jt`
|
|
11
|
+
* MySQL UPDATE `UPDATE t AS u JOIN JSON_TABLE(?, '$[*]' COLUMNS(…)) AS v(…)
|
|
12
|
+
* ON u.k = v.k SET u.c = IF(v._skip_c, u.c, v.c), …`
|
|
13
|
+
* SQLite INSERT `INSERT INTO t (cols) SELECT json_extract(value,'$.c'), … FROM json_each(?)`
|
|
14
|
+
* SQLite UPDATE `UPDATE t SET c = CASE <WHEN key THEN …> END … WHERE k IN
|
|
15
|
+
* (SELECT json_extract(value,'$.k') FROM json_each(?))` — one json param.
|
|
16
|
+
*
|
|
17
|
+
* ONE JSON param per group; the SQL text is STATIC (no per-row placeholder explosion).
|
|
18
|
+
* Correctness bar is RESULT PARITY vs v1, proven on real MySQL 8 + SQLite. PostgreSQL is
|
|
19
|
+
* untouched (UNNEST). A DBToken value (e.g. `NOW()`) cannot be JSON-encoded, so a group
|
|
20
|
+
* carrying one falls back to v1's builder for that group (rare; keeps correctness).
|
|
21
|
+
*/
|
|
22
|
+
import type { MakeSQL } from './makesql';
|
|
23
|
+
/** MySQL/SQLite batch dialects. */
|
|
24
|
+
export type JsonArrayDialect = 'mysql' | 'sqlite';
|
|
25
|
+
/** True if any row carries a DBToken value (can't be JSON-encoded → v1 fallback). */
|
|
26
|
+
export declare function rowsHaveDbToken(rows: Record<string, unknown>[], columns: string[]): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Infer the MySQL `JSON_TABLE` COLUMNS type + SELECT expression for a column, from the
|
|
29
|
+
* first non-null serialized value in the group. Chosen so the extracted value coerces
|
|
30
|
+
* to the target column identically to v1's bound `?`:
|
|
31
|
+
*
|
|
32
|
+
* - boolean / integer / bigint → `BIGINT` (JSON true/false → 1/0; ints exact),
|
|
33
|
+
* - non-integer number → `DECIMAL(65,30)` (lossless),
|
|
34
|
+
* - object / array → `JSON` kept as JSON (json columns),
|
|
35
|
+
* - string / null / other → `JSON`, then `JSON_UNQUOTE(...)` in the SELECT so
|
|
36
|
+
* arbitrary-length text (beyond CHAR's 255 cap) and JSON-null → SQL NULL work.
|
|
37
|
+
*
|
|
38
|
+
* Returns `{ colType, selectExpr }` where `selectExpr` is applied to the JSON_TABLE
|
|
39
|
+
* output column reference (e.g. `jt.name`).
|
|
40
|
+
*/
|
|
41
|
+
export declare function mysqlJsonTableColumn(col: string, rows: Record<string, unknown>[]): {
|
|
42
|
+
colType: string;
|
|
43
|
+
selectExpr: (ref: string) => string;
|
|
44
|
+
};
|
|
45
|
+
export interface JsonInsertOptions {
|
|
46
|
+
tableName: string;
|
|
47
|
+
columns: string[];
|
|
48
|
+
records: Record<string, unknown>[];
|
|
49
|
+
onConflict?: string[];
|
|
50
|
+
onConflictIgnore?: boolean;
|
|
51
|
+
onConflictUpdate?: 'all' | string[];
|
|
52
|
+
returning?: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Build the MySQL single-JSON-param batch INSERT. `INSERT [IGNORE] INTO t (cols)
|
|
56
|
+
* SELECT <typed exprs> FROM JSON_TABLE(?, '$[*]' COLUMNS(…)) jt [ON DUPLICATE KEY …]`.
|
|
57
|
+
*/
|
|
58
|
+
export declare function mysqlInsertJson(opts: JsonInsertOptions): MakeSQL;
|
|
59
|
+
/**
|
|
60
|
+
* Build the SQLite single-JSON-param batch INSERT. `INSERT [OR IGNORE] INTO t (cols)
|
|
61
|
+
* SELECT json_extract(value,'$.c'), … FROM json_each(?) [ON CONFLICT … DO …]`.
|
|
62
|
+
* `json_extract` returns each cell's natural type; SQLite affinity coerces on insert.
|
|
63
|
+
*/
|
|
64
|
+
export declare function sqliteInsertJson(opts: JsonInsertOptions): MakeSQL;
|
|
65
|
+
export interface JsonUpdateManyOptions {
|
|
66
|
+
tableName: string;
|
|
67
|
+
keyColumns: string[];
|
|
68
|
+
updateColumns: string[];
|
|
69
|
+
records: Record<string, unknown>[];
|
|
70
|
+
/** record-index → set of SKIP column names (leave DB value unchanged for those). */
|
|
71
|
+
skipMap?: Map<number, Set<string>>;
|
|
72
|
+
returning?: string;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Build the MySQL single-JSON-param batch UPDATE: JSON_TABLE join with the SKIP-column
|
|
76
|
+
* `IF(v._skip_c, u.c, v.c)` logic preserved. Each row's `_skip_c` flag is carried inside
|
|
77
|
+
* the ONE JSON param (as `0`/`1`), so the SQL text stays static.
|
|
78
|
+
*
|
|
79
|
+
* UPDATE t AS u JOIN JSON_TABLE(?, '$[*]' COLUMNS(k <T> PATH '$.k', c <T> PATH '$.c',
|
|
80
|
+
* _skip_c BIGINT PATH '$._skip_c', …)) AS v ON u.k = v.k
|
|
81
|
+
* SET u.c = IF(v._skip_c, u.c, v.c), …
|
|
82
|
+
*/
|
|
83
|
+
export declare function mysqlUpdateManyJson(opts: JsonUpdateManyOptions): MakeSQL;
|
|
84
|
+
/**
|
|
85
|
+
* Build the SQLite batch UPDATE over `json_each(?)`, SKIP preserved. A single JSON
|
|
86
|
+
* array-of-objects (keys + update values + `_skip_c` flags) carries every row.
|
|
87
|
+
*
|
|
88
|
+
* NOTE on params: the SAME one JSON string is bound to EACH `?` — one per update column's
|
|
89
|
+
* correlated sub-SELECT plus one for the WHERE — so `params` has `updateColumns.length + 1`
|
|
90
|
+
* entries, ALL equal to that single JSON value. The row COUNT never multiplies the param
|
|
91
|
+
* count (the N-placeholder explosion is what this replaces); the repetition is only across
|
|
92
|
+
* the (fixed) number of SET columns, and the SQL text stays static regardless of row count.
|
|
93
|
+
*
|
|
94
|
+
* Each update column becomes a correlated lookup into the json array by matching key(s):
|
|
95
|
+
* `c = (SELECT CASE WHEN json_extract(je.value,'$._skip_c') THEN <table>.c
|
|
96
|
+
* ELSE json_extract(je.value,'$.c') END
|
|
97
|
+
* FROM json_each(?) je
|
|
98
|
+
* WHERE json_extract(je.value,'$.k') = <table>.k [AND …] LIMIT 1)`
|
|
99
|
+
* and the WHERE clause restricts affected rows to the keys present in the json array.
|
|
100
|
+
*/
|
|
101
|
+
export declare function sqliteUpdateManyJson(opts: JsonUpdateManyOptions): MakeSQL;
|
|
102
|
+
//# sourceMappingURL=json-batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-batch.d.ts","sourceRoot":"","sources":["../../../src/scp/makesql/json-batch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEzC,mCAAmC;AACnC,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAElD,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,OAAO,CAO3F;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAC9B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;CAAE,CAuB1D;AAsBD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gBAAgB,CAAC,EAAE,KAAK,GAAG,MAAM,EAAE,CAAC;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CA0BhE;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAkBjE;AAMD,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IACnC,oFAAoF;IACpF,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CA6CxE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CA4CzE"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* litedbmodel v2 SCP — the single-JSON-param BATCH forms (createMany INSERT,
|
|
4
|
+
* updateMany UPDATE) for MySQL 8 + SQLite (epic #43 / #45).
|
|
5
|
+
*
|
|
6
|
+
* v1 emits a multi-VALUES INSERT / `VALUES ROW(…)`-join (MySQL) / `CASE WHEN` (SQLite)
|
|
7
|
+
* UPDATE, binding one param per cell. These forms instead encode the whole group's rows
|
|
8
|
+
* as ONE JSON array-of-objects param and expand it SERVER-side:
|
|
9
|
+
*
|
|
10
|
+
* MySQL INSERT `INSERT INTO t (cols) SELECT <exprs> FROM JSON_TABLE(?, '$[*]'
|
|
11
|
+
* COLUMNS(c <T> PATH '$.c', …)) jt`
|
|
12
|
+
* MySQL UPDATE `UPDATE t AS u JOIN JSON_TABLE(?, '$[*]' COLUMNS(…)) AS v(…)
|
|
13
|
+
* ON u.k = v.k SET u.c = IF(v._skip_c, u.c, v.c), …`
|
|
14
|
+
* SQLite INSERT `INSERT INTO t (cols) SELECT json_extract(value,'$.c'), … FROM json_each(?)`
|
|
15
|
+
* SQLite UPDATE `UPDATE t SET c = CASE <WHEN key THEN …> END … WHERE k IN
|
|
16
|
+
* (SELECT json_extract(value,'$.k') FROM json_each(?))` — one json param.
|
|
17
|
+
*
|
|
18
|
+
* ONE JSON param per group; the SQL text is STATIC (no per-row placeholder explosion).
|
|
19
|
+
* Correctness bar is RESULT PARITY vs v1, proven on real MySQL 8 + SQLite. PostgreSQL is
|
|
20
|
+
* untouched (UNNEST). A DBToken value (e.g. `NOW()`) cannot be JSON-encoded, so a group
|
|
21
|
+
* carrying one falls back to v1's builder for that group (rare; keeps correctness).
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.rowsHaveDbToken = rowsHaveDbToken;
|
|
25
|
+
exports.mysqlJsonTableColumn = mysqlJsonTableColumn;
|
|
26
|
+
exports.mysqlInsertJson = mysqlInsertJson;
|
|
27
|
+
exports.sqliteInsertJson = sqliteInsertJson;
|
|
28
|
+
exports.mysqlUpdateManyJson = mysqlUpdateManyJson;
|
|
29
|
+
exports.sqliteUpdateManyJson = sqliteUpdateManyJson;
|
|
30
|
+
const DBValues_1 = require("../../DBValues");
|
|
31
|
+
/** True if any row carries a DBToken value (can't be JSON-encoded → v1 fallback). */
|
|
32
|
+
function rowsHaveDbToken(rows, columns) {
|
|
33
|
+
for (const r of rows) {
|
|
34
|
+
for (const c of columns) {
|
|
35
|
+
if (r[c] instanceof DBValues_1.DBToken)
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Infer the MySQL `JSON_TABLE` COLUMNS type + SELECT expression for a column, from the
|
|
43
|
+
* first non-null serialized value in the group. Chosen so the extracted value coerces
|
|
44
|
+
* to the target column identically to v1's bound `?`:
|
|
45
|
+
*
|
|
46
|
+
* - boolean / integer / bigint → `BIGINT` (JSON true/false → 1/0; ints exact),
|
|
47
|
+
* - non-integer number → `DECIMAL(65,30)` (lossless),
|
|
48
|
+
* - object / array → `JSON` kept as JSON (json columns),
|
|
49
|
+
* - string / null / other → `JSON`, then `JSON_UNQUOTE(...)` in the SELECT so
|
|
50
|
+
* arbitrary-length text (beyond CHAR's 255 cap) and JSON-null → SQL NULL work.
|
|
51
|
+
*
|
|
52
|
+
* Returns `{ colType, selectExpr }` where `selectExpr` is applied to the JSON_TABLE
|
|
53
|
+
* output column reference (e.g. `jt.name`).
|
|
54
|
+
*/
|
|
55
|
+
function mysqlJsonTableColumn(col, rows) {
|
|
56
|
+
let sample;
|
|
57
|
+
for (const r of rows) {
|
|
58
|
+
const v = r[col];
|
|
59
|
+
if (v !== null && v !== undefined) {
|
|
60
|
+
sample = v;
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (typeof sample === 'boolean' || typeof sample === 'bigint') {
|
|
65
|
+
return { colType: 'BIGINT', selectExpr: (ref) => ref };
|
|
66
|
+
}
|
|
67
|
+
if (typeof sample === 'number') {
|
|
68
|
+
return Number.isInteger(sample)
|
|
69
|
+
? { colType: 'BIGINT', selectExpr: (ref) => ref }
|
|
70
|
+
: { colType: 'DECIMAL(65,30)', selectExpr: (ref) => ref };
|
|
71
|
+
}
|
|
72
|
+
if (sample !== null && typeof sample === 'object') {
|
|
73
|
+
// Object/array serialized value (e.g. a json column) — keep as JSON.
|
|
74
|
+
return { colType: 'JSON', selectExpr: (ref) => ref };
|
|
75
|
+
}
|
|
76
|
+
// string, null, date-as-string, json-string, or all-null column → text via JSON_UNQUOTE.
|
|
77
|
+
return { colType: 'JSON', selectExpr: (ref) => `JSON_UNQUOTE(${ref})` };
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Serialize a group's rows to a JSON array-of-objects string, keeping only `columns`.
|
|
81
|
+
* A JSON-string value produced by v1's serializer (e.g. a `@column.json()` column whose
|
|
82
|
+
* serializer already `JSON.stringify`'d it) is passed through as-is — it becomes a JSON
|
|
83
|
+
* string element, matching what v1 binds. `undefined` cells are dropped (the group is
|
|
84
|
+
* homogeneous by construction, so every column is present in every row).
|
|
85
|
+
*/
|
|
86
|
+
function rowsToJson(rows, columns) {
|
|
87
|
+
const objects = rows.map((r) => {
|
|
88
|
+
const o = {};
|
|
89
|
+
for (const c of columns)
|
|
90
|
+
o[c] = r[c] === undefined ? null : r[c];
|
|
91
|
+
return o;
|
|
92
|
+
});
|
|
93
|
+
return JSON.stringify(objects);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Build the MySQL single-JSON-param batch INSERT. `INSERT [IGNORE] INTO t (cols)
|
|
97
|
+
* SELECT <typed exprs> FROM JSON_TABLE(?, '$[*]' COLUMNS(…)) jt [ON DUPLICATE KEY …]`.
|
|
98
|
+
*/
|
|
99
|
+
function mysqlInsertJson(opts) {
|
|
100
|
+
const { tableName, columns, records, onConflict, onConflictIgnore, onConflictUpdate, returning } = opts;
|
|
101
|
+
const jtCols = [];
|
|
102
|
+
const selectExprs = [];
|
|
103
|
+
for (const col of columns) {
|
|
104
|
+
const { colType, selectExpr } = mysqlJsonTableColumn(col, records);
|
|
105
|
+
jtCols.push(`${col} ${colType} PATH '$.${col}'`);
|
|
106
|
+
selectExprs.push(selectExpr(`jt.${col}`));
|
|
107
|
+
}
|
|
108
|
+
const jsonParam = rowsToJson(records, columns);
|
|
109
|
+
const source = `SELECT ${selectExprs.join(', ')} ` +
|
|
110
|
+
`FROM JSON_TABLE(?, '$[*]' COLUMNS(${jtCols.join(', ')})) jt`;
|
|
111
|
+
let sql;
|
|
112
|
+
if (onConflict && onConflictIgnore) {
|
|
113
|
+
sql = `INSERT IGNORE INTO ${tableName} (${columns.join(', ')}) ${source}`;
|
|
114
|
+
}
|
|
115
|
+
else if (onConflict && onConflictUpdate) {
|
|
116
|
+
const updateCols = onConflictUpdate === 'all' ? columns : onConflictUpdate;
|
|
117
|
+
const updateClauses = updateCols.map((c) => `${c} = VALUES(${c})`);
|
|
118
|
+
sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ${source} ON DUPLICATE KEY UPDATE ${updateClauses.join(', ')}`;
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ${source}`;
|
|
122
|
+
}
|
|
123
|
+
if (returning)
|
|
124
|
+
sql += ` RETURNING ${returning}`;
|
|
125
|
+
return { sql, params: [jsonParam] };
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build the SQLite single-JSON-param batch INSERT. `INSERT [OR IGNORE] INTO t (cols)
|
|
129
|
+
* SELECT json_extract(value,'$.c'), … FROM json_each(?) [ON CONFLICT … DO …]`.
|
|
130
|
+
* `json_extract` returns each cell's natural type; SQLite affinity coerces on insert.
|
|
131
|
+
*/
|
|
132
|
+
function sqliteInsertJson(opts) {
|
|
133
|
+
const { tableName, columns, records, onConflict, onConflictIgnore, onConflictUpdate, returning } = opts;
|
|
134
|
+
const selectExprs = columns.map((c) => `json_extract(value, '$.${c}')`);
|
|
135
|
+
const jsonParam = rowsToJson(records, columns);
|
|
136
|
+
const source = `SELECT ${selectExprs.join(', ')} FROM json_each(?)`;
|
|
137
|
+
let sql;
|
|
138
|
+
if (onConflict && onConflictIgnore) {
|
|
139
|
+
sql = `INSERT OR IGNORE INTO ${tableName} (${columns.join(', ')}) ${source}`;
|
|
140
|
+
}
|
|
141
|
+
else if (onConflict && onConflictUpdate) {
|
|
142
|
+
const updateCols = onConflictUpdate === 'all' ? columns : onConflictUpdate;
|
|
143
|
+
const updateClauses = updateCols.map((c) => `${c} = excluded.${c}`);
|
|
144
|
+
sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ${source} ON CONFLICT (${onConflict.join(', ')}) DO UPDATE SET ${updateClauses.join(', ')}`;
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
sql = `INSERT INTO ${tableName} (${columns.join(', ')}) ${source}`;
|
|
148
|
+
}
|
|
149
|
+
if (returning)
|
|
150
|
+
sql += ` RETURNING ${returning}`;
|
|
151
|
+
return { sql, params: [jsonParam] };
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Build the MySQL single-JSON-param batch UPDATE: JSON_TABLE join with the SKIP-column
|
|
155
|
+
* `IF(v._skip_c, u.c, v.c)` logic preserved. Each row's `_skip_c` flag is carried inside
|
|
156
|
+
* the ONE JSON param (as `0`/`1`), so the SQL text stays static.
|
|
157
|
+
*
|
|
158
|
+
* UPDATE t AS u JOIN JSON_TABLE(?, '$[*]' COLUMNS(k <T> PATH '$.k', c <T> PATH '$.c',
|
|
159
|
+
* _skip_c BIGINT PATH '$._skip_c', …)) AS v ON u.k = v.k
|
|
160
|
+
* SET u.c = IF(v._skip_c, u.c, v.c), …
|
|
161
|
+
*/
|
|
162
|
+
function mysqlUpdateManyJson(opts) {
|
|
163
|
+
const { tableName, keyColumns, updateColumns, records, skipMap = new Map(), returning } = opts;
|
|
164
|
+
const allColumns = [...keyColumns, ...updateColumns];
|
|
165
|
+
const skipColumnsNeeded = new Set();
|
|
166
|
+
for (const s of skipMap.values())
|
|
167
|
+
for (const c of s)
|
|
168
|
+
skipColumnsNeeded.add(c);
|
|
169
|
+
// Build the JSON payload: each row's data columns + the _skip_ flags (0/1).
|
|
170
|
+
const skipCols = [...skipColumnsNeeded];
|
|
171
|
+
const payload = records.map((r, i) => {
|
|
172
|
+
const o = {};
|
|
173
|
+
for (const c of allColumns)
|
|
174
|
+
o[c] = r[c] === undefined ? null : r[c];
|
|
175
|
+
for (const c of skipCols)
|
|
176
|
+
o[`_skip_${c}`] = skipMap.get(i)?.has(c) ? 1 : 0;
|
|
177
|
+
return o;
|
|
178
|
+
});
|
|
179
|
+
const jsonParam = JSON.stringify(payload);
|
|
180
|
+
const jtCols = [];
|
|
181
|
+
const selectExprOf = new Map();
|
|
182
|
+
for (const col of allColumns) {
|
|
183
|
+
const { colType, selectExpr } = mysqlJsonTableColumn(col, records);
|
|
184
|
+
jtCols.push(`${col} ${colType} PATH '$.${col}'`);
|
|
185
|
+
selectExprOf.set(col, selectExpr);
|
|
186
|
+
}
|
|
187
|
+
for (const c of skipCols)
|
|
188
|
+
jtCols.push(`_skip_${c} BIGINT PATH '$._skip_${c}'`);
|
|
189
|
+
const setClauses = updateColumns.map((col) => {
|
|
190
|
+
const vExpr = selectExprOf.get(col)(`v.${col}`);
|
|
191
|
+
return skipColumnsNeeded.has(col)
|
|
192
|
+
? `u.${col} = IF(v._skip_${col}, u.${col}, ${vExpr})`
|
|
193
|
+
: `u.${col} = ${vExpr}`;
|
|
194
|
+
});
|
|
195
|
+
const onConditions = keyColumns.map((k) => `u.${k} = ${selectExprOf.get(k)(`v.${k}`)}`);
|
|
196
|
+
// Target aliased `u`, JSON_TABLE derived rows aliased `v`; the two aliases only need to
|
|
197
|
+
// differ from each other so `u.col` / `v.col` are unambiguous. (v1's MySQL builder used
|
|
198
|
+
// `AS t`; `u` here just keeps the target distinct from the `v` derived table — not a
|
|
199
|
+
// correctness requirement, purely disambiguation.)
|
|
200
|
+
let sql = `UPDATE ${tableName} AS u ` +
|
|
201
|
+
`JOIN JSON_TABLE(?, '$[*]' COLUMNS(${jtCols.join(', ')})) AS v ` +
|
|
202
|
+
`ON ${onConditions.join(' AND ')} ` +
|
|
203
|
+
`SET ${setClauses.join(', ')}`;
|
|
204
|
+
if (returning)
|
|
205
|
+
sql += ` RETURNING ${returning}`;
|
|
206
|
+
return { sql, params: [jsonParam] };
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Build the SQLite batch UPDATE over `json_each(?)`, SKIP preserved. A single JSON
|
|
210
|
+
* array-of-objects (keys + update values + `_skip_c` flags) carries every row.
|
|
211
|
+
*
|
|
212
|
+
* NOTE on params: the SAME one JSON string is bound to EACH `?` — one per update column's
|
|
213
|
+
* correlated sub-SELECT plus one for the WHERE — so `params` has `updateColumns.length + 1`
|
|
214
|
+
* entries, ALL equal to that single JSON value. The row COUNT never multiplies the param
|
|
215
|
+
* count (the N-placeholder explosion is what this replaces); the repetition is only across
|
|
216
|
+
* the (fixed) number of SET columns, and the SQL text stays static regardless of row count.
|
|
217
|
+
*
|
|
218
|
+
* Each update column becomes a correlated lookup into the json array by matching key(s):
|
|
219
|
+
* `c = (SELECT CASE WHEN json_extract(je.value,'$._skip_c') THEN <table>.c
|
|
220
|
+
* ELSE json_extract(je.value,'$.c') END
|
|
221
|
+
* FROM json_each(?) je
|
|
222
|
+
* WHERE json_extract(je.value,'$.k') = <table>.k [AND …] LIMIT 1)`
|
|
223
|
+
* and the WHERE clause restricts affected rows to the keys present in the json array.
|
|
224
|
+
*/
|
|
225
|
+
function sqliteUpdateManyJson(opts) {
|
|
226
|
+
const { tableName, keyColumns, updateColumns, records, skipMap = new Map(), returning } = opts;
|
|
227
|
+
const allColumns = [...keyColumns, ...updateColumns];
|
|
228
|
+
const skipColumnsNeeded = new Set();
|
|
229
|
+
for (const s of skipMap.values())
|
|
230
|
+
for (const c of s)
|
|
231
|
+
skipColumnsNeeded.add(c);
|
|
232
|
+
const skipCols = [...skipColumnsNeeded];
|
|
233
|
+
const payload = records.map((r, i) => {
|
|
234
|
+
const o = {};
|
|
235
|
+
for (const c of allColumns)
|
|
236
|
+
o[c] = r[c] === undefined ? null : r[c];
|
|
237
|
+
for (const c of skipCols)
|
|
238
|
+
o[`_skip_${c}`] = skipMap.get(i)?.has(c) ? 1 : 0;
|
|
239
|
+
return o;
|
|
240
|
+
});
|
|
241
|
+
const jsonParam = JSON.stringify(payload);
|
|
242
|
+
const params = [];
|
|
243
|
+
const keyMatch = keyColumns.map((k) => `json_extract(je.value, '$.${k}') = ${tableName}.${k}`).join(' AND ');
|
|
244
|
+
const setClauses = updateColumns.map((col) => {
|
|
245
|
+
params.push(jsonParam);
|
|
246
|
+
const valueExpr = skipColumnsNeeded.has(col)
|
|
247
|
+
? `CASE WHEN json_extract(je.value, '$._skip_${col}') THEN ${tableName}.${col} ELSE json_extract(je.value, '$.${col}') END`
|
|
248
|
+
: `json_extract(je.value, '$.${col}')`;
|
|
249
|
+
return (`${col} = (SELECT ${valueExpr} FROM json_each(?) je ` +
|
|
250
|
+
`WHERE ${keyMatch} LIMIT 1)`);
|
|
251
|
+
});
|
|
252
|
+
// WHERE: restrict to the affected keys. Single key → k IN (SELECT …); composite →
|
|
253
|
+
// EXISTS over json_each matching all key columns.
|
|
254
|
+
let whereClause;
|
|
255
|
+
if (keyColumns.length === 1) {
|
|
256
|
+
params.push(jsonParam);
|
|
257
|
+
whereClause = `${keyColumns[0]} IN (SELECT json_extract(value, '$.${keyColumns[0]}') FROM json_each(?))`;
|
|
258
|
+
}
|
|
259
|
+
else {
|
|
260
|
+
params.push(jsonParam);
|
|
261
|
+
const match = keyColumns.map((k) => `json_extract(je.value, '$.${k}') = ${tableName}.${k}`).join(' AND ');
|
|
262
|
+
whereClause = `EXISTS (SELECT 1 FROM json_each(?) je WHERE ${match})`;
|
|
263
|
+
}
|
|
264
|
+
let sql = `UPDATE ${tableName} SET ${setClauses.join(', ')} WHERE ${whereClause}`;
|
|
265
|
+
if (returning)
|
|
266
|
+
sql += ` RETURNING ${returning}`;
|
|
267
|
+
return { sql, params };
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=json-batch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-batch.js","sourceRoot":"","sources":["../../../src/scp/makesql/json-batch.ts"],"names":[],"mappings":"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"}
|