@quereus/quereus 4.7.0 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (218) hide show
  1. package/dist/src/common/types.d.ts +19 -0
  2. package/dist/src/common/types.d.ts.map +1 -1
  3. package/dist/src/common/types.js.map +1 -1
  4. package/dist/src/core/database.d.ts +45 -1
  5. package/dist/src/core/database.d.ts.map +1 -1
  6. package/dist/src/core/database.js +212 -2
  7. package/dist/src/core/database.js.map +1 -1
  8. package/dist/src/core/statement.d.ts.map +1 -1
  9. package/dist/src/core/statement.js +86 -0
  10. package/dist/src/core/statement.js.map +1 -1
  11. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  12. package/dist/src/func/builtins/aggregate.js +3 -2
  13. package/dist/src/func/builtins/aggregate.js.map +1 -1
  14. package/dist/src/func/builtins/conversion.d.ts +5 -0
  15. package/dist/src/func/builtins/conversion.d.ts.map +1 -1
  16. package/dist/src/func/builtins/conversion.js +16 -2
  17. package/dist/src/func/builtins/conversion.js.map +1 -1
  18. package/dist/src/func/builtins/index.d.ts.map +1 -1
  19. package/dist/src/func/builtins/index.js +5 -2
  20. package/dist/src/func/builtins/index.js.map +1 -1
  21. package/dist/src/func/builtins/string.d.ts +2 -0
  22. package/dist/src/func/builtins/string.d.ts.map +1 -1
  23. package/dist/src/func/builtins/string.js +60 -4
  24. package/dist/src/func/builtins/string.js.map +1 -1
  25. package/dist/src/index.d.ts +11 -4
  26. package/dist/src/index.d.ts.map +1 -1
  27. package/dist/src/index.js +18 -3
  28. package/dist/src/index.js.map +1 -1
  29. package/dist/src/planner/building/constraint-builder.js +1 -1
  30. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  31. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
  32. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +121 -51
  33. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  34. package/dist/src/planner/rules/shared/index-style-context.d.ts +14 -4
  35. package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
  36. package/dist/src/planner/rules/shared/index-style-context.js +14 -4
  37. package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
  38. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  39. package/dist/src/runtime/emit/alter-table.js +212 -155
  40. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  41. package/dist/src/runtime/emit/assertion-drop-guard.d.ts +26 -12
  42. package/dist/src/runtime/emit/assertion-drop-guard.d.ts.map +1 -1
  43. package/dist/src/runtime/emit/assertion-drop-guard.js +45 -26
  44. package/dist/src/runtime/emit/assertion-drop-guard.js.map +1 -1
  45. package/dist/src/runtime/emit/assertion-rename-helpers.d.ts +6 -6
  46. package/dist/src/runtime/emit/assertion-rename-helpers.d.ts.map +1 -1
  47. package/dist/src/runtime/emit/assertion-rename-helpers.js +18 -20
  48. package/dist/src/runtime/emit/assertion-rename-helpers.js.map +1 -1
  49. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  50. package/dist/src/runtime/emit/binary.js +12 -10
  51. package/dist/src/runtime/emit/binary.js.map +1 -1
  52. package/dist/src/runtime/emit/column-rename-cascade.d.ts +84 -0
  53. package/dist/src/runtime/emit/column-rename-cascade.d.ts.map +1 -0
  54. package/dist/src/runtime/emit/column-rename-cascade.js +134 -0
  55. package/dist/src/runtime/emit/column-rename-cascade.js.map +1 -0
  56. package/dist/src/runtime/emit/drop-column-guards.d.ts +82 -53
  57. package/dist/src/runtime/emit/drop-column-guards.d.ts.map +1 -1
  58. package/dist/src/runtime/emit/drop-column-guards.js +155 -90
  59. package/dist/src/runtime/emit/drop-column-guards.js.map +1 -1
  60. package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
  61. package/dist/src/runtime/emit/drop-table.js +5 -0
  62. package/dist/src/runtime/emit/drop-table.js.map +1 -1
  63. package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
  64. package/dist/src/runtime/emit/drop-view.js +5 -0
  65. package/dist/src/runtime/emit/drop-view.js.map +1 -1
  66. package/dist/src/runtime/emit/expression-drop-guard.d.ts +57 -0
  67. package/dist/src/runtime/emit/expression-drop-guard.d.ts.map +1 -0
  68. package/dist/src/runtime/emit/expression-drop-guard.js +82 -0
  69. package/dist/src/runtime/emit/expression-drop-guard.js.map +1 -0
  70. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +26 -9
  71. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  72. package/dist/src/runtime/emit/materialized-view-helpers.js +37 -17
  73. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  74. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  75. package/dist/src/runtime/emit/materialized-view.js +3 -0
  76. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  77. package/dist/src/runtime/emit/remote-query.d.ts.map +1 -1
  78. package/dist/src/runtime/emit/remote-query.js +8 -3
  79. package/dist/src/runtime/emit/remote-query.js.map +1 -1
  80. package/dist/src/runtime/emit/scan.d.ts.map +1 -1
  81. package/dist/src/runtime/emit/scan.js +4 -1
  82. package/dist/src/runtime/emit/scan.js.map +1 -1
  83. package/dist/src/runtime/emit/schema-declarative.js +2 -2
  84. package/dist/src/runtime/emit/schema-declarative.js.map +1 -1
  85. package/dist/src/runtime/parallel-driver.d.ts +4 -3
  86. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  87. package/dist/src/runtime/parallel-driver.js +8 -3
  88. package/dist/src/runtime/parallel-driver.js.map +1 -1
  89. package/dist/src/runtime/types.d.ts +10 -0
  90. package/dist/src/runtime/types.d.ts.map +1 -1
  91. package/dist/src/runtime/types.js.map +1 -1
  92. package/dist/src/runtime/utils.d.ts.map +1 -1
  93. package/dist/src/runtime/utils.js +11 -0
  94. package/dist/src/runtime/utils.js.map +1 -1
  95. package/dist/src/schema/assertion.d.ts +58 -0
  96. package/dist/src/schema/assertion.d.ts.map +1 -1
  97. package/dist/src/schema/assertion.js +78 -0
  98. package/dist/src/schema/assertion.js.map +1 -1
  99. package/dist/src/schema/catalog-persistability.d.ts +11 -3
  100. package/dist/src/schema/catalog-persistability.d.ts.map +1 -1
  101. package/dist/src/schema/catalog-persistability.js +48 -29
  102. package/dist/src/schema/catalog-persistability.js.map +1 -1
  103. package/dist/src/schema/catalog.d.ts +22 -0
  104. package/dist/src/schema/catalog.d.ts.map +1 -1
  105. package/dist/src/schema/catalog.js +4 -1
  106. package/dist/src/schema/catalog.js.map +1 -1
  107. package/dist/src/schema/column-republication.d.ts +126 -0
  108. package/dist/src/schema/column-republication.d.ts.map +1 -0
  109. package/dist/src/schema/column-republication.js +121 -0
  110. package/dist/src/schema/column-republication.js.map +1 -0
  111. package/dist/src/schema/column-source-resolver.d.ts +29 -6
  112. package/dist/src/schema/column-source-resolver.d.ts.map +1 -1
  113. package/dist/src/schema/column-source-resolver.js +78 -9
  114. package/dist/src/schema/column-source-resolver.js.map +1 -1
  115. package/dist/src/schema/expression-dependents.d.ts +160 -0
  116. package/dist/src/schema/expression-dependents.d.ts.map +1 -0
  117. package/dist/src/schema/expression-dependents.js +228 -0
  118. package/dist/src/schema/expression-dependents.js.map +1 -0
  119. package/dist/src/schema/object-dependency-closure.d.ts +127 -0
  120. package/dist/src/schema/object-dependency-closure.d.ts.map +1 -0
  121. package/dist/src/schema/object-dependency-closure.js +117 -0
  122. package/dist/src/schema/object-dependency-closure.js.map +1 -0
  123. package/dist/src/schema/object-ref-resolver.d.ts +83 -0
  124. package/dist/src/schema/object-ref-resolver.d.ts.map +1 -0
  125. package/dist/src/schema/object-ref-resolver.js +135 -0
  126. package/dist/src/schema/object-ref-resolver.js.map +1 -0
  127. package/dist/src/schema/rename/column-rename.d.ts +228 -0
  128. package/dist/src/schema/rename/column-rename.d.ts.map +1 -0
  129. package/dist/src/schema/rename/column-rename.js +1225 -0
  130. package/dist/src/schema/rename/column-rename.js.map +1 -0
  131. package/dist/src/schema/rename/self-qualifier-strip.d.ts +29 -0
  132. package/dist/src/schema/rename/self-qualifier-strip.d.ts.map +1 -0
  133. package/dist/src/schema/rename/self-qualifier-strip.js +267 -0
  134. package/dist/src/schema/rename/self-qualifier-strip.js.map +1 -0
  135. package/dist/src/schema/rename/shared.d.ts +108 -0
  136. package/dist/src/schema/rename/shared.d.ts.map +1 -0
  137. package/dist/src/schema/rename/shared.js +49 -0
  138. package/dist/src/schema/rename/shared.js.map +1 -0
  139. package/dist/src/schema/rename/table-rename.d.ts +263 -0
  140. package/dist/src/schema/rename/table-rename.d.ts.map +1 -0
  141. package/dist/src/schema/rename/table-rename.js +860 -0
  142. package/dist/src/schema/rename/table-rename.js.map +1 -0
  143. package/dist/src/schema/rename-rewriter.d.ts +18 -278
  144. package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
  145. package/dist/src/schema/rename-rewriter.js +17 -1506
  146. package/dist/src/schema/rename-rewriter.js.map +1 -1
  147. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  148. package/dist/src/schema/schema-differ.js +228 -21
  149. package/dist/src/schema/schema-differ.js.map +1 -1
  150. package/dist/src/types/builtin-types.d.ts.map +1 -1
  151. package/dist/src/types/builtin-types.js +12 -28
  152. package/dist/src/types/builtin-types.js.map +1 -1
  153. package/dist/src/types/cast-semantics.d.ts +7 -5
  154. package/dist/src/types/cast-semantics.d.ts.map +1 -1
  155. package/dist/src/types/cast-semantics.js +20 -9
  156. package/dist/src/types/cast-semantics.js.map +1 -1
  157. package/dist/src/types/index.d.ts +1 -1
  158. package/dist/src/types/index.d.ts.map +1 -1
  159. package/dist/src/types/index.js +1 -1
  160. package/dist/src/types/index.js.map +1 -1
  161. package/dist/src/types/registry.d.ts.map +1 -1
  162. package/dist/src/types/registry.js +2 -1
  163. package/dist/src/types/registry.js.map +1 -1
  164. package/dist/src/types/temporal-types.d.ts +11 -0
  165. package/dist/src/types/temporal-types.d.ts.map +1 -1
  166. package/dist/src/types/temporal-types.js +82 -0
  167. package/dist/src/types/temporal-types.js.map +1 -1
  168. package/dist/src/util/abort-signal.d.ts +25 -0
  169. package/dist/src/util/abort-signal.d.ts.map +1 -0
  170. package/dist/src/util/abort-signal.js +40 -0
  171. package/dist/src/util/abort-signal.js.map +1 -0
  172. package/dist/src/util/affinity.d.ts +2 -1
  173. package/dist/src/util/affinity.d.ts.map +1 -1
  174. package/dist/src/util/affinity.js +16 -5
  175. package/dist/src/util/affinity.js.map +1 -1
  176. package/dist/src/util/ast-spine-clone.d.ts +2 -2
  177. package/dist/src/util/ast-spine-clone.js +2 -2
  178. package/dist/src/util/value-text.d.ts +48 -0
  179. package/dist/src/util/value-text.d.ts.map +1 -0
  180. package/dist/src/util/value-text.js +35 -0
  181. package/dist/src/util/value-text.js.map +1 -0
  182. package/dist/src/vtab/concurrency.d.ts +15 -0
  183. package/dist/src/vtab/concurrency.d.ts.map +1 -1
  184. package/dist/src/vtab/concurrency.js +17 -0
  185. package/dist/src/vtab/concurrency.js.map +1 -1
  186. package/dist/src/vtab/memory/layer/base.d.ts +10 -0
  187. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  188. package/dist/src/vtab/memory/layer/base.js +10 -0
  189. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  190. package/dist/src/vtab/memory/layer/interface.d.ts +24 -0
  191. package/dist/src/vtab/memory/layer/interface.d.ts.map +1 -1
  192. package/dist/src/vtab/memory/layer/manager.d.ts +21 -3
  193. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  194. package/dist/src/vtab/memory/layer/manager.js +118 -93
  195. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  196. package/dist/src/vtab/memory/layer/transaction.d.ts +11 -0
  197. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  198. package/dist/src/vtab/memory/layer/transaction.js +19 -0
  199. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  200. package/dist/src/vtab/memory/module.d.ts +41 -0
  201. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  202. package/dist/src/vtab/memory/module.js +41 -0
  203. package/dist/src/vtab/memory/module.js.map +1 -1
  204. package/dist/src/vtab/memory/table.d.ts +12 -1
  205. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  206. package/dist/src/vtab/memory/table.js +12 -1
  207. package/dist/src/vtab/memory/table.js.map +1 -1
  208. package/dist/src/vtab/module.d.ts +33 -0
  209. package/dist/src/vtab/module.d.ts.map +1 -1
  210. package/dist/src/vtab/test-support/commit-stall.d.ts +53 -0
  211. package/dist/src/vtab/test-support/commit-stall.d.ts.map +1 -0
  212. package/dist/src/vtab/test-support/commit-stall.js +73 -0
  213. package/dist/src/vtab/test-support/commit-stall.js.map +1 -0
  214. package/dist/src/vtab/test-support/committed-read-conformance.d.ts +96 -0
  215. package/dist/src/vtab/test-support/committed-read-conformance.d.ts.map +1 -0
  216. package/dist/src/vtab/test-support/committed-read-conformance.js +364 -0
  217. package/dist/src/vtab/test-support/committed-read-conformance.js.map +1 -0
  218. package/package.json +3 -3
@@ -0,0 +1,1225 @@
1
+ import { spineCloneAst } from '../../util/ast-spine-clone.js';
2
+ import { eq, objectRefKeySchema, rewriteEach } from './shared.js';
3
+ // ──────────────────────────────────────────────────────────────────────
4
+ // Column rename
5
+ // ──────────────────────────────────────────────────────────────────────
6
+ /**
7
+ * Rewrite column references inside a full statement/expression AST, resolving
8
+ * unqualified refs against the FROM scopes the walk descends (no implicit seed,
9
+ * unlike {@link renameColumnInCheckExpression}). The walk descends a select
10
+ * body's trailing `with defaults (…)` clause ({@link AST.SelectStmt.defaults}),
11
+ * whose entry exprs evaluate in the body's FROM scope.
12
+ *
13
+ * `rowImage` says what a bare, unbound `new.` / `old.` qualifier means in the
14
+ * walked expression — independent of the (absent) seed; see
15
+ * {@link RowImageContext} and {@link matchesRowImage}. A view / assertion body
16
+ * is `'none'` (no written-row context — `new.x` is an ordinary table
17
+ * reference); another table's CHECK / DEFAULT / generated body is `'foreign'`
18
+ * (written-row context whose image belongs to that table, so the qualifier
19
+ * names nothing this walk cares about).
20
+ *
21
+ * When `resolveColumnInSource` is supplied, the scope walk consults it at each
22
+ * inner FROM frame so an unqualified ref that legitimately binds to a like-named
23
+ * column on a subquery's own FROM source is NOT false-captured by an enclosing
24
+ * binding (e.g. a `with defaults` expr `cap + (select max(cap) from lim)` under
25
+ * a `t.cap` rename must leave the inner `lim.cap` untouched). The same callback
26
+ * infrastructure backs {@link renameColumnInCheckExpression}; both the forward
27
+ * propagation (live schema lookup) and the differ's inverse reconcile
28
+ * (declared-side resolver) pass it so the two stay in parity.
29
+ */
30
+ export function renameColumnInAst(node, tableName, oldColName, newColName, resolve, targetKey, rowImage, resolveColumnInSource) {
31
+ if (!node)
32
+ return false;
33
+ const state = {
34
+ tableName: tableName.toLowerCase(),
35
+ oldCol: oldColName.toLowerCase(),
36
+ newCol: newColName,
37
+ resolveRef: resolve,
38
+ targetKey,
39
+ scopeStack: [],
40
+ changed: false,
41
+ resolveColumnInSource,
42
+ rowImage,
43
+ };
44
+ visitColumnRename(node, state);
45
+ return state.changed;
46
+ }
47
+ /**
48
+ * Rewrite a column reference inside a CHECK expression. Unlike
49
+ * `renameColumnInAst`, this entry point seeds the scope stack with an
50
+ * implicit unaliased binding to `tableName` so top-level unqualified
51
+ * `ColumnExpr` nodes resolve to the owning table. CHECK expressions
52
+ * cannot reference other tables at top level, so the implicit binding
53
+ * is safe there.
54
+ *
55
+ * When `resolveColumnInSource` is supplied, the scope walk consults it at
56
+ * each inner FROM frame: if any real-table source in that frame exposes
57
+ * `oldColName`, the unqualified ref binds inside the subquery and the
58
+ * walk stops before reaching the seed. This stops the rewriter from
59
+ * false-positively rewriting an inner unqualified ref that legitimately
60
+ * binds to a like-named column on the subquery's FROM (e.g.
61
+ * `check ((select min(v) from u) > 0)` when `u` also has a `v` column).
62
+ *
63
+ * Limitation: subquery / function-source inner sources are not asked (the
64
+ * rewriter would need recursive column-set inference on their bodies), so an
65
+ * unqualified ref binding only through one of those falls through to the seed.
66
+ * An ALIASED real source is asked like any other — an alias adds a qualifier,
67
+ * it does not take the source's columns out of scope. `renameColumnInAst`
68
+ * shares the same callback (passed by the view-body callers) and the same
69
+ * residual.
70
+ *
71
+ * The seed and the `new.` / `old.` **row-image** namespace are INDEPENDENT: the
72
+ * seed answers how *unqualified* refs bind, while the explicit `rowImage`
73
+ * argument answers what a bare, unbound `new.` / `old.` qualifier names (see
74
+ * {@link RowImageContext}). A CHECK / DEFAULT / generated body of the renamed
75
+ * table itself passes `'own'` — those expressions evaluate against the row
76
+ * being written, so `check (new.a > 0)` (`docs/sql-ddl.md` § CHECK Constraints)
77
+ * names the owning table's row image and matches. A partial-index predicate is
78
+ * seeded too but passes `'none'`: it describes rows already stored, with no
79
+ * written-row context.
80
+ *
81
+ * The `'own'` match is scope-**aware**, not depth-blind, because `new` and `old`
82
+ * are not reserved words in this parser: `create table "new" (…)` is legal, so a
83
+ * CHECK may legitimately contain `(select max("new".a) from "new")`. The match
84
+ * therefore requires that no FROM/WITH frame above the seed rebind the
85
+ * qualifier — see `matchesRowImage`.
86
+ */
87
+ export function renameColumnInCheckExpression(expr, tableName, oldColName, newColName, resolve, targetKey, rowImage, resolveColumnInSource) {
88
+ if (!expr)
89
+ return false;
90
+ const state = {
91
+ tableName: tableName.toLowerCase(),
92
+ oldCol: oldColName.toLowerCase(),
93
+ newCol: newColName,
94
+ resolveRef: resolve,
95
+ targetKey,
96
+ scopeStack: [],
97
+ changed: false,
98
+ resolveColumnInSource,
99
+ rowImage,
100
+ };
101
+ const frame = emptyFrame();
102
+ frame.qualifiers.set(state.tableName, state.targetKey);
103
+ state.scopeStack.push(frame);
104
+ try {
105
+ visitColumnRename(expr, state);
106
+ }
107
+ finally {
108
+ state.scopeStack.pop();
109
+ }
110
+ return state.changed;
111
+ }
112
+ /**
113
+ * Sentinel rename target for the read-only column probes: the two below, and
114
+ * the drop verb's republication fixpoint
115
+ * (`schema/column-republication.ts`), which probes exposure the same way. No
116
+ * user column can hold this name, and the probe rewrites *to* it, so it can
117
+ * never collide with anything the walk is looking for.
118
+ */
119
+ export const PROBE_COLUMN_NAME = '__quereus_column_probe__';
120
+ /**
121
+ * Whether `node` refers to `tableName`.`columnName` — read-only with respect to
122
+ * the caller's AST; a throwaway {@link spineCloneAst} copy is what gets
123
+ * rewritten.
124
+ *
125
+ * Same equivalence {@link tableReferencedInAst} establishes for the table verb,
126
+ * and for the same reason: "refers to" must mean "would have been rewritten by
127
+ * `ALTER TABLE … RENAME COLUMN`", or a DROP guard built on it refuses a
128
+ * different set of cases than the rename follows. See that function's comment
129
+ * for why the column verb reaches it by clone+sentinel rather than by `dryRun`.
130
+ *
131
+ * `resolveColumnInSource` is **not optional in practice**: without it the walk
132
+ * has no way to tell that an unqualified ref inside a subquery binds to a
133
+ * like-named column on the subquery's own FROM source, and a caller asking
134
+ * "does this body name `t.v`?" gets `true` for a body whose only `v` is
135
+ * another table's. Every caller should pass the catalog-backed resolver
136
+ * (`buildColumnSourceResolver`).
137
+ *
138
+ * NOTE: one spine clone plus one walk per probe, and the DROP COLUMN guard probes every
139
+ * live assertion. Trivial at the handful-of-assertions scale schemas have today, and it
140
+ * only runs on DDL; if a schema ever carries assertions by the hundred, filter by
141
+ * `tableReferencedInAst` (no clone) before paying for the column probe.
142
+ */
143
+ export function columnReferencedInAst(node, tableName, columnName, resolve, targetKey, rowImage, resolveColumnInSource) {
144
+ if (!node)
145
+ return false;
146
+ return renameColumnInAst(spineCloneAst(node), tableName, columnName, PROBE_COLUMN_NAME, resolve, targetKey, rowImage, resolveColumnInSource);
147
+ }
148
+ /**
149
+ * {@link columnReferencedInAst} for an expression that resolves unqualified refs
150
+ * against an implicit binding to `tableName` — a CHECK expression or a
151
+ * partial-index predicate. Uses {@link renameColumnInCheckExpression}'s seeded
152
+ * entry point so the probe answers for exactly the scope that expression is
153
+ * planned in.
154
+ */
155
+ export function columnReferencedInCheckExpression(expr, tableName, columnName, resolve, targetKey, rowImage, resolveColumnInSource) {
156
+ if (!expr)
157
+ return false;
158
+ return renameColumnInCheckExpression(spineCloneAst(expr), tableName, columnName, PROBE_COLUMN_NAME, resolve, targetKey, rowImage, resolveColumnInSource);
159
+ }
160
+ /**
161
+ * Rewrite the renamed column inside every partial-index predicate of `indexes`,
162
+ * in place. A predicate resolves unqualified refs against the indexed table
163
+ * itself — the same implicit seed a CHECK expression uses — so
164
+ * {@link renameColumnInCheckExpression} is the correct entry point here, not
165
+ * {@link renameColumnInAst}.
166
+ *
167
+ * The predicate `Expression` is shared by reference between the catalog's
168
+ * `TableSchema`, any module-local copy of it, and — for a unique partial index —
169
+ * the `derivedFromIndex` UNIQUE constraint that carries the same predicate.
170
+ * Rewriting in place keeps all of them in step; cloning would strand the derived
171
+ * constraint on the old AST.
172
+ *
173
+ * Idempotent: once rewritten, nothing names `oldColName` any more, so a second
174
+ * call with the same pair returns false without touching the AST.
175
+ *
176
+ * The parameter is structurally typed rather than `IndexSchema[]` so this module
177
+ * stays free of catalog imports; `IndexSchema` satisfies it.
178
+ *
179
+ * Row-image mode is `'none'`: a partial-index predicate describes rows already
180
+ * stored, so it has no written-row context and a bare `new.` / `old.` qualifier
181
+ * there is an ordinary table reference — the same asymmetry
182
+ * {@link import('./table-rename.js').renameTableInIndexPredicates} records by
183
+ * passing no `rowImageContext`. (An indexed table literally named `new` still
184
+ * matches through the seed binding, so this changes no answer for the case
185
+ * that plans.)
186
+ */
187
+ export function renameColumnInIndexPredicates(indexes, tableName, oldColName, newColName, resolve, targetKey, resolveColumnInSource) {
188
+ return rewriteEach(indexes, idx => idx.predicate, expr => renameColumnInCheckExpression(expr, tableName, oldColName, newColName, resolve, targetKey, 'none', resolveColumnInSource));
189
+ }
190
+ /**
191
+ * Rewrite the renamed column inside every CHECK constraint expression of `checks`
192
+ * belonging to the renamed table itself, in place. The seeded-scope entry point
193
+ * applies for the same reason it does for a partial-index predicate: a CHECK
194
+ * resolves unqualified refs against its owning table.
195
+ *
196
+ * Only pass the renamed table's OWN checks — a CHECK on a *different* table that
197
+ * happens to reference the renamed table needs {@link renameColumnInAst}, whose
198
+ * walk has no implicit seed. That own-table contract is also why the row-image
199
+ * mode is hardcoded `'own'` here: a CHECK evaluates against the row being
200
+ * written, whose image IS the renamed table's.
201
+ */
202
+ export function renameColumnInCheckConstraints(checks, tableName, oldColName, newColName, resolve, targetKey, resolveColumnInSource) {
203
+ return rewriteEach(checks, cc => cc.expr, expr => renameColumnInCheckExpression(expr, tableName, oldColName, newColName, resolve, targetKey, 'own', resolveColumnInSource));
204
+ }
205
+ /**
206
+ * Rewrite the renamed column inside the two expressions a `ColumnSchema` can carry —
207
+ * a column DEFAULT (`b integer default (new.a + 1)`, however authored: inline, or via
208
+ * `ALTER TABLE … ALTER COLUMN … SET DEFAULT`, which writes the same field) and a
209
+ * generated column's body (`g integer generated always as (a + 1)`) — in place, for
210
+ * every column of the renamed table itself.
211
+ *
212
+ * The seeded {@link renameColumnInCheckExpression} entry point with row-image mode
213
+ * `'own'` is correct for both:
214
+ *
215
+ * - The implicit unaliased binding to the owning table is what makes a generated
216
+ * column's **bare** `a` resolve — it has no FROM clause to bind against, exactly like
217
+ * a CHECK.
218
+ * - The `'own'` mode is what makes a default's `new.a` resolve. A default is evaluated
219
+ * against the row being written, so that qualifier names this table's row image, not
220
+ * anything a FROM binds.
221
+ *
222
+ * Case folding (`NEW.A`) and the shadowing edge (a real table literally named `new`,
223
+ * reached from a subquery inside the expression) therefore behave exactly as they do for
224
+ * a CHECK, because the same walk decides all three.
225
+ *
226
+ * Only pass the renamed table's OWN columns. A default on a *different* table can reach
227
+ * this table only through a subquery, so an unqualified ref there must bind inside that
228
+ * subquery's FROM — that caller wants {@link renameColumnInAst}, whose walk has no seed.
229
+ *
230
+ * Same sharing and idempotence story as {@link renameColumnInCheckConstraints}: a
231
+ * module's rename hook rebuilds only the renamed column's `ColumnSchema` and keeps every
232
+ * other one by reference, and even the rebuilt one carries the SAME expression nodes
233
+ * (`buildConstraintsFromColumn` passes them into the reconstructed `ColumnDef` by
234
+ * reference and `columnDefToSchema` assigns them straight back), so one in-place rewrite
235
+ * reaches every holder and a second call with the same pair is a no-op.
236
+ *
237
+ * The parameter is structurally typed rather than `ColumnSchema[]` so this module stays
238
+ * free of catalog imports; `ColumnSchema` satisfies it.
239
+ */
240
+ export function renameColumnInColumnExpressions(columns, tableName, oldColName, newColName, resolve, targetKey, resolveColumnInSource) {
241
+ const rewrite = (expr) => renameColumnInCheckExpression(expr, tableName, oldColName, newColName, resolve, targetKey, 'own', resolveColumnInSource);
242
+ // Both walks always run — `||` on the results, not short-circuited between them.
243
+ const defaultsChanged = rewriteEach(columns, c => c.defaultValue ?? undefined, rewrite);
244
+ const generatedChanged = rewriteEach(columns, c => c.generatedExpr, rewrite);
245
+ return defaultsChanged || generatedChanged;
246
+ }
247
+ /**
248
+ * Whether `body`'s top-level result columns publish the renamed column under
249
+ * `newColName` — i.e. whether rewriting this body shifted the name its readers
250
+ * see. Call AFTER the body rewrite: the predicate compares against the NEW
251
+ * name, exactly as the CTE exposure analysis it shares its internals with
252
+ * ({@link cteExposesRenamedColumn} / `isResultColumnExposure`). An explicit
253
+ * result-column alias suppresses exposure; a bare projection of the renamed
254
+ * column, or a `*` / `t.*` / `a.*` whose frame binds the target, exposes it —
255
+ * a FROM alias is no obstacle, since it only changes which qualifier names the
256
+ * source.
257
+ *
258
+ * An explicit view / materialized-view column list pins the published names
259
+ * regardless of the body — that guard belongs at the call site
260
+ * (`ViewSchema.columns` / `TableDerivation.columns` play the role
261
+ * `cte.columns` plays inside the CTE analysis; this predicate never sees them).
262
+ */
263
+ export function bodyExposesRenamedColumn(body, tableName, oldColName, newColName, resolve, targetKey, resolveColumnInSource) {
264
+ if (!body)
265
+ return false;
266
+ const state = {
267
+ tableName: tableName.toLowerCase(),
268
+ oldCol: oldColName.toLowerCase(),
269
+ newCol: newColName,
270
+ resolveRef: resolve,
271
+ targetKey,
272
+ scopeStack: [],
273
+ changed: false,
274
+ resolveColumnInSource,
275
+ // Exposure is a question about published output names, not row images —
276
+ // `'none'` unconditionally (a `new.`-qualified projection is never a bare
277
+ // passthrough, so the mode cannot matter to the answer).
278
+ rowImage: 'none',
279
+ };
280
+ return queryExposesRenamedColumn(body, state);
281
+ }
282
+ /**
283
+ * Sentinel target key for {@link bodyPublishesColumnNamed}'s throwaway walk
284
+ * state: the frame machinery wants a rename target, but this probe must match
285
+ * nothing — real object keys are `<schema>.<name>` built from identifiers,
286
+ * which never contain a NUL.
287
+ */
288
+ const PROBE_TARGET_KEY = '\u0000.\u0000';
289
+ /**
290
+ * Whether `body`'s top-level result columns publish a column named
291
+ * `columnName`, from ANY origin: an explicit alias, an unaliased bare
292
+ * projection of a column so named (wherever it binds), or a `*` / `t.*` whose
293
+ * bound real-table source exposes it (asked through `resolveColumnInSource`).
294
+ *
295
+ * The collision half of the rename-cascade pre-flight
296
+ * (`runtime/emit/column-rename-cascade.ts`): run against the PRISTINE
297
+ * (pre-rewrite) body with `columnName = newCol` — the rename target cannot
298
+ * publish `newCol` before the rename, so anything found is necessarily a
299
+ * DIFFERENT column the shifted name would collide with.
300
+ *
301
+ * Shares the walker's scope residual: a `*` over a subquery or function source
302
+ * has no askable column set, so a publication arriving only that way is not
303
+ * seen. A `*` over an aliased REAL source is seen — the alias binds like any
304
+ * other qualifier.
305
+ */
306
+ export function bodyPublishesColumnNamed(body, columnName, resolve, resolveColumnInSource) {
307
+ if (!body || body.type !== 'select')
308
+ return false;
309
+ const select = body;
310
+ const nameLower = columnName.toLowerCase();
311
+ const state = {
312
+ tableName: '\u0000',
313
+ oldCol: '\u0000',
314
+ newCol: '\u0000',
315
+ resolveRef: resolve,
316
+ targetKey: PROBE_TARGET_KEY,
317
+ scopeStack: [],
318
+ changed: false,
319
+ resolveColumnInSource,
320
+ rowImage: 'none',
321
+ };
322
+ const withFrame = analyzeWithFrame(select.withClause, state);
323
+ state.scopeStack.push(withFrame);
324
+ try {
325
+ const frame = buildScopeFrame(select.from, state);
326
+ for (const col of select.columns ?? []) {
327
+ if (resultColumnPublishesName(col, frame, nameLower, resolveColumnInSource))
328
+ return true;
329
+ }
330
+ return false;
331
+ }
332
+ finally {
333
+ state.scopeStack.pop();
334
+ }
335
+ }
336
+ /** One result column's published-name test for {@link bodyPublishesColumnNamed}. */
337
+ function resultColumnPublishesName(col, frame, nameLower, resolveColumnInSource) {
338
+ if (col.type === 'all') {
339
+ if (!resolveColumnInSource)
340
+ return false;
341
+ if (col.table === undefined) {
342
+ return frame.realSources.some(src => resolveColumnInSource(src.schemaLower, src.nameLower, nameLower));
343
+ }
344
+ const key = frame.qualifiers.get(col.table.toLowerCase());
345
+ if (key === undefined)
346
+ return false;
347
+ return frame.realSources.some(src => src.key === key
348
+ && resolveColumnInSource(src.schemaLower, src.nameLower, nameLower));
349
+ }
350
+ if (col.alias !== undefined)
351
+ return col.alias.toLowerCase() === nameLower;
352
+ return col.expr.type === 'column' && col.expr.name.toLowerCase() === nameLower;
353
+ }
354
+ function emptyFrame() {
355
+ return {
356
+ qualifiers: new Map(),
357
+ ctesExposingRenamed: new Set(),
358
+ ctesInScope: new Set(),
359
+ ctesShadowingSource: new Set(),
360
+ realSources: [],
361
+ };
362
+ }
363
+ function buildScopeFrame(from, state) {
364
+ const frame = emptyFrame();
365
+ if (!from)
366
+ return frame;
367
+ for (const item of from) {
368
+ collectFromBindings(item, state, frame);
369
+ }
370
+ return frame;
371
+ }
372
+ /**
373
+ * Bind a FROM source's one qualifier in `frame`, FIRST-write-wins.
374
+ *
375
+ * The order only matters when two sources in one FROM claim the same qualifier
376
+ * (`from t join u as t`), which is legal here — the planner accepts it and
377
+ * resolves a qualified ref to the FIRST such source in FROM order (`select t.k
378
+ * from t join u as t` reads `t`; `from u as t join t` reads `u`; a BARE `k` is
379
+ * rejected as ambiguous either way). Measured against the engine, not assumed.
380
+ * Skipping the overwrite is what keeps {@link resolveQualifierBinding} agreeing
381
+ * with that, now that one map holds aliases and bare names together — a
382
+ * two-map lookup used to answer alias-first, which matched the planner only
383
+ * when the aliased source came first.
384
+ *
385
+ * First-wins matches how the planner resolves a qualified COLUMN, but the planner
386
+ * expands a qualified STAR by name-matching every source, so `select t.* from u as
387
+ * t join t` yields both sources' columns while this walk sees only `u` — the
388
+ * engine disagreeing with itself about a duplicated qualifier, tracked by
389
+ * `bug-duplicate-from-qualifier-resolves-inconsistently`. Making this walk match
390
+ * the star would just pick the other side of the disagreement; the ticket's fix is
391
+ * to reject the duplicate at build time, after which the question stops existing.
392
+ */
393
+ function bindQualifier(frame, qualifierLower, key) {
394
+ if (!frame.qualifiers.has(qualifierLower))
395
+ frame.qualifiers.set(qualifierLower, key);
396
+ }
397
+ function collectFromBindings(item, state, frame) {
398
+ switch (item.type) {
399
+ case 'table': {
400
+ const ts = item;
401
+ const name = ts.table.name.toLowerCase();
402
+ // Unqualified reference to a CTE in scope — the CTE shadows any
403
+ // same-named real table. Whether it re-exposes the renamed column
404
+ // determines whether unqualified refs against this source rewrite.
405
+ if (ts.table.schema === undefined && isCteInScope(state, name)) {
406
+ if (isCteExposingInScope(state, name)) {
407
+ // One qualifier either way — the alias when present, the CTE's
408
+ // own name otherwise — and it both binds unqualified refs and
409
+ // serves as a qualifier for refs like `a.k`.
410
+ bindQualifier(frame, ts.alias ? ts.alias.toLowerCase() : name, state.targetKey);
411
+ }
412
+ else {
413
+ // Shadowing-but-not-exposing: the source binds to the CTE
414
+ // row source, not the renamed real table. Record the
415
+ // qualifier (alias if present, otherwise the source name)
416
+ // so qualified column refs against it don't short-circuit
417
+ // to the renamed table.
418
+ frame.ctesShadowingSource.add(ts.alias ? ts.alias.toLowerCase() : name);
419
+ }
420
+ // Shadowing-but-not-exposing: do not bind as the renamed table.
421
+ break;
422
+ }
423
+ // Resolve the source the way the planner would for this body: home
424
+ // schema path for an unqualified name, passthrough for a qualified
425
+ // one. The KEY is what every capture / qualifier decision compares —
426
+ // a bare `from t` binds the renamed table only when it RESOLVES to
427
+ // it, and `from other_schema.t z` never does just because the bare
428
+ // names collide.
429
+ const key = state.resolveRef(ts.table.schema, ts.table.name);
430
+ if (key === undefined)
431
+ break;
432
+ // The source's ONE qualifier: its alias when it has one, its bare
433
+ // name otherwise. Either way it binds unqualified refs — an alias
434
+ // does not take the source's columns out of scope — and either way
435
+ // an aliased source stops answering to its bare name (`select t.x
436
+ // from t a` does not plan, so `t` must not register).
437
+ bindQualifier(frame, ts.alias ? ts.alias.toLowerCase() : name, key);
438
+ // Record as a real-table source (under its RESOLVED schema) so the
439
+ // unqualified-scope walk can ask whether this source exposes the
440
+ // renamed column. Both aliased and unaliased real sources are
441
+ // recorded — asking "does u expose col v" is the same question
442
+ // regardless of any alias.
443
+ frame.realSources.push({ key, schemaLower: objectRefKeySchema(key, name), nameLower: name });
444
+ break;
445
+ }
446
+ case 'join': {
447
+ const join = item;
448
+ collectFromBindings(join.left, state, frame);
449
+ collectFromBindings(join.right, state, frame);
450
+ break;
451
+ }
452
+ case 'subquerySource':
453
+ case 'functionSource':
454
+ // No askable column set without recursive inference on the inner
455
+ // body, so these register no qualifier and no real source — the
456
+ // residual tracked by
457
+ // `bug-column-verbs-blind-to-star-over-subquery-source`.
458
+ break;
459
+ }
460
+ }
461
+ /**
462
+ * Innermost-first walk: an inner non-exposing same-name CTE shadows an
463
+ * outer exposing one, so a `ctesInScope` hit without a matching
464
+ * `ctesExposingRenamed` entry wins. `isCteInScope` (below) intentionally
465
+ * stays OR-shaped — it only gates "is this source a CTE rather than a
466
+ * real table?", a question for which any enclosing CTE suffices.
467
+ */
468
+ function isCteExposingInScope(state, name) {
469
+ for (let i = state.scopeStack.length - 1; i >= 0; i--) {
470
+ const frame = state.scopeStack[i];
471
+ if (frame.ctesExposingRenamed.has(name))
472
+ return true;
473
+ if (frame.ctesInScope.has(name))
474
+ return false;
475
+ }
476
+ return false;
477
+ }
478
+ function isCteInScope(state, name) {
479
+ for (const frame of state.scopeStack) {
480
+ if (frame.ctesInScope.has(name))
481
+ return true;
482
+ }
483
+ return false;
484
+ }
485
+ /**
486
+ * Whether a BARE (unqualified) column ref at the current position binds the
487
+ * renamed table. Innermost-first: a closer same-name CTE shadows an outer
488
+ * binding to the renamed real table. When a `resolveColumnInSource` callback is
489
+ * configured, also stop at any inner FROM frame whose real sources expose
490
+ * `oldCol` — the unqualified ref binds inside that frame and an outer seed
491
+ * binding must not capture it.
492
+ *
493
+ * Aliased and unaliased sources bind alike here, and the shadow scan over
494
+ * `realSources` widens in step (it records both), so `from u b` still stops an
495
+ * outer capture of a bare `x` that `u` exposes.
496
+ */
497
+ function unqualifiedRefBindsTarget(state) {
498
+ for (let i = state.scopeStack.length - 1; i >= 0; i--) {
499
+ const frame = state.scopeStack[i];
500
+ if (frame.ctesInScope.has(state.tableName))
501
+ return false;
502
+ if (state.resolveColumnInSource && frame.realSources.length > 0) {
503
+ for (const src of frame.realSources) {
504
+ // The renamed table itself trivially exposes oldCol; defer to
505
+ // the binding check below so we don't double-capture.
506
+ if (src.key === state.targetKey)
507
+ continue;
508
+ if (state.resolveColumnInSource(src.schemaLower, src.nameLower, state.oldCol))
509
+ return false;
510
+ }
511
+ }
512
+ if (frameBindsTarget(frame, state.targetKey))
513
+ return true;
514
+ }
515
+ return false;
516
+ }
517
+ /** Whether any source bound in `frame` resolves to `targetKey`. */
518
+ function frameBindsTarget(frame, targetKey) {
519
+ for (const key of frame.qualifiers.values()) {
520
+ if (key === targetKey)
521
+ return true;
522
+ }
523
+ return false;
524
+ }
525
+ /**
526
+ * Resolve a bare column qualifier innermost-first against the scope stack:
527
+ * the resolved key of the table it binds (through an alias, an unaliased
528
+ * source, or an exposing CTE), `null` when it binds something that is not the
529
+ * renamed real table and can never be (a non-exposing shadowing CTE, or a CTE
530
+ * merely in scope), or `undefined` when nothing in scope binds it. The
531
+ * innermost binding decides — standard SQL shadowing. Subquery / function
532
+ * sources are not recorded (pre-existing limitation, shared with the
533
+ * unqualified capture walk), so a qualifier bound only by one of those falls
534
+ * through to the caller's seedless fallback.
535
+ *
536
+ * A qualifier claimed TWICE inside one FROM (`from t join u as t`) is legal and
537
+ * resolves to the FIRST such source; {@link bindQualifier} is what makes that
538
+ * true here, and it is a small IMPROVEMENT on the two-map lookup this replaced
539
+ * (which answered alias-first, and so read `from t join u as t` as `u` where
540
+ * the planner reads it as `t`).
541
+ */
542
+ function resolveQualifierBinding(state, qualifierLower) {
543
+ for (let i = state.scopeStack.length - 1; i >= 0; i--) {
544
+ const frame = state.scopeStack[i];
545
+ const bound = frame.qualifiers.get(qualifierLower);
546
+ if (bound !== undefined)
547
+ return bound;
548
+ if (frame.ctesShadowingSource.has(qualifierLower))
549
+ return null;
550
+ if (frame.ctesInScope.has(qualifierLower))
551
+ return null;
552
+ }
553
+ return undefined;
554
+ }
555
+ /**
556
+ * Whether a qualified column ref names a **row image** — `new.<col>` /
557
+ * `old.<col>` in written-row context — and if so, whose. The three-way answer
558
+ * mirrors {@link RowImageContext}:
559
+ *
560
+ * - `'own'`: the image is the walk's target table — the ref matches and the
561
+ * rename rewrites it (`check (new.a > 0)` on the renamed table itself).
562
+ * - `'foreign'`: written-row context, but the image belongs to some OTHER
563
+ * relation (another table's CHECK / DEFAULT / generated body, or a
564
+ * `with inverse` expression whose `new.` names the enclosing select's output
565
+ * row) — the ref names nothing this walk cares about and must be IGNORED,
566
+ * not resolved as a table called `new`.
567
+ * - `'none'`: no written-row context here — the qualifier is not a row image
568
+ * at all, and the caller falls through to ordinary table-reference
569
+ * resolution.
570
+ *
571
+ * A schema-qualified `main.new.a` is a real three-part table reference, never a
572
+ * row image, so it answers `'none'` outright, as does any qualifier a FROM/WITH
573
+ * frame rebinds (`new` and `old` are not reserved words — `create table "new"`
574
+ * is legal, so `(select max("new".a) from "new")` must keep resolving as a
575
+ * table). Callers reach this only for a qualifier nothing in scope binds, which
576
+ * makes the rebind scan redundant there — kept as a cheap belt-and-braces guard
577
+ * so this function is safe to call from any future site.
578
+ *
579
+ * `qualifierLower` is `col.table` already lowercased by the caller.
580
+ */
581
+ function matchesRowImage(state, col, qualifierLower) {
582
+ if (state.rowImage === 'none')
583
+ return 'none';
584
+ if (col.schema !== undefined)
585
+ return 'none';
586
+ if (qualifierLower !== 'new' && qualifierLower !== 'old')
587
+ return 'none';
588
+ if (isQualifierReboundAboveSeed(state, qualifierLower))
589
+ return 'none';
590
+ return state.rowImage;
591
+ }
592
+ /**
593
+ * Whether any real FROM / WITH frame rebinds `qualifier` to a row source of its
594
+ * own, which would make a `new.` / `old.` ref name that source rather than the
595
+ * row image. `new` and `old` are not reserved words here — `create table "new"`
596
+ * is legal — so this scan is what stops the row-image match from false-firing
597
+ * inside `check ((select max("new".a) from "new") >= 0)`.
598
+ *
599
+ * The scan starts at index 1: frame 0 is the implicit seed
600
+ * {@link renameColumnInCheckExpression} pushes for the owning table, which is
601
+ * not a FROM binding and must not count as a rebind. All three ways a frame can
602
+ * bind a qualifier are checked — a FROM source's own qualifier (its alias, or
603
+ * its bare name when unaliased), a CTE name in scope, and a
604
+ * shadowing-but-not-exposing CTE source.
605
+ *
606
+ * Deliberately "any enclosing frame wins" rather than innermost-first: a frame
607
+ * only sits above index 0 while the walk is *inside* the subquery that pushed
608
+ * it, so every such frame really does enclose the reference. A top-level
609
+ * `new.a` in `check (new.a > 0 and (select count(*) from "new") >= 0)` is
610
+ * visited with only the seed on the stack and still matches.
611
+ *
612
+ * NOTE: the residual is a *correlated* `new.<col>` written inside a subquery
613
+ * that selects from a real table named `new` — left alone by both the rewrite
614
+ * and the drop refusal, since the qualifier is ambiguous there anyway. If that
615
+ * ever needs to resolve to the row image, it needs a spelling that distinguishes
616
+ * the two (the SQL standard has none), not a change to this scan.
617
+ */
618
+ function isQualifierReboundAboveSeed(state, qualifier) {
619
+ for (let i = 1; i < state.scopeStack.length; i++) {
620
+ const frame = state.scopeStack[i];
621
+ if (frame.qualifiers.has(qualifier))
622
+ return true;
623
+ if (frame.ctesInScope.has(qualifier))
624
+ return true;
625
+ if (frame.ctesShadowingSource.has(qualifier))
626
+ return true;
627
+ }
628
+ return false;
629
+ }
630
+ /**
631
+ * Visit a subtree that is written-row context belonging to the ENCLOSING select
632
+ * — a `with inverse` assignment expression or a `with defaults` entry
633
+ * expression — with the row-image mode forced to `'foreign'`, restoring the
634
+ * ambient mode after. The override is unconditional (not `||`-combined with the
635
+ * ambient mode): even inside a seeded `'own'` walk, a nested select's inverse
636
+ * expression's `new.` refs name THAT select's outputs, not the seeded table's
637
+ * row image.
638
+ */
639
+ function visitRowImageForeignSubtree(expr, state) {
640
+ const ambient = state.rowImage;
641
+ state.rowImage = 'foreign';
642
+ try {
643
+ visitColumnRename(expr, state);
644
+ }
645
+ finally {
646
+ state.rowImage = ambient;
647
+ }
648
+ }
649
+ function visitColumnRename(node, state) {
650
+ if (!node)
651
+ return;
652
+ switch (node.type) {
653
+ case 'select': {
654
+ const stmt = node;
655
+ pushWithFrame(stmt.withClause, state);
656
+ try {
657
+ const frame = buildScopeFrame(stmt.from, state);
658
+ state.scopeStack.push(frame);
659
+ try {
660
+ // Capture pre-rewrite output names of UNALIASED bare projections: a
661
+ // rename that rewrites one shifts the select's OUTPUT name with it, so
662
+ // any `new.<old>` refs in sibling `with inverse` exprs must follow
663
+ // (a `new.` ref is by view-output name; aliased / computed projections
664
+ // keep their output name, so the body rewrite alone covers them).
665
+ const preOutputNames = (stmt.columns ?? []).map(c => c.type === 'column' && !c.alias && c.expr.type === 'column' ? c.expr.name : undefined);
666
+ (stmt.columns ?? []).forEach(c => {
667
+ if (c.type === 'column')
668
+ visitColumnRename(c.expr, state);
669
+ });
670
+ // `with inverse` clauses: the assignment target is a bare base-column
671
+ // name resolving against this select's FROM — exactly an unqualified
672
+ // body ref, so it rides the same scope-aware walk via a synthetic
673
+ // probe (the `with defaults` clause below uses the same pattern). The
674
+ // assignment EXPR is written-row context regardless of the ambient
675
+ // mode: its `new.<x>` refs name the enclosing select's OUTPUT row
676
+ // (docs/sql-select.md § Result-column inverses), never any rename
677
+ // target's row image and never a table called `new` — so the walk
678
+ // descends it with the mode forced to `'foreign'` (the legitimate
679
+ // output-name shift is applied separately by
680
+ // {@link renameNewQualifiedRefs} after the body walk). The table
681
+ // walker forces its own suppression over the same subtree.
682
+ (stmt.columns ?? []).forEach(c => {
683
+ if (c.type !== 'column' || !c.inverse?.length)
684
+ return;
685
+ c.inverse.forEach(a => {
686
+ const probe = { type: 'column', name: a.column };
687
+ visitColumnRename(probe, state);
688
+ if (probe.name !== a.column) {
689
+ a.column = probe.name;
690
+ state.changed = true;
691
+ }
692
+ visitRowImageForeignSubtree(a.expr, state);
693
+ });
694
+ });
695
+ // `with defaults` clause: each entry's `column` is a bare base-column
696
+ // name of this select's FROM (a projected-away base column), so it
697
+ // rides the same scope-aware synthetic probe as a `with inverse`
698
+ // target; the entry's `expr` evaluates in the FROM frame already on
699
+ // the scope stack, so it rewrites like any body expression (an inner
700
+ // subquery in the expr pushes its own frame and disambiguates a
701
+ // like-named column). A defaults entry is documented self-contained —
702
+ // it cannot reference the inserted row (docs/sql-select.md § Insert
703
+ // defaults) — but nothing enforces that at CREATE VIEW time, so the
704
+ // walk pins the documented meaning the same way the inverse arm above
705
+ // does: a bare unbound `new.` / `old.` there is inert, never a
706
+ // reference to a table so named (such a ref could not plan anyway —
707
+ // the lowered INSERT gives the expr no scope that binds `new`).
708
+ (stmt.defaults ?? []).forEach(d => {
709
+ const probe = { type: 'column', name: d.column };
710
+ visitColumnRename(probe, state);
711
+ if (probe.name !== d.column) {
712
+ d.column = probe.name;
713
+ state.changed = true;
714
+ }
715
+ visitRowImageForeignSubtree(d.expr, state);
716
+ });
717
+ const outputRenames = new Map();
718
+ (stmt.columns ?? []).forEach((c, i) => {
719
+ const before = preOutputNames[i];
720
+ if (before !== undefined && c.type === 'column' && c.expr.type === 'column' && c.expr.name !== before) {
721
+ outputRenames.set(before.toLowerCase(), c.expr.name);
722
+ }
723
+ });
724
+ // A star projection covering the renamed table exposes the old column
725
+ // name as an OUTPUT name too — the rename shifts it exactly like an
726
+ // unaliased bare projection, so sibling `new.<old>` refs must follow.
727
+ // Skipped when an explicit projection still exposes the old name
728
+ // (first-occurrence resolution keeps `new.<old>` bound to it).
729
+ const hasInverseClauses = (stmt.columns ?? []).some(c => c.type === 'column' && !!c.inverse?.length);
730
+ if (hasInverseClauses && !outputRenames.has(state.oldCol)) {
731
+ const starCoversRenamed = (stmt.columns ?? []).some(c => {
732
+ if (c.type !== 'all')
733
+ return false;
734
+ if (c.table === undefined)
735
+ return frameBindsTarget(frame, state.targetKey);
736
+ return frame.qualifiers.get(c.table.toLowerCase()) === state.targetKey;
737
+ });
738
+ const oldStillExposed = (stmt.columns ?? []).some(c => c.type === 'column'
739
+ && (c.alias
740
+ ? c.alias.toLowerCase() === state.oldCol
741
+ : c.expr.type === 'column' && c.expr.name.toLowerCase() === state.oldCol));
742
+ if (starCoversRenamed && !oldStillExposed) {
743
+ outputRenames.set(state.oldCol, state.newCol);
744
+ }
745
+ }
746
+ if (outputRenames.size > 0) {
747
+ (stmt.columns ?? []).forEach(c => {
748
+ if (c.type !== 'column' || !c.inverse?.length)
749
+ return;
750
+ c.inverse.forEach(a => {
751
+ if (renameNewQualifiedRefs(a.expr, outputRenames))
752
+ state.changed = true;
753
+ });
754
+ });
755
+ }
756
+ (stmt.from ?? []).forEach(f => visitColumnRename(f, state));
757
+ visitColumnRename(stmt.where, state);
758
+ (stmt.groupBy ?? []).forEach(g => visitColumnRename(g, state));
759
+ visitColumnRename(stmt.having, state);
760
+ (stmt.orderBy ?? []).forEach(o => visitColumnRename(o.expr, state));
761
+ visitColumnRename(stmt.limit, state);
762
+ visitColumnRename(stmt.offset, state);
763
+ visitColumnRename(stmt.union, state);
764
+ if (stmt.compound)
765
+ visitColumnRename(stmt.compound.select, state);
766
+ }
767
+ finally {
768
+ state.scopeStack.pop();
769
+ }
770
+ }
771
+ finally {
772
+ state.scopeStack.pop();
773
+ }
774
+ break;
775
+ }
776
+ case 'insert': {
777
+ const stmt = node;
778
+ pushWithFrame(stmt.withClause, state);
779
+ try {
780
+ // Mirrors the planner's write-target rule (and the table walker's
781
+ // `visitDmlTarget`): an unqualified target naming a member of the
782
+ // statement's own leading WITH binds that CTE, not the renamed table.
783
+ const targetIsCte = stmt.table.schema === undefined &&
784
+ (stmt.withClause?.ctes ?? []).some(c => eq(c.name, stmt.table.name));
785
+ const targetIsRenamed = !targetIsCte &&
786
+ state.resolveRef(stmt.table.schema, stmt.table.name) === state.targetKey;
787
+ if (targetIsRenamed && stmt.columns) {
788
+ stmt.columns = stmt.columns.map(c => {
789
+ if (c.toLowerCase() === state.oldCol) {
790
+ state.changed = true;
791
+ return state.newCol;
792
+ }
793
+ return c;
794
+ });
795
+ }
796
+ if (targetIsRenamed) {
797
+ (stmt.upsertClauses ?? []).forEach(uc => {
798
+ if (uc.conflictTarget) {
799
+ uc.conflictTarget = uc.conflictTarget.map(c => {
800
+ if (c.toLowerCase() === state.oldCol) {
801
+ state.changed = true;
802
+ return state.newCol;
803
+ }
804
+ return c;
805
+ });
806
+ }
807
+ if (uc.assignments) {
808
+ for (const a of uc.assignments) {
809
+ if (a.column.toLowerCase() === state.oldCol) {
810
+ a.column = state.newCol;
811
+ state.changed = true;
812
+ }
813
+ }
814
+ }
815
+ });
816
+ }
817
+ visitColumnRename(stmt.source, state);
818
+ (stmt.upsertClauses ?? []).forEach(uc => {
819
+ (uc.assignments ?? []).forEach(a => visitColumnRename(a.value, state));
820
+ visitColumnRename(uc.where, state);
821
+ });
822
+ (stmt.returning ?? []).forEach(r => {
823
+ if (r.type === 'column')
824
+ visitColumnRename(r.expr, state);
825
+ });
826
+ (stmt.contextValues ?? []).forEach(cv => visitColumnRename(cv.value, state));
827
+ }
828
+ finally {
829
+ state.scopeStack.pop();
830
+ }
831
+ break;
832
+ }
833
+ case 'update': {
834
+ const stmt = node;
835
+ pushWithFrame(stmt.withClause, state);
836
+ try {
837
+ // Same write-target rule as the insert arm above.
838
+ const targetIsCte = stmt.table.schema === undefined &&
839
+ (stmt.withClause?.ctes ?? []).some(c => eq(c.name, stmt.table.name));
840
+ const targetIsRenamed = !targetIsCte &&
841
+ state.resolveRef(stmt.table.schema, stmt.table.name) === state.targetKey;
842
+ if (targetIsRenamed) {
843
+ for (const a of stmt.assignments) {
844
+ if (a.column.toLowerCase() === state.oldCol) {
845
+ a.column = state.newCol;
846
+ state.changed = true;
847
+ }
848
+ }
849
+ }
850
+ // Push a scope frame so unqualified column refs in WHERE/RETURNING
851
+ // resolve against the update target, bound under its resolved key.
852
+ const frame = emptyFrame();
853
+ const targetBinding = state.resolveRef(stmt.table.schema, stmt.table.name);
854
+ if (targetBinding !== undefined) {
855
+ frame.qualifiers.set(stmt.table.name.toLowerCase(), targetBinding);
856
+ }
857
+ state.scopeStack.push(frame);
858
+ try {
859
+ stmt.assignments.forEach(a => visitColumnRename(a.value, state));
860
+ visitColumnRename(stmt.where, state);
861
+ (stmt.returning ?? []).forEach(r => {
862
+ if (r.type === 'column')
863
+ visitColumnRename(r.expr, state);
864
+ });
865
+ (stmt.contextValues ?? []).forEach(cv => visitColumnRename(cv.value, state));
866
+ }
867
+ finally {
868
+ state.scopeStack.pop();
869
+ }
870
+ }
871
+ finally {
872
+ state.scopeStack.pop();
873
+ }
874
+ break;
875
+ }
876
+ case 'delete': {
877
+ const stmt = node;
878
+ pushWithFrame(stmt.withClause, state);
879
+ try {
880
+ const frame = emptyFrame();
881
+ const targetBinding = state.resolveRef(stmt.table.schema, stmt.table.name);
882
+ if (targetBinding !== undefined) {
883
+ frame.qualifiers.set(stmt.table.name.toLowerCase(), targetBinding);
884
+ }
885
+ state.scopeStack.push(frame);
886
+ try {
887
+ visitColumnRename(stmt.where, state);
888
+ (stmt.returning ?? []).forEach(r => {
889
+ if (r.type === 'column')
890
+ visitColumnRename(r.expr, state);
891
+ });
892
+ (stmt.contextValues ?? []).forEach(cv => visitColumnRename(cv.value, state));
893
+ }
894
+ finally {
895
+ state.scopeStack.pop();
896
+ }
897
+ }
898
+ finally {
899
+ state.scopeStack.pop();
900
+ }
901
+ break;
902
+ }
903
+ case 'values': {
904
+ const stmt = node;
905
+ stmt.values.forEach(row => row.forEach(v => visitColumnRename(v, state)));
906
+ break;
907
+ }
908
+ case 'join': {
909
+ const join = node;
910
+ visitColumnRename(join.left, state);
911
+ visitColumnRename(join.right, state);
912
+ visitColumnRename(join.condition, state);
913
+ break;
914
+ }
915
+ case 'functionSource': {
916
+ node.args.forEach(a => visitColumnRename(a, state));
917
+ break;
918
+ }
919
+ case 'subquerySource': {
920
+ visitColumnRename(node.subquery, state);
921
+ break;
922
+ }
923
+ case 'binary': {
924
+ const e = node;
925
+ visitColumnRename(e.left, state);
926
+ visitColumnRename(e.right, state);
927
+ break;
928
+ }
929
+ case 'unary':
930
+ case 'cast':
931
+ case 'collate':
932
+ visitColumnRename(node.expr, state);
933
+ break;
934
+ case 'function':
935
+ node.args.forEach(a => visitColumnRename(a, state));
936
+ break;
937
+ case 'subquery':
938
+ visitColumnRename(node.query, state);
939
+ break;
940
+ case 'windowFunction': {
941
+ const wf = node;
942
+ visitColumnRename(wf.function, state);
943
+ visitColumnRename(wf.window, state);
944
+ break;
945
+ }
946
+ case 'windowDefinition': {
947
+ const wd = node;
948
+ (wd.partitionBy ?? []).forEach(p => visitColumnRename(p, state));
949
+ (wd.orderBy ?? []).forEach(o => visitColumnRename(o.expr, state));
950
+ break;
951
+ }
952
+ case 'case': {
953
+ const ce = node;
954
+ visitColumnRename(ce.baseExpr, state);
955
+ ce.whenThenClauses.forEach(wt => {
956
+ visitColumnRename(wt.when, state);
957
+ visitColumnRename(wt.then, state);
958
+ });
959
+ visitColumnRename(ce.elseExpr, state);
960
+ break;
961
+ }
962
+ case 'in': {
963
+ const ie = node;
964
+ visitColumnRename(ie.expr, state);
965
+ (ie.values ?? []).forEach(v => visitColumnRename(v, state));
966
+ visitColumnRename(ie.subquery, state);
967
+ break;
968
+ }
969
+ case 'exists':
970
+ visitColumnRename(node.subquery, state);
971
+ break;
972
+ case 'between': {
973
+ const be = node;
974
+ visitColumnRename(be.expr, state);
975
+ visitColumnRename(be.lower, state);
976
+ visitColumnRename(be.upper, state);
977
+ break;
978
+ }
979
+ case 'column': {
980
+ const col = node;
981
+ if (col.name.toLowerCase() !== state.oldCol)
982
+ break;
983
+ if (col.table) {
984
+ const qualifierLower = col.table.toLowerCase();
985
+ let hit;
986
+ if (col.schema !== undefined) {
987
+ // A schema-qualified qualifier (`main.t.k`) can never be an
988
+ // alias, a CTE, or the row image — resolve it directly.
989
+ hit = state.resolveRef(col.schema, col.table) === state.targetKey;
990
+ }
991
+ else {
992
+ const binding = resolveQualifierBinding(state, qualifierLower);
993
+ if (binding !== undefined) {
994
+ // The innermost binding decides: the renamed table's key,
995
+ // another object's key, or null (a shadowing CTE). A table
996
+ // genuinely named `new` / `old` that is bound in scope —
997
+ // the seeded owning table included — resolves through its
998
+ // binding, never through the row image.
999
+ hit = binding === state.targetKey;
1000
+ }
1001
+ else {
1002
+ // An UNBOUND bare qualifier: the three-way row-image mode
1003
+ // decides (see {@link matchesRowImage}) — `'own'` matches
1004
+ // the walk's target, `'foreign'` is another relation's row
1005
+ // image and must be ignored (NOT resolved as a table called
1006
+ // `new`), `'none'` falls through to direct resolution, as
1007
+ // the planner would against the body's home schema path
1008
+ // (correlation the walk cannot see).
1009
+ const image = matchesRowImage(state, col, qualifierLower);
1010
+ hit = image === 'own' ? true
1011
+ : image === 'foreign' ? false
1012
+ : state.resolveRef(undefined, col.table) === state.targetKey;
1013
+ }
1014
+ }
1015
+ if (hit) {
1016
+ col.name = state.newCol;
1017
+ state.changed = true;
1018
+ }
1019
+ }
1020
+ else {
1021
+ if (unqualifiedRefBindsTarget(state)) {
1022
+ col.name = state.newCol;
1023
+ state.changed = true;
1024
+ }
1025
+ }
1026
+ break;
1027
+ }
1028
+ case 'table':
1029
+ // Table sources don't contain column names.
1030
+ break;
1031
+ default:
1032
+ break;
1033
+ }
1034
+ }
1035
+ /**
1036
+ * Push a with-frame that registers any CTEs in the given WITH clause that
1037
+ * re-expose the renamed column. CTEs are visited in declaration order so
1038
+ * later CTEs see earlier ones in the same WITH.
1039
+ *
1040
+ * For `with recursive`, each CTE's name is registered in `ctesInScope`
1041
+ * *before* its body is visited so self-references inside the recursive step
1042
+ * resolve to the CTE (not to a same-named renamed table). For non-recursive
1043
+ * WITH, the name is registered only after the body — a non-recursive body
1044
+ * must not see itself.
1045
+ *
1046
+ * Caller is responsible for popping the frame via `state.scopeStack.pop()`.
1047
+ */
1048
+ function pushWithFrame(withClause, state) {
1049
+ const frame = emptyFrame();
1050
+ state.scopeStack.push(frame);
1051
+ if (withClause) {
1052
+ for (const cte of withClause.ctes) {
1053
+ const nameLower = cte.name.toLowerCase();
1054
+ if (withClause.recursive) {
1055
+ frame.ctesInScope.add(nameLower);
1056
+ }
1057
+ visitColumnRename(cte.query, state);
1058
+ if (cteExposesRenamedColumn(cte, state)) {
1059
+ frame.ctesExposingRenamed.add(nameLower);
1060
+ }
1061
+ frame.ctesInScope.add(nameLower);
1062
+ }
1063
+ }
1064
+ return frame;
1065
+ }
1066
+ /**
1067
+ * Rebuild a with-frame's `ctesExposingRenamed` set for exposure analysis
1068
+ * without re-visiting CTE bodies (they were already visited).
1069
+ */
1070
+ function analyzeWithFrame(withClause, state) {
1071
+ const frame = emptyFrame();
1072
+ if (!withClause)
1073
+ return frame;
1074
+ state.scopeStack.push(frame);
1075
+ try {
1076
+ for (const cte of withClause.ctes) {
1077
+ if (cteExposesRenamedColumn(cte, state)) {
1078
+ frame.ctesExposingRenamed.add(cte.name.toLowerCase());
1079
+ }
1080
+ frame.ctesInScope.add(cte.name.toLowerCase());
1081
+ }
1082
+ }
1083
+ finally {
1084
+ state.scopeStack.pop();
1085
+ }
1086
+ return frame;
1087
+ }
1088
+ /**
1089
+ * Determine whether a CTE re-exposes the renamed column under name `state.newCol`
1090
+ * (the column has already been rewritten inside its body if the body referenced it).
1091
+ *
1092
+ * Returns false when:
1093
+ * - The CTE has an explicit column list (renaming the input to fixed names).
1094
+ * - The body is not a SELECT (INSERT/UPDATE/DELETE WITH RETURNING — out of scope).
1095
+ * - No passthrough result column references the renamed table's column.
1096
+ */
1097
+ function cteExposesRenamedColumn(cte, state) {
1098
+ if (cte.columns)
1099
+ return false;
1100
+ return queryExposesRenamedColumn(cte.query, state);
1101
+ }
1102
+ /**
1103
+ * The query-level core {@link cteExposesRenamedColumn} and the public
1104
+ * {@link bodyExposesRenamedColumn} share: whether `query`'s top-level result
1105
+ * columns publish the renamed column under `state.newCol`. Expects the body to
1106
+ * be ALREADY rewritten — the walk rewrites a CTE body before asking, and the
1107
+ * public entry point documents the same contract. For a compound body the
1108
+ * first arm's result list is what names the output, and that is what
1109
+ * `query.columns` holds here; a non-select body never exposes.
1110
+ */
1111
+ function queryExposesRenamedColumn(query, state) {
1112
+ if (query.type !== 'select')
1113
+ return false;
1114
+ const select = query;
1115
+ // Recreate the body's own with-frame so nested CTE refs in `select.from`
1116
+ // resolve correctly during exposure analysis.
1117
+ const bodyWithFrame = analyzeWithFrame(select.withClause, state);
1118
+ state.scopeStack.push(bodyWithFrame);
1119
+ try {
1120
+ const bodyFrame = buildScopeFrame(select.from, state);
1121
+ for (const col of select.columns ?? []) {
1122
+ if (isResultColumnExposure(col, bodyFrame, state))
1123
+ return true;
1124
+ }
1125
+ return false;
1126
+ }
1127
+ finally {
1128
+ state.scopeStack.pop();
1129
+ }
1130
+ }
1131
+ function isResultColumnExposure(col, bodyFrame, state) {
1132
+ if (col.type === 'all') {
1133
+ if (col.table === undefined) {
1134
+ return frameBindsTarget(bodyFrame, state.targetKey);
1135
+ }
1136
+ return bodyFrame.qualifiers.get(col.table.toLowerCase()) === state.targetKey;
1137
+ }
1138
+ if (col.alias !== undefined)
1139
+ return false;
1140
+ const expr = col.expr;
1141
+ if (expr.type !== 'column')
1142
+ return false;
1143
+ const colExpr = expr;
1144
+ if (colExpr.name.toLowerCase() !== state.newCol.toLowerCase())
1145
+ return false;
1146
+ if (colExpr.table === undefined) {
1147
+ return frameBindsTarget(bodyFrame, state.targetKey);
1148
+ }
1149
+ if (colExpr.schema !== undefined) {
1150
+ // Schema-qualified: never an alias — resolve directly.
1151
+ return state.resolveRef(colExpr.schema, colExpr.table) === state.targetKey;
1152
+ }
1153
+ return bodyFrame.qualifiers.get(colExpr.table.toLowerCase()) === state.targetKey;
1154
+ }
1155
+ /**
1156
+ * Rename `new.<old>` → `new.<new>` references inside a `with inverse` assignment
1157
+ * expression, for output columns whose name shifted under a column rename (an
1158
+ * unaliased bare projection of the renamed column). Uniform, depth-blind in-place
1159
+ * walk over the expression's object graph: the `new.` qualifier alone decides (it
1160
+ * is the reserved written-row namespace no FROM source legitimately shadows), so
1161
+ * no scope tracking applies — narrower and simpler than the scope-aware walkers
1162
+ * above. Returns whether any reference was rewritten.
1163
+ *
1164
+ * Depth-blind **on purpose**, unlike the row-image match in `matchesRowImage`.
1165
+ * The two look alike but answer different questions: this one rewrites by view
1166
+ * *output* name inside a `with inverse` expr, so no FROM scope tracking applies
1167
+ * (`new.` is the written-row namespace validation reserves there —
1168
+ * `planner/analysis/authored-inverse.ts` requires every `new.<x>` to resolve to
1169
+ * an output column of its own enclosing select), whereas a CHECK body may
1170
+ * contain a subquery selecting from a real table named `"new"` and so must
1171
+ * consult the scope stack.
1172
+ *
1173
+ * One boundary the depth-blind walk must still respect: a subquery inside the
1174
+ * expression may be a select carrying its OWN `with inverse` clause, whose
1175
+ * `new.` refs name THAT select's outputs (validation pins them there — they can
1176
+ * never reach the outer select's), so the walk skips `inverse` subtrees.
1177
+ * `inverse` is a property of {@link AST.ResultColumnExpr} only, and the walk's
1178
+ * roots are the OUTER clause's assignment exprs, so every `inverse` key met
1179
+ * in-walk belongs to a nested select. Correlated `new.<outer-output>` refs in
1180
+ * ORDINARY subqueries of the expression are still reached — the skip is the
1181
+ * clause, not the subquery.
1182
+ *
1183
+ * NOTE: `inverse` is skipped but `defaults` is not, deliberately — a nested
1184
+ * select's `with defaults` entry expr owns no written-row scope (the inverse
1185
+ * validation in `planner/analysis/authored-inverse.ts` never descends one, so a
1186
+ * `new.` ref there binds nothing either way) and shifting a ref inside one is
1187
+ * inert. If `with defaults` ever gains a written-row scope of its own, this walk
1188
+ * needs the same skip for `defaults`.
1189
+ */
1190
+ function renameNewQualifiedRefs(expr, renames) {
1191
+ let changed = false;
1192
+ const visit = (v) => {
1193
+ if (Array.isArray(v)) {
1194
+ v.forEach(visit);
1195
+ return;
1196
+ }
1197
+ if (v === null || typeof v !== 'object')
1198
+ return;
1199
+ const n = v;
1200
+ if (n.type === 'column' && typeof n.table === 'string' && n.table.toLowerCase() === 'new'
1201
+ && n.schema === undefined && typeof n.name === 'string') {
1202
+ const to = renames.get(n.name.toLowerCase());
1203
+ if (to !== undefined && to !== n.name) {
1204
+ n.name = to;
1205
+ changed = true;
1206
+ }
1207
+ }
1208
+ for (const key of Object.keys(n)) {
1209
+ if (key === 'loc' || key === 'inverse')
1210
+ continue;
1211
+ visit(n[key]);
1212
+ }
1213
+ };
1214
+ visit(expr);
1215
+ return changed;
1216
+ }
1217
+ // The former `renameTableInInsertDefaults` / `renameColumnInInsertDefaults`
1218
+ // standalone clause rewriters are gone: `with defaults (…)` now rides inside the
1219
+ // select body (`SelectStmt.defaults`), so the body walks above
1220
+ // (`visitTableRename` / `visitColumnRename` select cases) descend it directly —
1221
+ // the entry `expr` rewrites in the select's FROM scope frame and the entry
1222
+ // `column` target rides the same scope-aware synthetic probe as a `with inverse`
1223
+ // target. No seeded CHECK-expr scope / declared resolver is needed: the real
1224
+ // FROM frame is on the scope stack, exactly as for any body reference.
1225
+ //# sourceMappingURL=column-rename.js.map