@quereus/quereus 4.13.1 → 4.15.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/src/core/statement.d.ts +30 -0
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +49 -0
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/emit/ast-stringify.d.ts.map +1 -1
- package/dist/src/emit/ast-stringify.js +0 -4
- package/dist/src/emit/ast-stringify.js.map +1 -1
- package/dist/src/func/builtins/explain.d.ts.map +1 -1
- package/dist/src/func/builtins/explain.js +89 -62
- package/dist/src/func/builtins/explain.js.map +1 -1
- package/dist/src/func/registration.d.ts +7 -0
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +7 -0
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/ast.d.ts +0 -2
- package/dist/src/parser/ast.d.ts.map +1 -1
- package/dist/src/parser/parser.d.ts +1 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +29 -28
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/mutation/capture-correlation.d.ts +57 -0
- package/dist/src/planner/mutation/capture-correlation.d.ts.map +1 -0
- package/dist/src/planner/mutation/capture-correlation.js +57 -0
- package/dist/src/planner/mutation/capture-correlation.js.map +1 -0
- package/dist/src/planner/mutation/decomposition.d.ts.map +1 -1
- package/dist/src/planner/mutation/decomposition.js +86 -23
- package/dist/src/planner/mutation/decomposition.js.map +1 -1
- package/dist/src/planner/mutation/lens-enforcement.d.ts.map +1 -1
- package/dist/src/planner/mutation/lens-enforcement.js +15 -20
- package/dist/src/planner/mutation/lens-enforcement.js.map +1 -1
- package/dist/src/planner/mutation/multi-source.d.ts +52 -11
- package/dist/src/planner/mutation/multi-source.d.ts.map +1 -1
- package/dist/src/planner/mutation/multi-source.js +273 -86
- package/dist/src/planner/mutation/multi-source.js.map +1 -1
- package/dist/src/planner/mutation/set-op.d.ts.map +1 -1
- package/dist/src/planner/mutation/set-op.js +4 -12
- package/dist/src/planner/mutation/set-op.js.map +1 -1
- package/dist/src/planner/mutation/single-source.js +1 -1
- package/dist/src/planner/mutation/single-source.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +8 -10
- package/dist/src/runtime/emit/alter-table.js.map +1 -1
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +23 -0
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts +16 -4
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.js +182 -25
- package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view.js +1 -1
- package/dist/src/runtime/emit/materialized-view.js.map +1 -1
- package/dist/src/runtime/emit/scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/scan.js +52 -9
- package/dist/src/runtime/emit/scan.js.map +1 -1
- package/dist/src/runtime/emit/set-object-tags.js +4 -4
- package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
- package/dist/src/runtime/emitters.d.ts.map +1 -1
- package/dist/src/runtime/emitters.js +6 -0
- package/dist/src/runtime/emitters.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +5 -0
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
- package/dist/src/runtime/parallel-driver.js +6 -0
- package/dist/src/runtime/parallel-driver.js.map +1 -1
- package/dist/src/runtime/scheduler.d.ts +9 -0
- package/dist/src/runtime/scheduler.d.ts.map +1 -1
- package/dist/src/runtime/scheduler.js +27 -11
- package/dist/src/runtime/scheduler.js.map +1 -1
- package/dist/src/runtime/types.d.ts +19 -0
- package/dist/src/runtime/types.d.ts.map +1 -1
- package/dist/src/runtime/types.js.map +1 -1
- package/dist/src/runtime/work-counters.d.ts +180 -0
- package/dist/src/runtime/work-counters.d.ts.map +1 -0
- package/dist/src/runtime/work-counters.js +210 -0
- package/dist/src/runtime/work-counters.js.map +1 -0
- package/dist/src/schema/ddl-generator.d.ts.map +1 -1
- package/dist/src/schema/ddl-generator.js +65 -24
- package/dist/src/schema/ddl-generator.js.map +1 -1
- package/dist/src/schema/lens-ack.d.ts +1 -1
- package/dist/src/schema/lens-ack.js +2 -2
- package/dist/src/schema/lens-compiler.d.ts.map +1 -1
- package/dist/src/schema/lens-compiler.js +63 -34
- package/dist/src/schema/lens-compiler.js.map +1 -1
- package/dist/src/schema/lens-prover.d.ts +4 -4
- package/dist/src/schema/lens-prover.d.ts.map +1 -1
- package/dist/src/schema/lens-prover.js +13 -5
- package/dist/src/schema/lens-prover.js.map +1 -1
- package/dist/src/schema/lens.d.ts +1 -1
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +25 -13
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/reserved-tags.js +1 -1
- package/dist/src/schema/reserved-tags.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +4 -3
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/table.d.ts +41 -34
- package/dist/src/schema/table.d.ts.map +1 -1
- package/dist/src/schema/table.js +24 -43
- package/dist/src/schema/table.js.map +1 -1
- package/dist/src/vtab/alter-event-shape.d.ts +35 -0
- package/dist/src/vtab/alter-event-shape.d.ts.map +1 -0
- package/dist/src/vtab/alter-event-shape.js +47 -0
- package/dist/src/vtab/alter-event-shape.js.map +1 -0
- package/dist/src/vtab/memory/layer/alter-column.d.ts +8 -1
- package/dist/src/vtab/memory/layer/alter-column.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/alter-column.js +20 -10
- package/dist/src/vtab/memory/layer/alter-column.js.map +1 -1
- package/dist/src/vtab/memory/layer/base.d.ts +9 -1
- package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base.js +12 -1
- package/dist/src/vtab/memory/layer/base.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +23 -18
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +44 -33
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/memory/layer/row-convert.d.ts +13 -0
- package/dist/src/vtab/memory/layer/row-convert.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/row-convert.js +24 -0
- package/dist/src/vtab/memory/layer/row-convert.js.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.d.ts +21 -7
- package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.js +30 -14
- package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts +0 -8
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +2 -25
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/module.d.ts +20 -2
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type * as AST from '../../parser/ast.js';
|
|
2
|
+
import type { TableSchema } from '../../schema/table.js';
|
|
3
|
+
/**
|
|
4
|
+
* One identifying key column of a capture correlation, with its **declared**
|
|
5
|
+
* nullability. Key membership does not imply NOT NULL (docs/schema.md
|
|
6
|
+
* § Primary-key nullability), and key comparison treats NULL as a value equal to
|
|
7
|
+
* itself — so a capture reader correlating on a nullable key column must use the
|
|
8
|
+
* NULL-safe equality {@link captureKeyEquality} emits, or a row keyed NULL is
|
|
9
|
+
* silently unaddressable.
|
|
10
|
+
*/
|
|
11
|
+
export interface KeyColumnInfo {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** True when the column's declared type admits NULL (`ColumnSchema.notNull === false`). */
|
|
14
|
+
readonly nullable: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Look one correlation key column up on its base schema (case-insensitively). An
|
|
18
|
+
* unknown column (defensive — every caller resolves the name off validated
|
|
19
|
+
* schema/advertisement metadata) reads as **nullable**, so a correlation over it
|
|
20
|
+
* falls back to the NULL-safe form: semantically identical for a NOT NULL column,
|
|
21
|
+
* merely less index-friendly. The one place declared nullability is read, so the
|
|
22
|
+
* decomposition write fan-out and the lens read body cannot drift apart on the
|
|
23
|
+
* gate that decides {@link captureKeyEquality}'s shape.
|
|
24
|
+
*/
|
|
25
|
+
export declare function keyColumnInfo(schema: TableSchema, name: string): KeyColumnInfo;
|
|
26
|
+
/**
|
|
27
|
+
* Per-key-column equality for correlating two spellings of one key value — a
|
|
28
|
+
* capture (`__vmupd_keys`) reader to its target row, and equally a decomposition
|
|
29
|
+
* lens's stitch-key correlations (the get body's `anchor ⋈ member` ON condition
|
|
30
|
+
* and EAV entity subquery in `schema/lens-compiler.ts`, and the write fan-out's
|
|
31
|
+
* anchor EXISTS correlation in `decomposition.ts`):
|
|
32
|
+
*
|
|
33
|
+
* ```
|
|
34
|
+
* left = right -- column declared NOT NULL
|
|
35
|
+
* left = right or (left is null and right is null) -- column declared nullable
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* The nullability gate exists because SQL `=` yields UNKNOWN when either operand
|
|
39
|
+
* is NULL: a nullable key column needs the NULL-safe disjunction (NULL is a
|
|
40
|
+
* self-equal key value), while a NOT NULL column keeps the plain `=`, which
|
|
41
|
+
* stays index-friendly where a disjunction can cost the engine a seek. Sibling
|
|
42
|
+
* implementation: `planner/analysis/key-filter.ts` emits the same per-column
|
|
43
|
+
* shape for residual key filters (as plan nodes rather than AST).
|
|
44
|
+
*
|
|
45
|
+
* Operands are cloned into each emitted position, so a caller may pass the same
|
|
46
|
+
* node it uses elsewhere without sharing AST subtrees.
|
|
47
|
+
*
|
|
48
|
+
* NOTE: the gate reads the **declared** nullability, so a database run under
|
|
49
|
+
* `pragma default_column_nullability = nullable` declares every key column nullable
|
|
50
|
+
* and every capture correlation takes the disjunction — including the ones a NOT NULL
|
|
51
|
+
* default would have left as a seekable `=`. Correct either way, but if view writes
|
|
52
|
+
* under that pragma ever show up as slow, narrow the gate to the *reachable* nullability
|
|
53
|
+
* (a key column with a NOT NULL check, or one the body's predicate already excludes NULL
|
|
54
|
+
* on) rather than the declared one.
|
|
55
|
+
*/
|
|
56
|
+
export declare function captureKeyEquality(left: AST.Expression, right: AST.Expression, nullable: boolean): AST.Expression;
|
|
57
|
+
//# sourceMappingURL=capture-correlation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-correlation.d.ts","sourceRoot":"","sources":["../../../../src/planner/mutation/capture-correlation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAGzD;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CAG9E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,GAAG,CAAC,UAAU,EAAE,QAAQ,EAAE,OAAO,GAAG,GAAG,CAAC,UAAU,CAUjH"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { cloneExpr } from './scope-transform.js';
|
|
2
|
+
/**
|
|
3
|
+
* Look one correlation key column up on its base schema (case-insensitively). An
|
|
4
|
+
* unknown column (defensive — every caller resolves the name off validated
|
|
5
|
+
* schema/advertisement metadata) reads as **nullable**, so a correlation over it
|
|
6
|
+
* falls back to the NULL-safe form: semantically identical for a NOT NULL column,
|
|
7
|
+
* merely less index-friendly. The one place declared nullability is read, so the
|
|
8
|
+
* decomposition write fan-out and the lens read body cannot drift apart on the
|
|
9
|
+
* gate that decides {@link captureKeyEquality}'s shape.
|
|
10
|
+
*/
|
|
11
|
+
export function keyColumnInfo(schema, name) {
|
|
12
|
+
const col = schema.columns.find(c => c.name.toLowerCase() === name.toLowerCase());
|
|
13
|
+
return { name, nullable: !col || !col.notNull };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Per-key-column equality for correlating two spellings of one key value — a
|
|
17
|
+
* capture (`__vmupd_keys`) reader to its target row, and equally a decomposition
|
|
18
|
+
* lens's stitch-key correlations (the get body's `anchor ⋈ member` ON condition
|
|
19
|
+
* and EAV entity subquery in `schema/lens-compiler.ts`, and the write fan-out's
|
|
20
|
+
* anchor EXISTS correlation in `decomposition.ts`):
|
|
21
|
+
*
|
|
22
|
+
* ```
|
|
23
|
+
* left = right -- column declared NOT NULL
|
|
24
|
+
* left = right or (left is null and right is null) -- column declared nullable
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* The nullability gate exists because SQL `=` yields UNKNOWN when either operand
|
|
28
|
+
* is NULL: a nullable key column needs the NULL-safe disjunction (NULL is a
|
|
29
|
+
* self-equal key value), while a NOT NULL column keeps the plain `=`, which
|
|
30
|
+
* stays index-friendly where a disjunction can cost the engine a seek. Sibling
|
|
31
|
+
* implementation: `planner/analysis/key-filter.ts` emits the same per-column
|
|
32
|
+
* shape for residual key filters (as plan nodes rather than AST).
|
|
33
|
+
*
|
|
34
|
+
* Operands are cloned into each emitted position, so a caller may pass the same
|
|
35
|
+
* node it uses elsewhere without sharing AST subtrees.
|
|
36
|
+
*
|
|
37
|
+
* NOTE: the gate reads the **declared** nullability, so a database run under
|
|
38
|
+
* `pragma default_column_nullability = nullable` declares every key column nullable
|
|
39
|
+
* and every capture correlation takes the disjunction — including the ones a NOT NULL
|
|
40
|
+
* default would have left as a seekable `=`. Correct either way, but if view writes
|
|
41
|
+
* under that pragma ever show up as slow, narrow the gate to the *reachable* nullability
|
|
42
|
+
* (a key column with a NOT NULL check, or one the body's predicate already excludes NULL
|
|
43
|
+
* on) rather than the declared one.
|
|
44
|
+
*/
|
|
45
|
+
export function captureKeyEquality(left, right, nullable) {
|
|
46
|
+
const eq = { type: 'binary', operator: '=', left: cloneExpr(left), right: cloneExpr(right) };
|
|
47
|
+
if (!nullable)
|
|
48
|
+
return eq;
|
|
49
|
+
const bothNull = {
|
|
50
|
+
type: 'binary',
|
|
51
|
+
operator: 'AND',
|
|
52
|
+
left: { type: 'unary', operator: 'IS NULL', expr: cloneExpr(left) },
|
|
53
|
+
right: { type: 'unary', operator: 'IS NULL', expr: cloneExpr(right) },
|
|
54
|
+
};
|
|
55
|
+
return { type: 'binary', operator: 'OR', left: eq, right: bothNull };
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=capture-correlation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture-correlation.js","sourceRoot":"","sources":["../../../../src/planner/mutation/capture-correlation.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAgBjD;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,MAAmB,EAAE,IAAY;IAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;IAClF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;AACjD,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAoB,EAAE,KAAqB,EAAE,QAAiB;IAChG,MAAM,EAAE,GAAmB,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;IAC7G,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,QAAQ,GAAmB;QAChC,IAAI,EAAE,QAAQ;QACd,QAAQ,EAAE,KAAK;QACf,IAAI,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,CAAC,EAAmB;QACpF,KAAK,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,EAAmB;KACtF,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;AACtE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decomposition.d.ts","sourceRoot":"","sources":["../../../../src/planner/mutation/decomposition.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,qBAAqB,CAAC;AAChD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAIhD,OAAO,KAAK,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAC7F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAQ/E,OAAO,EAAsB,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAC7E,OAAO,EAAwC,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"decomposition.d.ts","sourceRoot":"","sources":["../../../../src/planner/mutation/decomposition.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FilterNode } from '../nodes/filter.js';
|
|
2
2
|
import { ProjectNode } from '../nodes/project-node.js';
|
|
3
3
|
import { buildTableReference } from '../building/table.js';
|
|
4
|
-
import { combineAnd } from './single-source.js';
|
|
4
|
+
import { combineAnd, SELF_ALIAS } from './single-source.js';
|
|
5
5
|
import { transformExpr, cloneExpr } from './scope-transform.js';
|
|
6
6
|
import { buildExpression } from '../building/expression.js';
|
|
7
7
|
import { columnSchemaToScalarType } from '../type-utils.js';
|
|
@@ -10,6 +10,7 @@ import { containsNonDeterministicCall } from '../analysis/check-extraction.js';
|
|
|
10
10
|
import { FunctionFlags } from '../../common/constants.js';
|
|
11
11
|
import { analyzeBodyLineage } from './backward-body.js';
|
|
12
12
|
import { keyColumnName, capturedValueSubquery } from './multi-source.js';
|
|
13
|
+
import { captureKeyEquality, keyColumnInfo } from './capture-correlation.js';
|
|
13
14
|
import { raiseMutationDiagnostic } from './mutation-diagnostic.js';
|
|
14
15
|
/**
|
|
15
16
|
* Plan the synthesized get body **once** and read its threaded `updateLineage`
|
|
@@ -435,19 +436,26 @@ function decomposeDelete(ctx, view, shape, stmt) {
|
|
|
435
436
|
* One member's delete. No predicate ⇒ an unconditional `delete from <member>`
|
|
436
437
|
* (truncate the component — also the sound singleton path, which has no key to
|
|
437
438
|
* thread). With an anchor predicate ⇒ `delete from <member> where
|
|
438
|
-
* <memberKeyOrEntity> in (select <anchorKey> from <anchor> where <pred>)
|
|
439
|
+
* <memberKeyOrEntity> in (select <anchorKey> from <anchor> where <pred>)` — or
|
|
440
|
+
* the NULL-safe correlated EXISTS with the target aliased {@link SELF_ALIAS}
|
|
441
|
+
* when both stitch columns are nullable ({@link anchorKeyCorrelation}), so a
|
|
442
|
+
* NULL-keyed component row does not survive the fan-out as an orphan.
|
|
439
443
|
*/
|
|
440
444
|
function memberDeleteOp(ctx, view, shape, member, pred, stmt) {
|
|
441
445
|
let where;
|
|
446
|
+
let targetAlias;
|
|
442
447
|
if (pred) {
|
|
443
448
|
const memberCol = member.attributePivot
|
|
444
449
|
? member.attributePivot.entityColumn // EAV: delete every triple for the matched entities
|
|
445
450
|
: singleKeyColumn(view, shape, member);
|
|
446
|
-
|
|
451
|
+
const correlation = anchorKeyCorrelation(ctx, shape, member, memberCol, pred);
|
|
452
|
+
where = correlation.where;
|
|
453
|
+
targetAlias = correlation.targetAlias;
|
|
447
454
|
}
|
|
448
455
|
const statement = {
|
|
449
456
|
type: 'delete',
|
|
450
457
|
table: memberIdentifier(member),
|
|
458
|
+
alias: targetAlias,
|
|
451
459
|
where,
|
|
452
460
|
contextValues: stmt.contextValues,
|
|
453
461
|
schemaPath: stmt.schemaPath,
|
|
@@ -754,19 +762,20 @@ function collectValueScopes(expr) {
|
|
|
754
762
|
walk(expr);
|
|
755
763
|
return { qualifiers, hasUnqualifiedColumn, hasSubquery };
|
|
756
764
|
}
|
|
757
|
-
/**
|
|
765
|
+
/**
|
|
766
|
+
* `update <member> set <cols> where <memberKey> in (select <anchorKey> from <anchor>
|
|
767
|
+
* where <pred>)` — or the NULL-safe correlated EXISTS with the target aliased
|
|
768
|
+
* {@link SELF_ALIAS} when both stitch columns are nullable ({@link anchorKeyCorrelation}).
|
|
769
|
+
*/
|
|
758
770
|
function memberUpdateOp(ctx, view, shape, member, assignments, pred, stmt) {
|
|
759
771
|
const memberKey = singleKeyColumn(view, shape, member);
|
|
760
|
-
const
|
|
761
|
-
type: 'in',
|
|
762
|
-
expr: { type: 'column', name: memberKey },
|
|
763
|
-
subquery: anchorKeySubquery(shape, pred),
|
|
764
|
-
};
|
|
772
|
+
const correlation = anchorKeyCorrelation(ctx, shape, member, memberKey, pred);
|
|
765
773
|
const statement = {
|
|
766
774
|
type: 'update',
|
|
767
775
|
table: memberIdentifier(member),
|
|
776
|
+
alias: correlation.targetAlias,
|
|
768
777
|
assignments: assignments.map(a => ({ column: a.column, value: a.value })),
|
|
769
|
-
where,
|
|
778
|
+
where: correlation.where,
|
|
770
779
|
contextValues: stmt.contextValues,
|
|
771
780
|
schemaPath: stmt.schemaPath,
|
|
772
781
|
loc: stmt.loc,
|
|
@@ -941,7 +950,7 @@ function emitCapturedMemberUpdate(ctx, view, shape, member, cells, pred, stmt, o
|
|
|
941
950
|
// basis column so the two read-back sites (member key, anchor key) bind the same alias.
|
|
942
951
|
const srcByCell = cells.map(c => registerCapturedExpr(`cap:${member.relationId.toLowerCase()}:${c.basisColumn.toLowerCase()}`, cloneExpr(c.value)));
|
|
943
952
|
// Matched UPDATE (unfiltered over the present rows): each value read back by the member key.
|
|
944
|
-
ops.push(memberUpdateOp(ctx, view, shape, member, cells.map((c, i) => ({ column: c.basisColumn, value: capturedValueSubquery(srcByCell[i], 0, [memberKey]) })), pred, stmt));
|
|
953
|
+
ops.push(memberUpdateOp(ctx, view, shape, member, cells.map((c, i) => ({ column: c.basisColumn, value: capturedValueSubquery(srcByCell[i], 0, [keyColumnInfo(schema, memberKey)]) })), pred, stmt));
|
|
945
954
|
// Materialize INSERT (filtered over the absent rows): each value read back by the anchor key.
|
|
946
955
|
ops.push(buildCapturedMaterializeInsert(ctx, view, shape, member, cells, srcByCell, pred, stmt));
|
|
947
956
|
}
|
|
@@ -963,6 +972,7 @@ function buildCapturedMaterializeInsert(ctx, view, shape, member, cells, srcByCe
|
|
|
963
972
|
const ref = resolveMemberTable(ctx, member);
|
|
964
973
|
const schema = ref.tableSchema;
|
|
965
974
|
const anchorKey = singleKeyColumn(view, shape, shape.anchor);
|
|
975
|
+
const anchorKeyInfo = keyColumnInfo(resolveMemberTable(ctx, shape.anchor).tableSchema, anchorKey);
|
|
966
976
|
const memberKey = singleKeyColumn(view, shape, member);
|
|
967
977
|
const targetColumns = [memberKey];
|
|
968
978
|
const projections = [
|
|
@@ -970,14 +980,14 @@ function buildCapturedMaterializeInsert(ctx, view, shape, member, cells, srcByCe
|
|
|
970
980
|
];
|
|
971
981
|
cells.forEach((c, i) => {
|
|
972
982
|
targetColumns.push(c.basisColumn);
|
|
973
|
-
projections.push({ type: 'column', expr: capturedValueSubquery(srcByCell[i], 0, [
|
|
983
|
+
projections.push({ type: 'column', expr: capturedValueSubquery(srcByCell[i], 0, [anchorKeyInfo]) });
|
|
974
984
|
});
|
|
975
985
|
assertNoMissingNotNull(view, schema, targetColumns.map((baseColumn) => ({ baseColumn })));
|
|
976
986
|
// Non-empty image filter: OR over each captured value (read back by the anchor key) being
|
|
977
987
|
// non-null. A fresh `capturedValueSubquery` per disjunct (distinct AST nodes from the
|
|
978
988
|
// projections') keeps the filter self-contained.
|
|
979
989
|
const nonEmpty = cells
|
|
980
|
-
.map((_, i) => ({ type: 'unary', operator: 'IS NOT NULL', expr: capturedValueSubquery(srcByCell[i], 0, [
|
|
990
|
+
.map((_, i) => ({ type: 'unary', operator: 'IS NOT NULL', expr: capturedValueSubquery(srcByCell[i], 0, [anchorKeyInfo]) }))
|
|
981
991
|
.reduce((acc, e) => acc ? { type: 'binary', operator: 'OR', left: acc, right: e } : e);
|
|
982
992
|
const where = combineAnd(pred ? cloneExpr(pred) : undefined, nonEmpty);
|
|
983
993
|
const select = {
|
|
@@ -1327,34 +1337,34 @@ function emitEavCapturedAttr(ctx, view, shape, member, pivot, cell, pred, stmt,
|
|
|
1327
1337
|
// so both read-back sites (entity column, anchor key) bind the same alias.
|
|
1328
1338
|
const srcAlias = registerCapturedExpr(`cap:${member.relationId.toLowerCase()}:attr:${cell.attribute.toLowerCase()}`, cloneExpr(cell.value));
|
|
1329
1339
|
// Matched UPDATE: existing triples read the captured value back by the entity column.
|
|
1330
|
-
ops.push(buildEavAttrOp(ctx, view, shape, member, pivot, cell, pred, stmt, 'update', capturedValueSubquery(srcAlias, 0, [pivot.entityColumn])));
|
|
1340
|
+
ops.push(buildEavAttrOp(ctx, view, shape, member, pivot, cell, pred, stmt, 'update', capturedValueSubquery(srcAlias, 0, [keyColumnInfo(resolveMemberTable(ctx, member).tableSchema, pivot.entityColumn)])));
|
|
1331
1341
|
// Materialize INSERT: entities lacking the triple, value read back by the anchor key, non-null filtered.
|
|
1332
1342
|
ops.push(buildEavCapturedInsert(ctx, view, shape, member, pivot, cell, srcAlias, pred, stmt));
|
|
1333
1343
|
}
|
|
1334
1344
|
/**
|
|
1335
1345
|
* One matched EAV op for an attribute: `update <pivot> set <valCol> = <value>` (upsert
|
|
1336
1346
|
* value branch) or `delete from <pivot>` (null branch), each scoped
|
|
1337
|
-
* `where <attrCol> = '<attribute>' and <entityCol> in (<anchor subquery>)
|
|
1347
|
+
* `where <attrCol> = '<attribute>' and <entityCol> in (<anchor subquery>)` — the entity
|
|
1348
|
+
* correlation taking the NULL-safe EXISTS form (target aliased {@link SELF_ALIAS}) when
|
|
1349
|
+
* both the entity column and the anchor key are nullable ({@link anchorKeyCorrelation});
|
|
1350
|
+
* the attribute conjunct compares a non-null literal and stays plain `=`. `valueOverride`
|
|
1338
1351
|
* supplies the matched-UPDATE value for a `captured` cell (the `__vmupd_keys` read-back correlated
|
|
1339
1352
|
* by the entity column); absent, the cell's own lowered value is used (the `constant` branch).
|
|
1340
1353
|
*/
|
|
1341
1354
|
function buildEavAttrOp(ctx, view, shape, member, pivot, cell, pred, stmt, op, valueOverride) {
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
expr: { type: 'column', name: pivot.entityColumn },
|
|
1345
|
-
subquery: anchorKeySubquery(shape, pred),
|
|
1346
|
-
});
|
|
1355
|
+
const correlation = anchorKeyCorrelation(ctx, shape, member, pivot.entityColumn, pred);
|
|
1356
|
+
const where = combineAnd(eavAttrEquals(pivot, cell.attribute), correlation.where);
|
|
1347
1357
|
const table = memberIdentifier(member);
|
|
1348
1358
|
const resolved = resolveMemberTable(ctx, member);
|
|
1349
1359
|
if (op === 'delete') {
|
|
1350
1360
|
const statement = {
|
|
1351
|
-
type: 'delete', table, where,
|
|
1361
|
+
type: 'delete', table, alias: correlation.targetAlias, where,
|
|
1352
1362
|
contextValues: stmt.contextValues, schemaPath: stmt.schemaPath, loc: stmt.loc,
|
|
1353
1363
|
};
|
|
1354
1364
|
return { table: resolved, op: 'delete', statement };
|
|
1355
1365
|
}
|
|
1356
1366
|
const statement = {
|
|
1357
|
-
type: 'update', table,
|
|
1367
|
+
type: 'update', table, alias: correlation.targetAlias,
|
|
1358
1368
|
assignments: [{ column: pivot.valueColumn, value: valueOverride ?? cloneExpr(cell.value) }],
|
|
1359
1369
|
where,
|
|
1360
1370
|
contextValues: stmt.contextValues, schemaPath: stmt.schemaPath, loc: stmt.loc,
|
|
@@ -1426,8 +1436,9 @@ function buildEavInsertSelect(ctx, view, shape, member, pivot, cell, pred, stmt,
|
|
|
1426
1436
|
function buildEavCapturedInsert(ctx, view, shape, member, pivot, cell, srcAlias, pred, stmt) {
|
|
1427
1437
|
const ref = resolveMemberTable(ctx, member);
|
|
1428
1438
|
const anchorKey = singleKeyColumn(view, shape, shape.anchor);
|
|
1439
|
+
const anchorKeyInfo = keyColumnInfo(resolveMemberTable(ctx, shape.anchor).tableSchema, anchorKey);
|
|
1429
1440
|
// A fresh read-back subquery per use (projection + filter) — distinct AST nodes.
|
|
1430
|
-
const capturedVal = () => capturedValueSubquery(srcAlias, 0, [
|
|
1441
|
+
const capturedVal = () => capturedValueSubquery(srcAlias, 0, [anchorKeyInfo]);
|
|
1431
1442
|
const projections = [
|
|
1432
1443
|
{ type: 'column', expr: { type: 'column', name: anchorKey, table: shape.anchor.relationId } },
|
|
1433
1444
|
{ type: 'column', expr: { type: 'literal', value: cell.attribute } },
|
|
@@ -1627,6 +1638,58 @@ function anchorKeySubquery(shape, pred) {
|
|
|
1627
1638
|
where: pred ? cloneExpr(pred) : undefined,
|
|
1628
1639
|
};
|
|
1629
1640
|
}
|
|
1641
|
+
/**
|
|
1642
|
+
* The identifying correlation a member op routes on: `<memberCol> in (select <anchorKey>
|
|
1643
|
+
* from <anchor> [where <pred>])` when either stitch column is declared NOT NULL, or the
|
|
1644
|
+
* NULL-safe correlated `exists (select 1 from <anchor> where [<pred> and] <per-column
|
|
1645
|
+
* {@link captureKeyEquality}>)` when **both** are nullable — NULL is a self-equal key
|
|
1646
|
+
* value (docs/schema.md § Primary-key nullability), and `NULL IN (…)` is UNKNOWN, so
|
|
1647
|
+
* the IN shape leaves a NULL-keyed member row unreachable (an update misses it; a
|
|
1648
|
+
* fan-out delete strands it as an orphan). The both-sides gate keeps the emitted
|
|
1649
|
+
* statement byte-identical to the legacy IN form for any schema whose stitch key is
|
|
1650
|
+
* NOT NULL on either side. `memberCol` is the member's stitch key, or the EAV pivot's
|
|
1651
|
+
* entity column.
|
|
1652
|
+
*
|
|
1653
|
+
* On the EXISTS branch the member operand is qualified with {@link SELF_ALIAS} — under
|
|
1654
|
+
* a logical-tuple key the anchor and member key columns are spelled identically, so a
|
|
1655
|
+
* bare ref inside the subquery would bind the anchor's column, not the outer target
|
|
1656
|
+
* row. The caller must stamp the returned `targetAlias` onto the emitted UPDATE/DELETE
|
|
1657
|
+
* (`stmt.alias` → `AliasedScope`, `building/update.ts` / `building/delete.ts`);
|
|
1658
|
+
* `targetAlias` is undefined on the IN branch so NOT NULL schemas keep an alias-free
|
|
1659
|
+
* statement. Composite stitch keys are deferred by {@link singleKeyColumn}, so the
|
|
1660
|
+
* per-column shape degenerates to one column here.
|
|
1661
|
+
*
|
|
1662
|
+
* NOTE: the EXISTS branch is **correlated**, so unlike the uncorrelated IN subquery it
|
|
1663
|
+
* cannot be evaluated once per statement. The anchor's own value-column UPDATE routes
|
|
1664
|
+
* through here too (it is a mandatory member of its own fan-out) and so self-correlates
|
|
1665
|
+
* against the table it writes — where {@link anchorDeleteOp} sidesteps the same shape by
|
|
1666
|
+
* applying the bare predicate. Not measured; if a nullable-stitch-key lens write ever
|
|
1667
|
+
* shows up in profiles, give the anchor member the bare-predicate form the delete side
|
|
1668
|
+
* already uses, and consider a NULL-safe uncorrelated membership shape for the rest.
|
|
1669
|
+
*/
|
|
1670
|
+
function anchorKeyCorrelation(ctx, shape, member, memberCol, pred) {
|
|
1671
|
+
const anchorKey = singleKeyColumn(undefined, shape, shape.anchor);
|
|
1672
|
+
const anchorInfo = keyColumnInfo(resolveMemberTable(ctx, shape.anchor).tableSchema, anchorKey);
|
|
1673
|
+
const memberInfo = keyColumnInfo(resolveMemberTable(ctx, member).tableSchema, memberCol);
|
|
1674
|
+
if (!(anchorInfo.nullable && memberInfo.nullable)) {
|
|
1675
|
+
return {
|
|
1676
|
+
where: { type: 'in', expr: { type: 'column', name: memberCol }, subquery: anchorKeySubquery(shape, pred) },
|
|
1677
|
+
};
|
|
1678
|
+
}
|
|
1679
|
+
const keyEq = captureKeyEquality({ type: 'column', name: anchorKey, table: shape.anchor.relationId }, { type: 'column', name: memberCol, table: SELF_ALIAS }, true);
|
|
1680
|
+
return {
|
|
1681
|
+
where: {
|
|
1682
|
+
type: 'exists',
|
|
1683
|
+
subquery: {
|
|
1684
|
+
type: 'select',
|
|
1685
|
+
columns: [{ type: 'column', expr: { type: 'literal', value: 1 } }],
|
|
1686
|
+
from: [{ ...memberIdentifierSource(shape.anchor), alias: shape.anchor.relationId }],
|
|
1687
|
+
where: combineAnd(pred ? cloneExpr(pred) : undefined, keyEq),
|
|
1688
|
+
},
|
|
1689
|
+
},
|
|
1690
|
+
targetAlias: SELF_ALIAS,
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1630
1693
|
/**
|
|
1631
1694
|
* Substitute references to logical columns (unqualified, or qualified by the
|
|
1632
1695
|
* logical table's own name) with their backing get-body expression. Base-member-
|