@quereus/quereus 4.8.0 → 4.10.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.
- package/dist/src/core/database.d.ts +6 -0
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +45 -5
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/statement.d.ts.map +1 -1
- package/dist/src/core/statement.js +74 -15
- package/dist/src/core/statement.js.map +1 -1
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
- package/dist/src/func/builtins/aggregate.js +88 -35
- package/dist/src/func/builtins/aggregate.js.map +1 -1
- package/dist/src/func/builtins/conversion.d.ts +5 -0
- package/dist/src/func/builtins/conversion.d.ts.map +1 -1
- package/dist/src/func/builtins/conversion.js +16 -2
- package/dist/src/func/builtins/conversion.js.map +1 -1
- package/dist/src/func/builtins/explain.d.ts.map +1 -1
- package/dist/src/func/builtins/explain.js +9 -2
- package/dist/src/func/builtins/explain.js.map +1 -1
- package/dist/src/func/builtins/index.d.ts.map +1 -1
- package/dist/src/func/builtins/index.js +5 -2
- package/dist/src/func/builtins/index.js.map +1 -1
- package/dist/src/func/builtins/scalar.d.ts.map +1 -1
- package/dist/src/func/builtins/scalar.js +4 -2
- package/dist/src/func/builtins/scalar.js.map +1 -1
- package/dist/src/func/builtins/string.d.ts +2 -0
- package/dist/src/func/builtins/string.d.ts.map +1 -1
- package/dist/src/func/builtins/string.js +60 -4
- package/dist/src/func/builtins/string.js.map +1 -1
- package/dist/src/func/registration.d.ts +6 -0
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +1 -0
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +7 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +21 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/planner/building/constraint-builder.js +1 -1
- package/dist/src/planner/building/constraint-builder.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +13 -1
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +121 -51
- package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
- package/dist/src/planner/rules/shared/index-style-context.d.ts +14 -4
- package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
- package/dist/src/planner/rules/shared/index-style-context.js +14 -4
- package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
- package/dist/src/runtime/emission-context.d.ts +14 -1
- package/dist/src/runtime/emission-context.d.ts.map +1 -1
- package/dist/src/runtime/emission-context.js +17 -5
- package/dist/src/runtime/emission-context.js.map +1 -1
- package/dist/src/runtime/emit/aggregate-setup.d.ts +44 -8
- package/dist/src/runtime/emit/aggregate-setup.d.ts.map +1 -1
- package/dist/src/runtime/emit/aggregate-setup.js +79 -15
- package/dist/src/runtime/emit/aggregate-setup.js.map +1 -1
- package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/aggregate.js +21 -106
- package/dist/src/runtime/emit/aggregate.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +212 -155
- package/dist/src/runtime/emit/alter-table.js.map +1 -1
- package/dist/src/runtime/emit/asof-scan.js +1 -1
- package/dist/src/runtime/emit/asof-scan.js.map +1 -1
- package/dist/src/runtime/emit/assertion-drop-guard.d.ts +26 -12
- package/dist/src/runtime/emit/assertion-drop-guard.d.ts.map +1 -1
- package/dist/src/runtime/emit/assertion-drop-guard.js +45 -26
- package/dist/src/runtime/emit/assertion-drop-guard.js.map +1 -1
- package/dist/src/runtime/emit/assertion-rename-helpers.d.ts +6 -6
- package/dist/src/runtime/emit/assertion-rename-helpers.d.ts.map +1 -1
- package/dist/src/runtime/emit/assertion-rename-helpers.js +18 -20
- package/dist/src/runtime/emit/assertion-rename-helpers.js.map +1 -1
- package/dist/src/runtime/emit/between.d.ts +2 -0
- package/dist/src/runtime/emit/between.d.ts.map +1 -1
- package/dist/src/runtime/emit/between.js +7 -8
- package/dist/src/runtime/emit/between.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts +27 -5
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +278 -187
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/case.d.ts +34 -0
- package/dist/src/runtime/emit/case.d.ts.map +1 -1
- package/dist/src/runtime/emit/case.js +18 -11
- package/dist/src/runtime/emit/case.js.map +1 -1
- package/dist/src/runtime/emit/cast.d.ts +2 -0
- package/dist/src/runtime/emit/cast.d.ts.map +1 -1
- package/dist/src/runtime/emit/cast.js +7 -5
- package/dist/src/runtime/emit/cast.js.map +1 -1
- package/dist/src/runtime/emit/column-reference.d.ts +3 -1
- package/dist/src/runtime/emit/column-reference.d.ts.map +1 -1
- package/dist/src/runtime/emit/column-reference.js +6 -2
- package/dist/src/runtime/emit/column-reference.js.map +1 -1
- package/dist/src/runtime/emit/column-rename-cascade.d.ts +84 -0
- package/dist/src/runtime/emit/column-rename-cascade.d.ts.map +1 -0
- package/dist/src/runtime/emit/column-rename-cascade.js +134 -0
- package/dist/src/runtime/emit/column-rename-cascade.js.map +1 -0
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +21 -0
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/drop-column-guards.d.ts +82 -53
- package/dist/src/runtime/emit/drop-column-guards.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-column-guards.js +155 -90
- package/dist/src/runtime/emit/drop-column-guards.js.map +1 -1
- package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-table.js +5 -0
- package/dist/src/runtime/emit/drop-table.js.map +1 -1
- package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/drop-view.js +5 -0
- package/dist/src/runtime/emit/drop-view.js.map +1 -1
- package/dist/src/runtime/emit/expression-drop-guard.d.ts +57 -0
- package/dist/src/runtime/emit/expression-drop-guard.d.ts.map +1 -0
- package/dist/src/runtime/emit/expression-drop-guard.js +82 -0
- package/dist/src/runtime/emit/expression-drop-guard.js.map +1 -0
- package/dist/src/runtime/emit/filter.d.ts +1 -1
- package/dist/src/runtime/emit/filter.js +1 -1
- package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/hash-aggregate.js +18 -88
- package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
- package/dist/src/runtime/emit/literal.d.ts +14 -1
- package/dist/src/runtime/emit/literal.d.ts.map +1 -1
- package/dist/src/runtime/emit/literal.js +33 -7
- package/dist/src/runtime/emit/literal.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts +33 -10
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.js +158 -49
- 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 +3 -0
- package/dist/src/runtime/emit/materialized-view.js.map +1 -1
- package/dist/src/runtime/emit/parameter.d.ts +3 -1
- package/dist/src/runtime/emit/parameter.d.ts.map +1 -1
- package/dist/src/runtime/emit/parameter.js +7 -2
- package/dist/src/runtime/emit/parameter.js.map +1 -1
- package/dist/src/runtime/emit/scalar-function.d.ts +29 -1
- package/dist/src/runtime/emit/scalar-function.d.ts.map +1 -1
- package/dist/src/runtime/emit/scalar-function.js +66 -16
- package/dist/src/runtime/emit/scalar-function.js.map +1 -1
- package/dist/src/runtime/emit/scalar-op.d.ts +63 -0
- package/dist/src/runtime/emit/scalar-op.d.ts.map +1 -0
- package/dist/src/runtime/emit/scalar-op.js +37 -0
- package/dist/src/runtime/emit/scalar-op.js.map +1 -0
- package/dist/src/runtime/emit/scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/scan.js +19 -1
- package/dist/src/runtime/emit/scan.js.map +1 -1
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts +18 -8
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -1
- package/dist/src/runtime/emit/temporal-arithmetic.js +29 -311
- package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -1
- package/dist/src/runtime/emit/unary.d.ts +2 -0
- package/dist/src/runtime/emit/unary.d.ts.map +1 -1
- package/dist/src/runtime/emit/unary.js +125 -53
- package/dist/src/runtime/emit/unary.js.map +1 -1
- package/dist/src/runtime/emitters.d.ts +22 -0
- package/dist/src/runtime/emitters.d.ts.map +1 -1
- package/dist/src/runtime/emitters.js +29 -0
- package/dist/src/runtime/emitters.js.map +1 -1
- package/dist/src/runtime/scalar-fusion.d.ts +47 -0
- package/dist/src/runtime/scalar-fusion.d.ts.map +1 -0
- package/dist/src/runtime/scalar-fusion.js +275 -0
- package/dist/src/runtime/scalar-fusion.js.map +1 -0
- package/dist/src/runtime/strict-flags.d.ts +9 -0
- package/dist/src/runtime/strict-flags.d.ts.map +1 -1
- package/dist/src/runtime/strict-flags.js +9 -0
- package/dist/src/runtime/strict-flags.js.map +1 -1
- package/dist/src/runtime/strict-fork.js +2 -2
- package/dist/src/runtime/strict-fork.js.map +1 -1
- package/dist/src/runtime/strict-representation.d.ts +78 -0
- package/dist/src/runtime/strict-representation.d.ts.map +1 -0
- package/dist/src/runtime/strict-representation.js +159 -0
- package/dist/src/runtime/strict-representation.js.map +1 -0
- package/dist/src/schema/assertion.d.ts +58 -0
- package/dist/src/schema/assertion.d.ts.map +1 -1
- package/dist/src/schema/assertion.js +78 -0
- package/dist/src/schema/assertion.js.map +1 -1
- package/dist/src/schema/catalog-persistability.d.ts +11 -3
- package/dist/src/schema/catalog-persistability.d.ts.map +1 -1
- package/dist/src/schema/catalog-persistability.js +48 -29
- package/dist/src/schema/catalog-persistability.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +22 -0
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +4 -1
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/column-republication.d.ts +126 -0
- package/dist/src/schema/column-republication.d.ts.map +1 -0
- package/dist/src/schema/column-republication.js +121 -0
- package/dist/src/schema/column-republication.js.map +1 -0
- package/dist/src/schema/column-source-resolver.d.ts +29 -6
- package/dist/src/schema/column-source-resolver.d.ts.map +1 -1
- package/dist/src/schema/column-source-resolver.js +78 -9
- package/dist/src/schema/column-source-resolver.js.map +1 -1
- package/dist/src/schema/expression-dependents.d.ts +160 -0
- package/dist/src/schema/expression-dependents.d.ts.map +1 -0
- package/dist/src/schema/expression-dependents.js +228 -0
- package/dist/src/schema/expression-dependents.js.map +1 -0
- package/dist/src/schema/function.d.ts +15 -0
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/object-dependency-closure.d.ts +127 -0
- package/dist/src/schema/object-dependency-closure.d.ts.map +1 -0
- package/dist/src/schema/object-dependency-closure.js +117 -0
- package/dist/src/schema/object-dependency-closure.js.map +1 -0
- package/dist/src/schema/object-ref-resolver.d.ts +83 -0
- package/dist/src/schema/object-ref-resolver.d.ts.map +1 -0
- package/dist/src/schema/object-ref-resolver.js +135 -0
- package/dist/src/schema/object-ref-resolver.js.map +1 -0
- package/dist/src/schema/rename/column-rename.d.ts +228 -0
- package/dist/src/schema/rename/column-rename.d.ts.map +1 -0
- package/dist/src/schema/rename/column-rename.js +1225 -0
- package/dist/src/schema/rename/column-rename.js.map +1 -0
- package/dist/src/schema/rename/self-qualifier-strip.d.ts +29 -0
- package/dist/src/schema/rename/self-qualifier-strip.d.ts.map +1 -0
- package/dist/src/schema/rename/self-qualifier-strip.js +267 -0
- package/dist/src/schema/rename/self-qualifier-strip.js.map +1 -0
- package/dist/src/schema/rename/shared.d.ts +108 -0
- package/dist/src/schema/rename/shared.d.ts.map +1 -0
- package/dist/src/schema/rename/shared.js +49 -0
- package/dist/src/schema/rename/shared.js.map +1 -0
- package/dist/src/schema/rename/table-rename.d.ts +263 -0
- package/dist/src/schema/rename/table-rename.d.ts.map +1 -0
- package/dist/src/schema/rename/table-rename.js +860 -0
- package/dist/src/schema/rename/table-rename.js.map +1 -0
- package/dist/src/schema/rename-rewriter.d.ts +18 -278
- package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
- package/dist/src/schema/rename-rewriter.js +17 -1506
- package/dist/src/schema/rename-rewriter.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +228 -21
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/types/builtin-types.d.ts.map +1 -1
- package/dist/src/types/builtin-types.js +29 -36
- package/dist/src/types/builtin-types.js.map +1 -1
- package/dist/src/types/cast-semantics.d.ts +7 -5
- package/dist/src/types/cast-semantics.d.ts.map +1 -1
- package/dist/src/types/cast-semantics.js +20 -9
- package/dist/src/types/cast-semantics.js.map +1 -1
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/registry.d.ts.map +1 -1
- package/dist/src/types/registry.js +2 -1
- package/dist/src/types/registry.js.map +1 -1
- package/dist/src/types/temporal-ops.d.ts +119 -0
- package/dist/src/types/temporal-ops.d.ts.map +1 -0
- package/dist/src/types/temporal-ops.js +348 -0
- package/dist/src/types/temporal-ops.js.map +1 -0
- package/dist/src/types/temporal-types.d.ts +11 -0
- package/dist/src/types/temporal-types.d.ts.map +1 -1
- package/dist/src/types/temporal-types.js +87 -0
- package/dist/src/types/temporal-types.js.map +1 -1
- package/dist/src/util/affinity.d.ts +2 -1
- package/dist/src/util/affinity.d.ts.map +1 -1
- package/dist/src/util/affinity.js +16 -5
- package/dist/src/util/affinity.js.map +1 -1
- package/dist/src/util/ast-spine-clone.d.ts +2 -2
- package/dist/src/util/ast-spine-clone.js +2 -2
- package/dist/src/util/coercion.d.ts +22 -7
- package/dist/src/util/coercion.d.ts.map +1 -1
- package/dist/src/util/coercion.js +28 -15
- package/dist/src/util/coercion.js.map +1 -1
- package/dist/src/util/comparison.d.ts +25 -0
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +25 -0
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/util/key-tuple-codec.d.ts +12 -3
- package/dist/src/util/key-tuple-codec.d.ts.map +1 -1
- package/dist/src/util/key-tuple-codec.js.map +1 -1
- package/dist/src/util/numeric-canonical.d.ts +26 -0
- package/dist/src/util/numeric-canonical.d.ts.map +1 -0
- package/dist/src/util/numeric-canonical.js +66 -0
- package/dist/src/util/numeric-canonical.js.map +1 -0
- package/dist/src/util/value-text.d.ts +48 -0
- package/dist/src/util/value-text.d.ts.map +1 -0
- package/dist/src/util/value-text.js +35 -0
- package/dist/src/util/value-text.js.map +1 -0
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +14 -5
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/module.d.ts +25 -0
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,860 @@
|
|
|
1
|
+
import { eq, objectRefKey, objectRefKeySchema, rewriteEach } from './shared.js';
|
|
2
|
+
/**
|
|
3
|
+
* `target.resolve` and the derived `targetKey` (`objectRefKey(target.schemaName,
|
|
4
|
+
* target.oldName)`) decide matching for the rewrite and the probe below alike: a
|
|
5
|
+
* reference matches when `resolve(ref.schema, ref.name)` — the reference's
|
|
6
|
+
* planner-parity resolution under the WALKED BODY's home schema path — equals
|
|
7
|
+
* `targetKey`. The bare-name equality check in front is a pure short-circuit:
|
|
8
|
+
* the resolver echoes the name it is given into the key, so a reference spelled
|
|
9
|
+
* differently can never resolve to the target.
|
|
10
|
+
*
|
|
11
|
+
* The rewrite additionally enforces a POST-CONDITION the probe does not need,
|
|
12
|
+
* and it covers EVERY reference the walk reports — not only the ones it
|
|
13
|
+
* rewrites:
|
|
14
|
+
*
|
|
15
|
+
* > Walking a body for a rename, every reference must mean after the statement
|
|
16
|
+
* > exactly what it meant before.
|
|
17
|
+
*
|
|
18
|
+
* Both arms decide that the same way: re-resolve the reference under the same
|
|
19
|
+
* home schema path against the catalog AS IT WILL BE AFTER THE RENAME
|
|
20
|
+
* (`target.resolveAfter`) and compare with what it meant before.
|
|
21
|
+
*
|
|
22
|
+
* - A REWRITTEN reference must still yield the renamed object's key. When it
|
|
23
|
+
* would not — the bare new name is captured by an earlier schema on the
|
|
24
|
+
* body's home path — it is schema-qualified to the renamed schema via
|
|
25
|
+
* {@link TableRef.qualify}.
|
|
26
|
+
* - An UNTOUCHED reference must still yield its own before-answer. A rename
|
|
27
|
+
* also CREATES a name, and that new name can capture a reference the walk
|
|
28
|
+
* never matched: a `temp`-owned body reading bare `k` (falling through to
|
|
29
|
+
* `main.k`, because `temp` holds no `k`) silently re-binds to `temp.k` the
|
|
30
|
+
* moment `alter table temp.other rename to k` lands, without its text ever
|
|
31
|
+
* mentioning the renamed table. When the answer would change, the reference
|
|
32
|
+
* is qualified to the schema its BEFORE-answer named — recovered from that
|
|
33
|
+
* key by {@link import('./shared.js').objectRefKeySchema}. This arm applies
|
|
34
|
+
* to the stored bodies that plan under the home schema path and is walked
|
|
35
|
+
* ONCE per body; {@link TableRenameOpts.schemaAuthoredBody} turns it off for
|
|
36
|
+
* the schema-authored expressions, which need it neither for correctness nor
|
|
37
|
+
* for the re-walks they are deliberately subjected to — see that flag.
|
|
38
|
+
*
|
|
39
|
+
* The post-condition also covers the FROM-frame namespace, not just the
|
|
40
|
+
* catalog's: inside one body, the bare qualifier an unaliased FROM source
|
|
41
|
+
* exposes must keep binding that source. When the rename would give a source a
|
|
42
|
+
* name already visible as a qualifier where it sits ({@link
|
|
43
|
+
* TableRef.qualifierCollides}), the source pins its pre-rename spelling as an
|
|
44
|
+
* explicit alias ({@link TableRef.aliasAs}) and every column qualifier bound
|
|
45
|
+
* through it keeps the old spelling — `from t join u as t2` becomes
|
|
46
|
+
* `from t2 as t join u as t2` under `t → t2`, with `t.x` untouched. And when
|
|
47
|
+
* the new name is shadowed by a CTE in scope at the source ({@link
|
|
48
|
+
* TableRef.cteShadows}) — invisible to the catalog resolvers — the reference
|
|
49
|
+
* is schema-qualified so it cannot re-bind to the CTE.
|
|
50
|
+
*
|
|
51
|
+
* Qualification is conditional in both arms, never eager, so the common
|
|
52
|
+
* no-collision rename leaves body text (and a materialized view's `bodyHash`)
|
|
53
|
+
* exactly as before. A qualified reference resolves by passthrough, so its
|
|
54
|
+
* before- and after-answers are always equal and neither arm can fire on one;
|
|
55
|
+
* only unqualified references ever gain a qualifier. And only the NEW name can
|
|
56
|
+
* change meaning without being rewritten: removing the old name can only push a
|
|
57
|
+
* resolution later, and the references that resolved to the removed entry are
|
|
58
|
+
* exactly the ones the rewrite already matches — so there is no freed-name
|
|
59
|
+
* direction to handle.
|
|
60
|
+
*
|
|
61
|
+
* ALREADY-BROKEN BODIES: the resolver answers a total function — a name held by
|
|
62
|
+
* no schema on the path still gets a stable key, the home schema's — so a body
|
|
63
|
+
* naming an object that exists nowhere reads `before = <home>.<name>`, and a
|
|
64
|
+
* rename introducing that name in an earlier schema flips `after`. The
|
|
65
|
+
* untouched arm then qualifies the reference to the home schema, PINNING the
|
|
66
|
+
* pre-existing "no such table" failure rather than letting the body silently
|
|
67
|
+
* start reading the new arrival. That is the invariant-faithful answer, so no
|
|
68
|
+
* existence probe is added to dodge it (same reasoning as the seedless-qualifier
|
|
69
|
+
* emit site below: it only ever fires on an already-unevaluable body).
|
|
70
|
+
*/
|
|
71
|
+
export function renameTableInAst(node, target, opts) {
|
|
72
|
+
if (!node)
|
|
73
|
+
return false;
|
|
74
|
+
const { oldName, newName, schemaName, resolve, resolveAfter } = target;
|
|
75
|
+
const targetKey = objectRefKey(schemaName, oldName);
|
|
76
|
+
const newTargetKey = objectRefKey(schemaName, newName);
|
|
77
|
+
let changed = false;
|
|
78
|
+
walkTableRefs(node, ref => {
|
|
79
|
+
if (eq(ref.name, oldName) && resolve(ref.schema, ref.name) === targetKey) {
|
|
80
|
+
// Qualifier-namespace guard, the FROM-frame twin of the catalog
|
|
81
|
+
// post-condition below: the bare qualifier an unaliased source
|
|
82
|
+
// exposes must keep binding that source. When the new name is
|
|
83
|
+
// already visible as a qualifier where the source sits, the source
|
|
84
|
+
// takes the new name but pins the OLD spelling as an explicit
|
|
85
|
+
// alias, and every qualifier bound through it keeps that spelling.
|
|
86
|
+
// Guarded on a real name change so a case-only rename never
|
|
87
|
+
// self-collides on the source's own frame entry.
|
|
88
|
+
//
|
|
89
|
+
// NOTE: deliberately conservative — the source is aliased whenever
|
|
90
|
+
// the new name is visible in scope, even when nothing in its
|
|
91
|
+
// subtree actually spells it. Detecting real use would need a
|
|
92
|
+
// second pass over the subtree; the extra alias is harmless and
|
|
93
|
+
// only appears in a genuine collision. It is not free for a
|
|
94
|
+
// MAINTAINED body, though: a pin changes the body text, hence the
|
|
95
|
+
// `bodyHash`, hence forces a re-hash / regenerate on an MV whose
|
|
96
|
+
// meaning did not change. If that ever shows up as churn on large
|
|
97
|
+
// bodies, narrow the predicate to names the subtree actually
|
|
98
|
+
// spells as a qualifier.
|
|
99
|
+
const collides = !eq(oldName, newName) && (ref.qualifierCollides?.(newName) ?? false);
|
|
100
|
+
if (collides && !ref.aliasAs) {
|
|
101
|
+
// A column qualifier bound to a source the rename is about to
|
|
102
|
+
// alias: the aliased source keeps exposing the old spelling,
|
|
103
|
+
// so the qualifier keeps it too. The source's own emit (same
|
|
104
|
+
// walk, same predicate) is what sets `changed`.
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
ref.setName(newName);
|
|
108
|
+
changed = true;
|
|
109
|
+
if (collides)
|
|
110
|
+
ref.aliasAs?.(oldName);
|
|
111
|
+
// Catalog post-condition — with one addition the catalog resolvers
|
|
112
|
+
// cannot express: a CTE in scope at the source captures a bare
|
|
113
|
+
// name before the catalog is ever consulted, so a rewritten
|
|
114
|
+
// reference whose new name a CTE shadows must be schema-qualified
|
|
115
|
+
// even when the catalog answer alone would leave it bare.
|
|
116
|
+
if (ref.qualify && (resolveAfter(ref.schema, newName) !== newTargetKey || (ref.cteShadows?.(newName) ?? false))) {
|
|
117
|
+
ref.qualify(schemaName);
|
|
118
|
+
}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// The untouched arm (see the doc comment). Only a reference that
|
|
122
|
+
// re-resolves through the catalog carries `qualify` — the rest bind
|
|
123
|
+
// through a FROM frame and are preserved by qualifying their source.
|
|
124
|
+
if (opts?.schemaAuthoredBody || !ref.qualify)
|
|
125
|
+
return;
|
|
126
|
+
// NOTE: asks the resolvers about EVERY reference, where the rewrite arm
|
|
127
|
+
// above short-circuits on a bare-name compare first. The arm can in fact
|
|
128
|
+
// only fire on a reference spelling `newName` — the two resolvers differ
|
|
129
|
+
// only over `schemaName`'s name set, and the `oldName` direction is either
|
|
130
|
+
// the rewrite arm's or unchanged — so an `eq(ref.name, newName)` guard here
|
|
131
|
+
// would be equivalent and cheaper. Declined: it trades two Set lookups per
|
|
132
|
+
// reference on a DDL-only path for a claim that silently loses coverage if
|
|
133
|
+
// the `resolve`/`resolveAfter` pair ever differs more broadly than one
|
|
134
|
+
// table rename. Add the guard if rename propagation over a schema-heavy
|
|
135
|
+
// catalog ever measures hot.
|
|
136
|
+
const before = resolve(ref.schema, ref.name);
|
|
137
|
+
if (before === undefined || before === resolveAfter(ref.schema, ref.name))
|
|
138
|
+
return;
|
|
139
|
+
ref.qualify(objectRefKeySchema(before, ref.name.toLowerCase()));
|
|
140
|
+
changed = true;
|
|
141
|
+
}, opts);
|
|
142
|
+
return changed;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Whether `node` refers to the table/view identified by `targetKey` (spelled
|
|
146
|
+
* `name`) — read-only, nothing is mutated.
|
|
147
|
+
*
|
|
148
|
+
* Deliberately the SAME traversal {@link renameTableInAst} uses — both are
|
|
149
|
+
* sinks over {@link visitTableRefs} — so "refers to" can never drift from
|
|
150
|
+
* "would have been rewritten by a rename". That equivalence is the whole
|
|
151
|
+
* point: the DROP guard refuses exactly the cases `ALTER TABLE … RENAME`
|
|
152
|
+
* would have followed. In particular, a body whose `with <name> as (…)`
|
|
153
|
+
* merely SHADOWS the probed name is not a reference — the walk skips it for
|
|
154
|
+
* both verbs. The probe's sink stops the walk at the first match.
|
|
155
|
+
*
|
|
156
|
+
* The column-level probes ({@link import('./column-rename.js').columnReferencedInAst},
|
|
157
|
+
* {@link import('./column-rename.js').columnReferencedInCheckExpression}) reach
|
|
158
|
+
* the same equivalence a different way — a real rename, to a sentinel name,
|
|
159
|
+
* over a throwaway clone — because the column walker has ~8 mutation points
|
|
160
|
+
* and a CTE-re-exposure branch that reads the new name; see their comments.
|
|
161
|
+
*/
|
|
162
|
+
export function tableReferencedInAst(node, name, resolve, targetKey, opts) {
|
|
163
|
+
if (!node)
|
|
164
|
+
return false;
|
|
165
|
+
let found = false;
|
|
166
|
+
walkTableRefs(node, ref => {
|
|
167
|
+
if (eq(ref.name, name) && resolve(ref.schema, ref.name) === targetKey) {
|
|
168
|
+
found = true;
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
}, opts);
|
|
172
|
+
return found;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Every real (non-shadowed) table / view reference in `node`, keyed the way
|
|
176
|
+
* {@link renameTableInAst} and {@link tableReferencedInAst} key their target:
|
|
177
|
+
* `resolve(ref.schema, ref.name)`, the reference's planner-parity resolution
|
|
178
|
+
* under the WALKED BODY's home schema path.
|
|
179
|
+
*
|
|
180
|
+
* The collect-all THIRD sink over the same {@link walkTableRefs} traversal, so
|
|
181
|
+
* it inherits the scope-awareness of the other two for free: a body whose
|
|
182
|
+
* `with t as (…)` merely SHADOWS the name contributes no `<schema>.t` key, a FROM
|
|
183
|
+
* alias contributes nothing, and a `new.` / `old.` row-image qualifier under
|
|
184
|
+
* {@link TableRenameOpts.rowImageContext} contributes nothing. That matters
|
|
185
|
+
* more here than anywhere else — a collect-everything walk is exactly where
|
|
186
|
+
* that property gets quietly lost, and the reachability closure built on this
|
|
187
|
+
* ({@link import('../object-dependency-closure.js').reachableObjects}) turns
|
|
188
|
+
* every key it yields into a refused DROP.
|
|
189
|
+
*
|
|
190
|
+
* The value is the object NAME the key was built from, lowercased. The
|
|
191
|
+
* resolver echoes the name it is given into the key, so this is always
|
|
192
|
+
* `ref.name.toLowerCase()`; it is returned rather than re-derived because a
|
|
193
|
+
* schema name may itself contain a dot, which makes splitting a key by
|
|
194
|
+
* separator scan wrong — see {@link import('./shared.js').objectRefKeySchema},
|
|
195
|
+
* whose whole job is that split and which needs exactly this.
|
|
196
|
+
*/
|
|
197
|
+
export function collectTableRefsInAst(node, resolve, opts) {
|
|
198
|
+
const refs = new Map();
|
|
199
|
+
if (!node)
|
|
200
|
+
return refs;
|
|
201
|
+
walkTableRefs(node, ref => {
|
|
202
|
+
const key = resolve(ref.schema, ref.name);
|
|
203
|
+
// `undefined` means "no resolver could be consulted at all" — a reference
|
|
204
|
+
// with no key is not something a closure can follow.
|
|
205
|
+
if (key !== undefined)
|
|
206
|
+
refs.set(key, ref.name.toLowerCase());
|
|
207
|
+
}, opts);
|
|
208
|
+
return refs;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Rewrite the renamed table inside every partial-index predicate of `indexes`,
|
|
212
|
+
* in place. A predicate may carry a table-qualified self-reference
|
|
213
|
+
* (`create index ix on t (b) where t.b > 0`), which a table rename must follow.
|
|
214
|
+
*
|
|
215
|
+
* No {@link TableRenameOpts.rowImageContext}: a predicate describes rows
|
|
216
|
+
* already stored, so `new.x` there is a real table reference — see the flag's
|
|
217
|
+
* doc comment. {@link TableRenameOpts.schemaAuthoredBody} IS set: a predicate
|
|
218
|
+
* plans under its owning schema alone, and this AST is one of the several the
|
|
219
|
+
* rename walks more than once.
|
|
220
|
+
*
|
|
221
|
+
* Sharing and idempotence work exactly as in
|
|
222
|
+
* {@link import('./column-rename.js').renameColumnInIndexPredicates}: the
|
|
223
|
+
* predicate `Expression` is shared by reference with the catalog's
|
|
224
|
+
* `TableSchema` and with a unique partial index's `derivedFromIndex` UNIQUE
|
|
225
|
+
* constraint, so one in-place rewrite covers all of them, and a second call
|
|
226
|
+
* with the same pair finds nothing naming `oldName` and returns false.
|
|
227
|
+
*/
|
|
228
|
+
export function renameTableInIndexPredicates(indexes, target) {
|
|
229
|
+
return rewriteEach(indexes, idx => idx.predicate, expr => renameTableInAst(expr, target, { schemaAuthoredBody: true }));
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Rewrite the renamed table inside every CHECK constraint expression of `checks`,
|
|
233
|
+
* in place. A CHECK may carry a table-qualified self-reference (`check (t.b > 0)`)
|
|
234
|
+
* exactly as a partial-index predicate may, and a table rename must follow it.
|
|
235
|
+
*
|
|
236
|
+
* A CHECK is evaluated against a written row, so the walk runs with
|
|
237
|
+
* {@link TableRenameOpts.rowImageContext}: a bare `new.a` / `old.a` names the
|
|
238
|
+
* row image and survives even a rename of a table literally called `new`. It
|
|
239
|
+
* also runs with {@link TableRenameOpts.schemaAuthoredBody}, for the same
|
|
240
|
+
* reasons the predicate arm above does.
|
|
241
|
+
*
|
|
242
|
+
* Same sharing and idempotence story as {@link renameTableInIndexPredicates}: the
|
|
243
|
+
* `expr` is the very AST the catalog's `TableSchema` holds, so one rewrite covers
|
|
244
|
+
* every holder and a second call with the same pair is a no-op.
|
|
245
|
+
*/
|
|
246
|
+
export function renameTableInCheckConstraints(checks, target) {
|
|
247
|
+
return rewriteEach(checks, cc => cc.expr, expr => renameTableInAst(expr, target, { rowImageContext: true, schemaAuthoredBody: true }));
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Rewrite the renamed table inside the two expressions a `ColumnSchema` can carry —
|
|
251
|
+
* a column DEFAULT (`w integer default ((select min(v) from u))`, however authored:
|
|
252
|
+
* inline, or via `ALTER TABLE … ALTER COLUMN … SET DEFAULT`, which writes the same
|
|
253
|
+
* field) and a generated column's body (`g integer generated always as ((select
|
|
254
|
+
* min(u.v) from u) + id)`) — in place.
|
|
255
|
+
*
|
|
256
|
+
* Both expressions evaluate against the row being written, so the walk runs
|
|
257
|
+
* with {@link TableRenameOpts.rowImageContext}: a default's `new.a` names the
|
|
258
|
+
* row image, not a table called `new`. It also runs with
|
|
259
|
+
* {@link TableRenameOpts.schemaAuthoredBody}, for the same reasons the two arms
|
|
260
|
+
* above do.
|
|
261
|
+
*
|
|
262
|
+
* Unlike the column-rename counterpart
|
|
263
|
+
* ({@link import('./column-rename.js').renameColumnInColumnExpressions}) there
|
|
264
|
+
* is no seeded/unseeded split: {@link renameTableInAst} resolves nothing against an
|
|
265
|
+
* implicit owning table, so the SAME entry point is correct for the renamed table's own
|
|
266
|
+
* columns (a self-referencing default, `default ((select count(*) from t))`) and for
|
|
267
|
+
* every other table's.
|
|
268
|
+
*
|
|
269
|
+
* Same sharing and idempotence story as {@link renameTableInCheckConstraints}: the
|
|
270
|
+
* `Expression` is the very AST the catalog's `TableSchema` holds, so one in-place
|
|
271
|
+
* rewrite reaches every holder and a second call with the same pair finds nothing
|
|
272
|
+
* naming `oldName` and returns false.
|
|
273
|
+
*
|
|
274
|
+
* The parameter is structurally typed rather than `ColumnSchema[]` so this module stays
|
|
275
|
+
* free of catalog imports; `ColumnSchema` satisfies it.
|
|
276
|
+
*/
|
|
277
|
+
export function renameTableInColumnExpressions(columns, target) {
|
|
278
|
+
const rewrite = (expr) => renameTableInAst(expr, target, { rowImageContext: true, schemaAuthoredBody: true });
|
|
279
|
+
// Both walks always run — `||` on the results, not short-circuited between them.
|
|
280
|
+
const defaultsChanged = rewriteEach(columns, c => c.defaultValue ?? undefined, rewrite);
|
|
281
|
+
const generatedChanged = rewriteEach(columns, c => c.generatedExpr, rewrite);
|
|
282
|
+
return defaultsChanged || generatedChanged;
|
|
283
|
+
}
|
|
284
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
285
|
+
// The walk
|
|
286
|
+
// ──────────────────────────────────────────────────────────────────────
|
|
287
|
+
function walkTableRefs(node, sink, opts) {
|
|
288
|
+
const state = {
|
|
289
|
+
sink,
|
|
290
|
+
rowImage: opts?.rowImageContext ?? false,
|
|
291
|
+
stack: [],
|
|
292
|
+
stop: false,
|
|
293
|
+
};
|
|
294
|
+
visitTableRefs(node, state);
|
|
295
|
+
}
|
|
296
|
+
function emptyTableFrame() {
|
|
297
|
+
return { ctes: new Set(), bound: new Map() };
|
|
298
|
+
}
|
|
299
|
+
function emit(state, ref) {
|
|
300
|
+
if (state.stop)
|
|
301
|
+
return;
|
|
302
|
+
if (state.sink(ref) === true)
|
|
303
|
+
state.stop = true;
|
|
304
|
+
}
|
|
305
|
+
/** Whether any enclosing WITH frame declares `nameLower` as a CTE. */
|
|
306
|
+
function isCteInScope(state, nameLower) {
|
|
307
|
+
for (const frame of state.stack) {
|
|
308
|
+
if (frame.ctes.has(nameLower))
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Resolve a bare column qualifier innermost-first against the scope stack.
|
|
315
|
+
* `binding` is the real unaliased table source it binds, or `null` when it
|
|
316
|
+
* binds something that is not directly a table name (an alias, a CTE, a
|
|
317
|
+
* subquery / function source); `frameIndex` is the stack index of the frame
|
|
318
|
+
* that bound it, so the collision predicate can be evaluated at the BINDING
|
|
319
|
+
* frame's depth — a deeper frame binding the new name is inner to the source
|
|
320
|
+
* and must not falsely trigger. Returns `undefined` when nothing in scope
|
|
321
|
+
* binds the qualifier.
|
|
322
|
+
*/
|
|
323
|
+
function resolveQualifier(state, qualifierLower) {
|
|
324
|
+
for (let i = state.stack.length - 1; i >= 0; i--) {
|
|
325
|
+
const frame = state.stack[i];
|
|
326
|
+
const binding = frame.bound.get(qualifierLower);
|
|
327
|
+
if (binding !== undefined)
|
|
328
|
+
return { binding, frameIndex: i };
|
|
329
|
+
// A CTE name in scope shadows the qualifier even without a FROM entry
|
|
330
|
+
// (mirrors the column walker's rebind scan, which counts `ctesInScope`).
|
|
331
|
+
if (frame.ctes.has(qualifierLower))
|
|
332
|
+
return { binding: null, frameIndex: i };
|
|
333
|
+
}
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Whether `nameLower` is visible as a column qualifier at frame `limit` or any
|
|
338
|
+
* enclosing frame — a FROM binding (an alias or an unaliased source's name) or
|
|
339
|
+
* a CTE declaration.
|
|
340
|
+
*/
|
|
341
|
+
function qualifierVisibleUpTo(state, nameLower, limit) {
|
|
342
|
+
for (let i = 0; i <= limit && i < state.stack.length; i++) {
|
|
343
|
+
const frame = state.stack[i];
|
|
344
|
+
if (frame.bound.has(nameLower) || frame.ctes.has(nameLower))
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* The predicate behind {@link TableRef.qualifierCollides}, evaluated at the
|
|
351
|
+
* SOURCE's own frame (its FROM frame for the source emit, the binding frame
|
|
352
|
+
* for a bound column-qualifier emit — the same frame, so the two sites always
|
|
353
|
+
* agree). `nameLower` collides when it is visible as a qualifier at or above
|
|
354
|
+
* that frame, OR when the frame's select SUBTREE can introduce it (an inner
|
|
355
|
+
* alias / source / CTE): a qualifier bound to the source can sit inside a
|
|
356
|
+
* correlated subquery, where an inner binding of the new name would capture
|
|
357
|
+
* its new spelling — `select t.x from t where exists (select 1 from other as
|
|
358
|
+
* t2 where t2.id = t.id)` must not let `t → t2` re-bind the correlation.
|
|
359
|
+
* Frame state was filled with pre-rewrite names before anything under the
|
|
360
|
+
* FROM was visited, and the subtree set is memoized on first consult, so
|
|
361
|
+
* every consult for one frame gets the same answer regardless of visit order.
|
|
362
|
+
*/
|
|
363
|
+
function qualifierCollidesAt(state, nameLower, frameIndex) {
|
|
364
|
+
if (qualifierVisibleUpTo(state, nameLower, frameIndex))
|
|
365
|
+
return true;
|
|
366
|
+
const frame = state.stack[frameIndex];
|
|
367
|
+
if (!frame?.subtreeRoot)
|
|
368
|
+
return false;
|
|
369
|
+
frame.introduced ??= collectIntroducedQualifierNames(frame.subtreeRoot);
|
|
370
|
+
return frame.introduced.has(nameLower);
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Every name `root`'s subtree can bind as a column qualifier or capture a FROM
|
|
374
|
+
* source with, lowercased: FROM source bare names and aliases, subquery /
|
|
375
|
+
* function-source aliases, CTE names, UPDATE/DELETE correlation names.
|
|
376
|
+
* Deliberately position-blind and conservative — a name introduced in a
|
|
377
|
+
* sibling subquery that could never actually shadow the consulted source still
|
|
378
|
+
* counts, because the only cost of a false positive is a harmless explicit
|
|
379
|
+
* alias on the renamed source. Structural recursion over plain objects/arrays
|
|
380
|
+
* so unknown node kinds are walked rather than missed.
|
|
381
|
+
*/
|
|
382
|
+
function collectIntroducedQualifierNames(root) {
|
|
383
|
+
const names = new Set();
|
|
384
|
+
const visit = (node) => {
|
|
385
|
+
if (Array.isArray(node)) {
|
|
386
|
+
for (const item of node)
|
|
387
|
+
visit(item);
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
if (node === null || typeof node !== 'object')
|
|
391
|
+
return;
|
|
392
|
+
const obj = node;
|
|
393
|
+
switch (obj.type) {
|
|
394
|
+
case 'table': {
|
|
395
|
+
const ts = obj;
|
|
396
|
+
names.add((ts.alias ?? ts.table.name).toLowerCase());
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case 'subquerySource':
|
|
400
|
+
names.add(obj.alias.toLowerCase());
|
|
401
|
+
break;
|
|
402
|
+
case 'functionSource': {
|
|
403
|
+
const alias = obj.alias;
|
|
404
|
+
if (alias)
|
|
405
|
+
names.add(alias.toLowerCase());
|
|
406
|
+
break;
|
|
407
|
+
}
|
|
408
|
+
case 'commonTableExpr':
|
|
409
|
+
names.add(obj.name.toLowerCase());
|
|
410
|
+
break;
|
|
411
|
+
case 'update':
|
|
412
|
+
case 'delete': {
|
|
413
|
+
const alias = obj.alias;
|
|
414
|
+
if (alias)
|
|
415
|
+
names.add(alias.toLowerCase());
|
|
416
|
+
break;
|
|
417
|
+
}
|
|
418
|
+
default:
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
for (const value of Object.values(obj))
|
|
422
|
+
visit(value);
|
|
423
|
+
};
|
|
424
|
+
visit(root);
|
|
425
|
+
return names;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Push a WITH frame and visit each member's body inside it, registering names
|
|
429
|
+
* in the SAME visibility order the planner uses
|
|
430
|
+
* ({@link import('../../planner/building/with.js').buildCommonTableExpr}) and
|
|
431
|
+
* the column walker mirrors: a `with recursive` member registers its own name
|
|
432
|
+
* BEFORE its body is visited (so the recursive leg's self-reference resolves
|
|
433
|
+
* to the CTE, not a same-named real table); a non-recursive member registers
|
|
434
|
+
* AFTER (its body must not see itself, only prior siblings — a forward
|
|
435
|
+
* reference to a later sibling binds the real table and still rewrites).
|
|
436
|
+
*
|
|
437
|
+
* Caller is responsible for popping the frame (in a `finally`).
|
|
438
|
+
*/
|
|
439
|
+
function pushWithFrame(withClause, state) {
|
|
440
|
+
const frame = emptyTableFrame();
|
|
441
|
+
state.stack.push(frame);
|
|
442
|
+
if (!withClause)
|
|
443
|
+
return;
|
|
444
|
+
for (const cte of withClause.ctes) {
|
|
445
|
+
const nameLower = cte.name.toLowerCase();
|
|
446
|
+
if (withClause.recursive)
|
|
447
|
+
frame.ctes.add(nameLower);
|
|
448
|
+
visitTableRefs(cte.query, state);
|
|
449
|
+
frame.ctes.add(nameLower);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Collect the qualifiers a FROM clause binds into `frame`. Runs BEFORE the
|
|
454
|
+
* frame is pushed (and before anything under the FROM is visited), so the
|
|
455
|
+
* bindings reflect pre-rewrite names — every match still compares against the
|
|
456
|
+
* old name.
|
|
457
|
+
*/
|
|
458
|
+
function collectFromBindings(item, state, frame) {
|
|
459
|
+
switch (item.type) {
|
|
460
|
+
case 'table': {
|
|
461
|
+
const ts = item;
|
|
462
|
+
const nameLower = ts.table.name.toLowerCase();
|
|
463
|
+
const bindsCte = ts.table.schema === undefined && isCteInScope(state, nameLower);
|
|
464
|
+
if (ts.alias) {
|
|
465
|
+
// The alias replaces the source name as the row's only qualifier;
|
|
466
|
+
// renaming a table never changes an alias, so qualified refs
|
|
467
|
+
// through it have nothing to rewrite even when the underlying
|
|
468
|
+
// source IS the renamed table (the FROM source itself is still
|
|
469
|
+
// reported as a reference by the visit below).
|
|
470
|
+
frame.bound.set(ts.alias.toLowerCase(), null);
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
frame.bound.set(nameLower, bindsCte ? null : { schema: ts.table.schema, name: ts.table.name });
|
|
474
|
+
}
|
|
475
|
+
break;
|
|
476
|
+
}
|
|
477
|
+
case 'join': {
|
|
478
|
+
const join = item;
|
|
479
|
+
collectFromBindings(join.left, state, frame);
|
|
480
|
+
collectFromBindings(join.right, state, frame);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
case 'subquerySource':
|
|
484
|
+
frame.bound.set(item.alias.toLowerCase(), null);
|
|
485
|
+
break;
|
|
486
|
+
case 'functionSource': {
|
|
487
|
+
const fs = item;
|
|
488
|
+
// An unaliased function source contributes no qualifier.
|
|
489
|
+
if (fs.alias)
|
|
490
|
+
frame.bound.set(fs.alias.toLowerCase(), null);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
/**
|
|
496
|
+
* A DML target identifier. Mirrors the planner's write-target rule
|
|
497
|
+
* ({@link import('../../planner/building/dml-target.js').resolveCteTarget}):
|
|
498
|
+
* an UNQUALIFIED target name that matches a member of the statement's OWN
|
|
499
|
+
* leading WITH clause binds that CTE — which shadows a same-named schema
|
|
500
|
+
* object as a write target — so it is not a table reference; a
|
|
501
|
+
* schema-qualified target never binds a CTE. Enclosing statements' CTEs do
|
|
502
|
+
* NOT shadow a write target (the resolver consults only the statement's own
|
|
503
|
+
* clause), so only `withClause` is checked here, not the scope stack.
|
|
504
|
+
*/
|
|
505
|
+
function visitDmlTarget(id, withClause, state) {
|
|
506
|
+
if (state.stop)
|
|
507
|
+
return;
|
|
508
|
+
if (id.schema === undefined && (withClause?.ctes ?? []).some(c => eq(c.name, id.name)))
|
|
509
|
+
return;
|
|
510
|
+
emit(state, {
|
|
511
|
+
schema: id.schema,
|
|
512
|
+
name: id.name,
|
|
513
|
+
setName: next => { id.name = next; },
|
|
514
|
+
// A DML target re-resolves through the catalog when the body is next
|
|
515
|
+
// planned, so it can carry the post-condition qualifier.
|
|
516
|
+
qualify: schemaName => { id.schema = schemaName; },
|
|
517
|
+
// The statement's OWN WITH can capture an unqualified target the rename
|
|
518
|
+
// gives a member's name (`resolveCteTarget` binds it as a write target);
|
|
519
|
+
// enclosing statements' CTEs cannot, so only `withClause` is consulted.
|
|
520
|
+
cteShadows: next => id.schema === undefined && (withClause?.ctes ?? []).some(c => eq(c.name, next)),
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Visit the clauses an UPDATE / DELETE evaluates against its TARGET row — the
|
|
525
|
+
* assignments, WHERE, RETURNING and mutation-context values — inside a frame
|
|
526
|
+
* binding the statement's target correlation name.
|
|
527
|
+
*
|
|
528
|
+
* That name is a qualifier, never a table: it is the mandatory alias of an
|
|
529
|
+
* inline subquery target (`update (select …) as v set … where v.k = 1`) or the
|
|
530
|
+
* collision-proof one the view-mutation lowering synthesizes for a named
|
|
531
|
+
* target. Renaming a table that happens to share it must leave `v.k` alone,
|
|
532
|
+
* exactly as a FROM alias is left alone — and the alias itself never moves, so
|
|
533
|
+
* rewriting the qualifier would only break the reference.
|
|
534
|
+
*/
|
|
535
|
+
function visitDmlTargetRowClauses(stmt, state) {
|
|
536
|
+
const frame = emptyTableFrame();
|
|
537
|
+
if (stmt.alias)
|
|
538
|
+
frame.bound.set(stmt.alias.toLowerCase(), null);
|
|
539
|
+
state.stack.push(frame);
|
|
540
|
+
try {
|
|
541
|
+
if (stmt.type === 'update')
|
|
542
|
+
stmt.assignments.forEach(a => visitTableRefs(a.value, state));
|
|
543
|
+
visitTableRefs(stmt.where, state);
|
|
544
|
+
(stmt.returning ?? []).forEach(r => {
|
|
545
|
+
if (r.type === 'column')
|
|
546
|
+
visitTableRefs(r.expr, state);
|
|
547
|
+
});
|
|
548
|
+
(stmt.contextValues ?? []).forEach(cv => visitTableRefs(cv.value, state));
|
|
549
|
+
}
|
|
550
|
+
finally {
|
|
551
|
+
state.stack.pop();
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Visit a `with inverse` assignment expression or `with defaults` entry
|
|
556
|
+
* expression with the row-image suppression forced ON, restoring the ambient
|
|
557
|
+
* flag after — the table-walker twin of the column walker's
|
|
558
|
+
* `visitRowImageForeignSubtree`. Unconditional on purpose: these subtrees are
|
|
559
|
+
* written-row context of their enclosing select wherever the walk entered, so a
|
|
560
|
+
* reached view body self-suppresses even though the entry point (rightly)
|
|
561
|
+
* passed no `rowImageContext` for it.
|
|
562
|
+
*/
|
|
563
|
+
function visitRowImageSubtree(expr, state) {
|
|
564
|
+
const ambient = state.rowImage;
|
|
565
|
+
state.rowImage = true;
|
|
566
|
+
try {
|
|
567
|
+
visitTableRefs(expr, state);
|
|
568
|
+
}
|
|
569
|
+
finally {
|
|
570
|
+
state.rowImage = ambient;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
function visitTableRefs(node, state) {
|
|
574
|
+
if (!node || state.stop)
|
|
575
|
+
return;
|
|
576
|
+
switch (node.type) {
|
|
577
|
+
case 'select': {
|
|
578
|
+
const stmt = node;
|
|
579
|
+
pushWithFrame(stmt.withClause, state);
|
|
580
|
+
try {
|
|
581
|
+
const frame = emptyTableFrame();
|
|
582
|
+
(stmt.from ?? []).forEach(f => collectFromBindings(f, state, frame));
|
|
583
|
+
frame.subtreeRoot = stmt;
|
|
584
|
+
state.stack.push(frame);
|
|
585
|
+
try {
|
|
586
|
+
(stmt.columns ?? []).forEach(c => {
|
|
587
|
+
if (c.type === 'column') {
|
|
588
|
+
visitTableRefs(c.expr, state);
|
|
589
|
+
// A `with inverse` assignment expr can embed a subquery naming any
|
|
590
|
+
// table; the assignment's target names a base COLUMN, untouched by a
|
|
591
|
+
// table rename (same as the `with defaults` clause below). Both sit
|
|
592
|
+
// in this body's FROM frame — but the expr is written-row context
|
|
593
|
+
// of the ENCLOSING select regardless of the walk's entry point: a
|
|
594
|
+
// bare unbound `new.<x>` there names an output column of this
|
|
595
|
+
// select (docs/sql-select.md § Result-column inverses), never a
|
|
596
|
+
// table called `new`, so the row-image suppression is forced over
|
|
597
|
+
// the subtree (the column walker forces `'foreign'` over the same
|
|
598
|
+
// one).
|
|
599
|
+
(c.inverse ?? []).forEach(a => visitRowImageSubtree(a.expr, state));
|
|
600
|
+
}
|
|
601
|
+
});
|
|
602
|
+
// `with defaults` clause: each entry's `expr` (an inserted-row default) can
|
|
603
|
+
// embed a subquery naming any table; the entry's `column` names a base
|
|
604
|
+
// COLUMN, untouched by a table rename. A defaults entry is documented
|
|
605
|
+
// self-contained — it cannot reference the inserted row — but nothing
|
|
606
|
+
// enforces that at CREATE VIEW time, so a bare unbound `new.` / `old.`
|
|
607
|
+
// there is pinned inert the same way (column-walker parity; such a ref
|
|
608
|
+
// could never plan as a table reference anyway).
|
|
609
|
+
(stmt.defaults ?? []).forEach(d => visitRowImageSubtree(d.expr, state));
|
|
610
|
+
(stmt.from ?? []).forEach(f => visitTableRefs(f, state));
|
|
611
|
+
visitTableRefs(stmt.where, state);
|
|
612
|
+
(stmt.groupBy ?? []).forEach(g => visitTableRefs(g, state));
|
|
613
|
+
visitTableRefs(stmt.having, state);
|
|
614
|
+
(stmt.orderBy ?? []).forEach(o => visitTableRefs(o.expr, state));
|
|
615
|
+
visitTableRefs(stmt.limit, state);
|
|
616
|
+
visitTableRefs(stmt.offset, state);
|
|
617
|
+
// Set-op legs stay inside this select's frames (mirroring the column
|
|
618
|
+
// walker): each leg is its own select and pushes its own frames on top.
|
|
619
|
+
visitTableRefs(stmt.union, state);
|
|
620
|
+
if (stmt.compound)
|
|
621
|
+
visitTableRefs(stmt.compound.select, state);
|
|
622
|
+
}
|
|
623
|
+
finally {
|
|
624
|
+
state.stack.pop();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
finally {
|
|
628
|
+
state.stack.pop();
|
|
629
|
+
}
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
case 'insert': {
|
|
633
|
+
const stmt = node;
|
|
634
|
+
pushWithFrame(stmt.withClause, state);
|
|
635
|
+
try {
|
|
636
|
+
visitDmlTarget(stmt.table, stmt.withClause, state);
|
|
637
|
+
visitTableRefs(stmt.source, state);
|
|
638
|
+
(stmt.upsertClauses ?? []).forEach(uc => {
|
|
639
|
+
(uc.assignments ?? []).forEach(a => visitTableRefs(a.value, state));
|
|
640
|
+
visitTableRefs(uc.where, state);
|
|
641
|
+
});
|
|
642
|
+
(stmt.returning ?? []).forEach(r => {
|
|
643
|
+
if (r.type === 'column')
|
|
644
|
+
visitTableRefs(r.expr, state);
|
|
645
|
+
});
|
|
646
|
+
(stmt.contextValues ?? []).forEach(cv => visitTableRefs(cv.value, state));
|
|
647
|
+
}
|
|
648
|
+
finally {
|
|
649
|
+
state.stack.pop();
|
|
650
|
+
}
|
|
651
|
+
break;
|
|
652
|
+
}
|
|
653
|
+
case 'update':
|
|
654
|
+
case 'delete': {
|
|
655
|
+
const stmt = node;
|
|
656
|
+
pushWithFrame(stmt.withClause, state);
|
|
657
|
+
try {
|
|
658
|
+
// An inline subquery target (`update (select …) as v set …`): `table` is a
|
|
659
|
+
// synthetic placeholder equal to the alias, not a table reference — the real
|
|
660
|
+
// references live inside the subquery body. Visited OUTSIDE the target frame
|
|
661
|
+
// below: the body cannot see its own correlation name.
|
|
662
|
+
if (stmt.targetSource)
|
|
663
|
+
visitTableRefs(stmt.targetSource, state);
|
|
664
|
+
else
|
|
665
|
+
visitDmlTarget(stmt.table, stmt.withClause, state);
|
|
666
|
+
visitDmlTargetRowClauses(stmt, state);
|
|
667
|
+
}
|
|
668
|
+
finally {
|
|
669
|
+
state.stack.pop();
|
|
670
|
+
}
|
|
671
|
+
break;
|
|
672
|
+
}
|
|
673
|
+
case 'values': {
|
|
674
|
+
const stmt = node;
|
|
675
|
+
stmt.values.forEach(row => row.forEach(v => visitTableRefs(v, state)));
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
case 'table': {
|
|
679
|
+
const ts = node;
|
|
680
|
+
// A FROM source. An unqualified name that a CTE in scope declares binds
|
|
681
|
+
// the CTE, not a same-named real table; a schema-qualified source
|
|
682
|
+
// (`main.zap`) is never a CTE. The alias, if any, does not matter here —
|
|
683
|
+
// `from zap z` still reads the real table.
|
|
684
|
+
if (!(ts.table.schema === undefined && isCteInScope(state, ts.table.name.toLowerCase()))) {
|
|
685
|
+
// The top of the stack IS this source's own FROM frame: `case 'table'`
|
|
686
|
+
// is reachable only from a select's `from` array (directly or through
|
|
687
|
+
// `join` — UPDATE/DELETE targets are typed `AST.SubquerySource`), and
|
|
688
|
+
// the select arm pushes the FROM frame before visiting it.
|
|
689
|
+
const sourceFrameIndex = state.stack.length - 1;
|
|
690
|
+
const ref = {
|
|
691
|
+
schema: ts.table.schema,
|
|
692
|
+
name: ts.table.name,
|
|
693
|
+
setName: next => { ts.table.name = next; },
|
|
694
|
+
// A FROM source re-resolves through the catalog, so it can carry
|
|
695
|
+
// the post-condition qualifier. Column qualifiers bound through
|
|
696
|
+
// this source are unaffected: their frame bindings were copied by
|
|
697
|
+
// value BEFORE anything under the FROM was visited, and a
|
|
698
|
+
// qualified `main.t2` source still exposes the bare qualifier
|
|
699
|
+
// `t2` to the rows it produces.
|
|
700
|
+
qualify: schemaName => { ts.table.schema = schemaName; },
|
|
701
|
+
// The catalog resolvers cannot see a CTE capturing the new name.
|
|
702
|
+
cteShadows: next => ts.table.schema === undefined && isCteInScope(state, next.toLowerCase()),
|
|
703
|
+
};
|
|
704
|
+
if (ts.alias === undefined) {
|
|
705
|
+
// Only an unaliased source exposes its own name as a qualifier;
|
|
706
|
+
// an aliased one exposes the alias, which a rename never moves.
|
|
707
|
+
ref.qualifierCollides = next => qualifierCollidesAt(state, next.toLowerCase(), sourceFrameIndex);
|
|
708
|
+
ref.aliasAs = aliasName => { ts.alias = aliasName; };
|
|
709
|
+
}
|
|
710
|
+
emit(state, ref);
|
|
711
|
+
}
|
|
712
|
+
break;
|
|
713
|
+
}
|
|
714
|
+
case 'join': {
|
|
715
|
+
const join = node;
|
|
716
|
+
visitTableRefs(join.left, state);
|
|
717
|
+
visitTableRefs(join.right, state);
|
|
718
|
+
visitTableRefs(join.condition, state);
|
|
719
|
+
break;
|
|
720
|
+
}
|
|
721
|
+
case 'functionSource': {
|
|
722
|
+
const fs = node;
|
|
723
|
+
fs.args.forEach(a => visitTableRefs(a, state));
|
|
724
|
+
break;
|
|
725
|
+
}
|
|
726
|
+
case 'subquerySource': {
|
|
727
|
+
const ss = node;
|
|
728
|
+
visitTableRefs(ss.subquery, state);
|
|
729
|
+
break;
|
|
730
|
+
}
|
|
731
|
+
case 'binary': {
|
|
732
|
+
const e = node;
|
|
733
|
+
visitTableRefs(e.left, state);
|
|
734
|
+
visitTableRefs(e.right, state);
|
|
735
|
+
break;
|
|
736
|
+
}
|
|
737
|
+
case 'unary':
|
|
738
|
+
case 'cast':
|
|
739
|
+
case 'collate': {
|
|
740
|
+
visitTableRefs(node.expr, state);
|
|
741
|
+
break;
|
|
742
|
+
}
|
|
743
|
+
case 'function': {
|
|
744
|
+
node.args.forEach(a => visitTableRefs(a, state));
|
|
745
|
+
break;
|
|
746
|
+
}
|
|
747
|
+
case 'subquery': {
|
|
748
|
+
visitTableRefs(node.query, state);
|
|
749
|
+
break;
|
|
750
|
+
}
|
|
751
|
+
case 'windowFunction': {
|
|
752
|
+
const wf = node;
|
|
753
|
+
visitTableRefs(wf.function, state);
|
|
754
|
+
visitTableRefs(wf.window, state);
|
|
755
|
+
break;
|
|
756
|
+
}
|
|
757
|
+
case 'windowDefinition': {
|
|
758
|
+
const wd = node;
|
|
759
|
+
(wd.partitionBy ?? []).forEach(p => visitTableRefs(p, state));
|
|
760
|
+
(wd.orderBy ?? []).forEach(o => visitTableRefs(o.expr, state));
|
|
761
|
+
break;
|
|
762
|
+
}
|
|
763
|
+
case 'case': {
|
|
764
|
+
const ce = node;
|
|
765
|
+
visitTableRefs(ce.baseExpr, state);
|
|
766
|
+
ce.whenThenClauses.forEach(wt => {
|
|
767
|
+
visitTableRefs(wt.when, state);
|
|
768
|
+
visitTableRefs(wt.then, state);
|
|
769
|
+
});
|
|
770
|
+
visitTableRefs(ce.elseExpr, state);
|
|
771
|
+
break;
|
|
772
|
+
}
|
|
773
|
+
case 'in': {
|
|
774
|
+
const ie = node;
|
|
775
|
+
visitTableRefs(ie.expr, state);
|
|
776
|
+
(ie.values ?? []).forEach(v => visitTableRefs(v, state));
|
|
777
|
+
visitTableRefs(ie.subquery, state);
|
|
778
|
+
break;
|
|
779
|
+
}
|
|
780
|
+
case 'exists': {
|
|
781
|
+
visitTableRefs(node.subquery, state);
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
case 'between': {
|
|
785
|
+
const be = node;
|
|
786
|
+
visitTableRefs(be.expr, state);
|
|
787
|
+
visitTableRefs(be.lower, state);
|
|
788
|
+
visitTableRefs(be.upper, state);
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
case 'column': {
|
|
792
|
+
const col = node;
|
|
793
|
+
if (!col.table)
|
|
794
|
+
break;
|
|
795
|
+
if (col.schema !== undefined) {
|
|
796
|
+
// A schema-qualified qualifier (`main.zap.k`) can never be a CTE, an
|
|
797
|
+
// alias, or a row image — always a real table reference. No `qualify`:
|
|
798
|
+
// it only matches when its written schema IS the renamed schema, so
|
|
799
|
+
// the rewrite post-condition holds by construction (a qualified name
|
|
800
|
+
// resolves by passthrough).
|
|
801
|
+
emit(state, {
|
|
802
|
+
schema: col.schema,
|
|
803
|
+
name: col.table,
|
|
804
|
+
setName: next => { col.table = next; },
|
|
805
|
+
});
|
|
806
|
+
break;
|
|
807
|
+
}
|
|
808
|
+
const qualifierLower = col.table.toLowerCase();
|
|
809
|
+
const resolved = resolveQualifier(state, qualifierLower);
|
|
810
|
+
if (resolved === undefined) {
|
|
811
|
+
// Nothing in scope binds the qualifier: a seedless expression context
|
|
812
|
+
// (a CHECK / index-predicate self-qualifier, or correlation the walk
|
|
813
|
+
// cannot see). Treat as a direct table reference — except the row
|
|
814
|
+
// image in a written-row context.
|
|
815
|
+
if (state.rowImage && (qualifierLower === 'new' || qualifierLower === 'old'))
|
|
816
|
+
break;
|
|
817
|
+
emit(state, {
|
|
818
|
+
schema: undefined,
|
|
819
|
+
name: col.table,
|
|
820
|
+
setName: next => { col.table = next; },
|
|
821
|
+
// Seedless, so it re-resolves through the catalog and can carry
|
|
822
|
+
// the post-condition qualifier. In practice this only ever fires
|
|
823
|
+
// on an already-unevaluable body: a CHECK / DEFAULT plans under
|
|
824
|
+
// its owning schema ONLY (`schemaAuthoredContext`), so a bare
|
|
825
|
+
// qualifier that resolves by falling through to ANOTHER schema —
|
|
826
|
+
// the only way the post-condition can fail here — is one the
|
|
827
|
+
// planner already rejects. Harmless, arguably an improvement.
|
|
828
|
+
qualify: schemaName => { col.schema = schemaName; },
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
else if (resolved.binding !== null) {
|
|
832
|
+
// Bound by an unaliased real table source: the qualifier IS that
|
|
833
|
+
// table's name. Report under the SOURCE's identity so a source in
|
|
834
|
+
// another schema doesn't match by bare name. No `qualify`: this
|
|
835
|
+
// reference binds through the FROM frame, not the catalog —
|
|
836
|
+
// qualifying the SOURCE (the `table` case above) is what preserves
|
|
837
|
+
// resolution; the qualifier just needs the new bare name.
|
|
838
|
+
const { binding, frameIndex } = resolved;
|
|
839
|
+
emit(state, {
|
|
840
|
+
schema: binding.schema,
|
|
841
|
+
name: binding.name,
|
|
842
|
+
setName: next => { col.table = next; },
|
|
843
|
+
// Evaluated at the BINDING frame — the source's own depth — so
|
|
844
|
+
// this answer always matches the source emit's (same frame,
|
|
845
|
+
// same memoized subtree set). No `aliasAs`: when this fires,
|
|
846
|
+
// the sink leaves the qualifier's old spelling in place and
|
|
847
|
+
// the SOURCE gains the alias that keeps it bound.
|
|
848
|
+
qualifierCollides: next => qualifierCollidesAt(state, next.toLowerCase(), frameIndex),
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
// resolved.binding === null: an alias / CTE / subquery / function source
|
|
852
|
+
// — the qualifier is not a table name; nothing to report.
|
|
853
|
+
break;
|
|
854
|
+
}
|
|
855
|
+
// Leaf nodes / DDL — nothing to recurse into for our purposes.
|
|
856
|
+
default:
|
|
857
|
+
break;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
//# sourceMappingURL=table-rename.js.map
|