@quereus/quereus 4.8.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 (157) hide show
  1. package/dist/src/core/database.d.ts +6 -0
  2. package/dist/src/core/database.d.ts.map +1 -1
  3. package/dist/src/core/database.js +26 -1
  4. package/dist/src/core/database.js.map +1 -1
  5. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  6. package/dist/src/func/builtins/aggregate.js +3 -2
  7. package/dist/src/func/builtins/aggregate.js.map +1 -1
  8. package/dist/src/func/builtins/conversion.d.ts +5 -0
  9. package/dist/src/func/builtins/conversion.d.ts.map +1 -1
  10. package/dist/src/func/builtins/conversion.js +16 -2
  11. package/dist/src/func/builtins/conversion.js.map +1 -1
  12. package/dist/src/func/builtins/index.d.ts.map +1 -1
  13. package/dist/src/func/builtins/index.js +5 -2
  14. package/dist/src/func/builtins/index.js.map +1 -1
  15. package/dist/src/func/builtins/string.d.ts +2 -0
  16. package/dist/src/func/builtins/string.d.ts.map +1 -1
  17. package/dist/src/func/builtins/string.js +60 -4
  18. package/dist/src/func/builtins/string.js.map +1 -1
  19. package/dist/src/index.d.ts +6 -3
  20. package/dist/src/index.d.ts.map +1 -1
  21. package/dist/src/index.js +15 -2
  22. package/dist/src/index.js.map +1 -1
  23. package/dist/src/planner/building/constraint-builder.js +1 -1
  24. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  25. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
  26. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +121 -51
  27. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  28. package/dist/src/planner/rules/shared/index-style-context.d.ts +14 -4
  29. package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
  30. package/dist/src/planner/rules/shared/index-style-context.js +14 -4
  31. package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
  32. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  33. package/dist/src/runtime/emit/alter-table.js +212 -155
  34. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  35. package/dist/src/runtime/emit/assertion-drop-guard.d.ts +26 -12
  36. package/dist/src/runtime/emit/assertion-drop-guard.d.ts.map +1 -1
  37. package/dist/src/runtime/emit/assertion-drop-guard.js +45 -26
  38. package/dist/src/runtime/emit/assertion-drop-guard.js.map +1 -1
  39. package/dist/src/runtime/emit/assertion-rename-helpers.d.ts +6 -6
  40. package/dist/src/runtime/emit/assertion-rename-helpers.d.ts.map +1 -1
  41. package/dist/src/runtime/emit/assertion-rename-helpers.js +18 -20
  42. package/dist/src/runtime/emit/assertion-rename-helpers.js.map +1 -1
  43. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  44. package/dist/src/runtime/emit/binary.js +12 -10
  45. package/dist/src/runtime/emit/binary.js.map +1 -1
  46. package/dist/src/runtime/emit/column-rename-cascade.d.ts +84 -0
  47. package/dist/src/runtime/emit/column-rename-cascade.d.ts.map +1 -0
  48. package/dist/src/runtime/emit/column-rename-cascade.js +134 -0
  49. package/dist/src/runtime/emit/column-rename-cascade.js.map +1 -0
  50. package/dist/src/runtime/emit/drop-column-guards.d.ts +82 -53
  51. package/dist/src/runtime/emit/drop-column-guards.d.ts.map +1 -1
  52. package/dist/src/runtime/emit/drop-column-guards.js +155 -90
  53. package/dist/src/runtime/emit/drop-column-guards.js.map +1 -1
  54. package/dist/src/runtime/emit/drop-table.d.ts.map +1 -1
  55. package/dist/src/runtime/emit/drop-table.js +5 -0
  56. package/dist/src/runtime/emit/drop-table.js.map +1 -1
  57. package/dist/src/runtime/emit/drop-view.d.ts.map +1 -1
  58. package/dist/src/runtime/emit/drop-view.js +5 -0
  59. package/dist/src/runtime/emit/drop-view.js.map +1 -1
  60. package/dist/src/runtime/emit/expression-drop-guard.d.ts +57 -0
  61. package/dist/src/runtime/emit/expression-drop-guard.d.ts.map +1 -0
  62. package/dist/src/runtime/emit/expression-drop-guard.js +82 -0
  63. package/dist/src/runtime/emit/expression-drop-guard.js.map +1 -0
  64. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +26 -9
  65. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  66. package/dist/src/runtime/emit/materialized-view-helpers.js +37 -17
  67. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  68. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  69. package/dist/src/runtime/emit/materialized-view.js +3 -0
  70. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  71. package/dist/src/schema/assertion.d.ts +58 -0
  72. package/dist/src/schema/assertion.d.ts.map +1 -1
  73. package/dist/src/schema/assertion.js +78 -0
  74. package/dist/src/schema/assertion.js.map +1 -1
  75. package/dist/src/schema/catalog-persistability.d.ts +11 -3
  76. package/dist/src/schema/catalog-persistability.d.ts.map +1 -1
  77. package/dist/src/schema/catalog-persistability.js +48 -29
  78. package/dist/src/schema/catalog-persistability.js.map +1 -1
  79. package/dist/src/schema/catalog.d.ts +22 -0
  80. package/dist/src/schema/catalog.d.ts.map +1 -1
  81. package/dist/src/schema/catalog.js +4 -1
  82. package/dist/src/schema/catalog.js.map +1 -1
  83. package/dist/src/schema/column-republication.d.ts +126 -0
  84. package/dist/src/schema/column-republication.d.ts.map +1 -0
  85. package/dist/src/schema/column-republication.js +121 -0
  86. package/dist/src/schema/column-republication.js.map +1 -0
  87. package/dist/src/schema/column-source-resolver.d.ts +29 -6
  88. package/dist/src/schema/column-source-resolver.d.ts.map +1 -1
  89. package/dist/src/schema/column-source-resolver.js +78 -9
  90. package/dist/src/schema/column-source-resolver.js.map +1 -1
  91. package/dist/src/schema/expression-dependents.d.ts +160 -0
  92. package/dist/src/schema/expression-dependents.d.ts.map +1 -0
  93. package/dist/src/schema/expression-dependents.js +228 -0
  94. package/dist/src/schema/expression-dependents.js.map +1 -0
  95. package/dist/src/schema/object-dependency-closure.d.ts +127 -0
  96. package/dist/src/schema/object-dependency-closure.d.ts.map +1 -0
  97. package/dist/src/schema/object-dependency-closure.js +117 -0
  98. package/dist/src/schema/object-dependency-closure.js.map +1 -0
  99. package/dist/src/schema/object-ref-resolver.d.ts +83 -0
  100. package/dist/src/schema/object-ref-resolver.d.ts.map +1 -0
  101. package/dist/src/schema/object-ref-resolver.js +135 -0
  102. package/dist/src/schema/object-ref-resolver.js.map +1 -0
  103. package/dist/src/schema/rename/column-rename.d.ts +228 -0
  104. package/dist/src/schema/rename/column-rename.d.ts.map +1 -0
  105. package/dist/src/schema/rename/column-rename.js +1225 -0
  106. package/dist/src/schema/rename/column-rename.js.map +1 -0
  107. package/dist/src/schema/rename/self-qualifier-strip.d.ts +29 -0
  108. package/dist/src/schema/rename/self-qualifier-strip.d.ts.map +1 -0
  109. package/dist/src/schema/rename/self-qualifier-strip.js +267 -0
  110. package/dist/src/schema/rename/self-qualifier-strip.js.map +1 -0
  111. package/dist/src/schema/rename/shared.d.ts +108 -0
  112. package/dist/src/schema/rename/shared.d.ts.map +1 -0
  113. package/dist/src/schema/rename/shared.js +49 -0
  114. package/dist/src/schema/rename/shared.js.map +1 -0
  115. package/dist/src/schema/rename/table-rename.d.ts +263 -0
  116. package/dist/src/schema/rename/table-rename.d.ts.map +1 -0
  117. package/dist/src/schema/rename/table-rename.js +860 -0
  118. package/dist/src/schema/rename/table-rename.js.map +1 -0
  119. package/dist/src/schema/rename-rewriter.d.ts +18 -278
  120. package/dist/src/schema/rename-rewriter.d.ts.map +1 -1
  121. package/dist/src/schema/rename-rewriter.js +17 -1506
  122. package/dist/src/schema/rename-rewriter.js.map +1 -1
  123. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  124. package/dist/src/schema/schema-differ.js +228 -21
  125. package/dist/src/schema/schema-differ.js.map +1 -1
  126. package/dist/src/types/builtin-types.d.ts.map +1 -1
  127. package/dist/src/types/builtin-types.js +12 -28
  128. package/dist/src/types/builtin-types.js.map +1 -1
  129. package/dist/src/types/cast-semantics.d.ts +7 -5
  130. package/dist/src/types/cast-semantics.d.ts.map +1 -1
  131. package/dist/src/types/cast-semantics.js +20 -9
  132. package/dist/src/types/cast-semantics.js.map +1 -1
  133. package/dist/src/types/index.d.ts +1 -1
  134. package/dist/src/types/index.d.ts.map +1 -1
  135. package/dist/src/types/index.js +1 -1
  136. package/dist/src/types/index.js.map +1 -1
  137. package/dist/src/types/registry.d.ts.map +1 -1
  138. package/dist/src/types/registry.js +2 -1
  139. package/dist/src/types/registry.js.map +1 -1
  140. package/dist/src/types/temporal-types.d.ts +11 -0
  141. package/dist/src/types/temporal-types.d.ts.map +1 -1
  142. package/dist/src/types/temporal-types.js +82 -0
  143. package/dist/src/types/temporal-types.js.map +1 -1
  144. package/dist/src/util/affinity.d.ts +2 -1
  145. package/dist/src/util/affinity.d.ts.map +1 -1
  146. package/dist/src/util/affinity.js +16 -5
  147. package/dist/src/util/affinity.js.map +1 -1
  148. package/dist/src/util/ast-spine-clone.d.ts +2 -2
  149. package/dist/src/util/ast-spine-clone.js +2 -2
  150. package/dist/src/util/value-text.d.ts +48 -0
  151. package/dist/src/util/value-text.d.ts.map +1 -0
  152. package/dist/src/util/value-text.js +35 -0
  153. package/dist/src/util/value-text.js.map +1 -0
  154. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  155. package/dist/src/vtab/memory/layer/manager.js +14 -5
  156. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  157. package/package.json +3 -3
@@ -0,0 +1,228 @@
1
+ import { expressionToString } from '../emit/ast-stringify.js';
2
+ import { columnReferencedInAst, columnReferencedInCheckExpression, objectRefKey, } from './rename-rewriter.js';
3
+ import { buildColumnSourceResolver } from './column-source-resolver.js';
4
+ import { snapshotObjectRefResolvers } from './object-ref-resolver.js';
5
+ import { reachableObjects } from './object-dependency-closure.js';
6
+ import { collectColumnRepublication } from './column-republication.js';
7
+ /**
8
+ * The FIRST table in ANY schema whose `arm` expressions name
9
+ * `<tableSchema.schemaName>.<tableSchema.name>.<columnName>`, or `undefined` when none
10
+ * does. The probed table itself is scanned first, so a self-reference is always the one
11
+ * reported and the guards' own-table messages never change spelling because an unrelated
12
+ * schema happens to sort earlier.
13
+ *
14
+ * Only the first hit is returned: every caller is a guard that throws on it, and stopping
15
+ * early keeps the walk off the remaining schemas.
16
+ *
17
+ * Two expressions are skipped on the probed table itself, both deliberately:
18
+ *
19
+ * - The dropped column's **own** DEFAULT / generated body — it goes away with the column,
20
+ * so a self-naming default (`a integer default (new.a)`) is not something the drop
21
+ * orphans.
22
+ * - **Every** own-table generated body — `runDropColumn` already refuses off
23
+ * `generatedColumnDependencies`, the resolved column-index map
24
+ * `extractGeneratedColumnDependencies` builds, and reporting it twice would only change
25
+ * which of two messages the user sees. A generated body on ANOTHER table is not covered
26
+ * by that map and IS reported here.
27
+ */
28
+ export function findColumnExpressionDependent(db, tableSchema, columnName, arm) {
29
+ const resolvers = snapshotObjectRefResolvers(db);
30
+ const resolveColumnInSource = buildColumnSourceResolver(db);
31
+ const targetKey = objectRefKey(tableSchema.schemaName, tableSchema.name);
32
+ // One republication fixpoint per drop (seed first), shared by every probe below:
33
+ // a view republishing the column by a star (`select * from t`) is a target its
34
+ // readers reference the column THROUGH, without any body spelling the name.
35
+ const republication = collectColumnRepublication(db, { targetKey, tableName: tableSchema.name }, columnName, resolvers, resolveColumnInSource);
36
+ for (const table of tablesProbedTargetFirst(db, targetKey)) {
37
+ const ownTable = objectRefKey(table.schemaName, table.name) === targetKey;
38
+ const names = columnProbe(db, resolvers, table.schemaName, ownTable, republication, columnName, resolveColumnInSource);
39
+ const found = arm === 'check'
40
+ ? findInChecks(table, names)
41
+ : findInColumnExpressions(table, ownTable, columnName, names);
42
+ if (found)
43
+ return { table, ownTable, ...found };
44
+ }
45
+ return undefined;
46
+ }
47
+ /**
48
+ * The FIRST table in ANY schema whose CHECK / DEFAULT / `GENERATED ALWAYS AS` expressions
49
+ * name `<schemaName>.<objectName>`, or `undefined` when none does.
50
+ *
51
+ * The dropped object itself is skipped: its own CHECKs naming itself vanish with it, so
52
+ * refusing on them would make every self-referencing table undroppable. (The column verb
53
+ * keeps its target in the walk — a column drop leaves the rest of the table behind.)
54
+ *
55
+ * All three arms run with the walker's `rowImageContext` on the ROOT expression, exactly as
56
+ * `renameTableInCheckConstraints` / `renameTableInColumnExpressions` do: these
57
+ * expressions evaluate against a written row, so a bare `new.` / `old.` qualifier names
58
+ * that row image rather than a table literally called `new`.
59
+ *
60
+ * The match is `namerOf` over the expression's whole reach, so a CHECK that reads a view
61
+ * whose body reads the dropped object matches too, and `path` carries the shortest chain
62
+ * for the error to quote — empty exactly when the expression names the object itself.
63
+ */
64
+ export function findTableExpressionDependent(db, schemaName, objectName) {
65
+ const resolvers = snapshotObjectRefResolvers(db);
66
+ const targetKey = objectRefKey(schemaName, objectName);
67
+ for (const table of tablesProbedTargetFirst(db, targetKey)) {
68
+ if (objectRefKey(table.schemaName, table.name) === targetKey)
69
+ continue;
70
+ const names = expr => {
71
+ if (expr === undefined)
72
+ return undefined;
73
+ const path = reachableObjects(db, expr, table.schemaName, resolvers, { rowImageContext: true })
74
+ .namerOf(targetKey)?.path;
75
+ return path === undefined ? undefined : { path };
76
+ };
77
+ const found = findInChecks(table, names) ?? findInColumnExpressions(table, false, undefined, names);
78
+ if (found)
79
+ return { table, ownTable: false, ...found };
80
+ }
81
+ return undefined;
82
+ }
83
+ /**
84
+ * How an error should spell `table` when the object being dropped lives in `homeSchemaName`:
85
+ * bare when the two share a schema, schema-qualified when they do not — the same rule the
86
+ * rest of the engine's messages follow, and the only way a cross-schema refusal names a
87
+ * table the user can actually find.
88
+ */
89
+ export function describeDependentTable(table, homeSchemaName) {
90
+ return table.schemaName.toLowerCase() === homeSchemaName.toLowerCase()
91
+ ? `'${table.name}'`
92
+ : `'${table.schemaName}.${table.name}'`;
93
+ }
94
+ /**
95
+ * Every table in every schema, with the probed table (if it exists) first.
96
+ *
97
+ * Order is not cosmetic: the guards report the FIRST hit, and the own-table messages are
98
+ * the ones users and tests already know. Scanning the target first keeps a self-reference
99
+ * winning over an unrelated schema's.
100
+ *
101
+ * NOTE: past the probed table the order is `_getAllSchemas()` × `getAllTables()` insertion
102
+ * order, so with TWO referencing tables which one the refusal names follows catalog
103
+ * insertion rather than any user-visible rule. Harmless while the message is advisory —
104
+ * every referencing table has to be dealt with anyway. If a caller ever needs to *list*
105
+ * dependents (a `CASCADE` arm, an introspection function), give it a collect-all entry
106
+ * point with a defined order rather than calling this in a loop.
107
+ */
108
+ function* tablesProbedTargetFirst(db, targetKey) {
109
+ const rest = [];
110
+ for (const schema of db.schemaManager._getAllSchemas()) {
111
+ for (const table of schema.getAllTables()) {
112
+ if (objectRefKey(table.schemaName, table.name) === targetKey)
113
+ yield table;
114
+ else
115
+ rest.push(table);
116
+ }
117
+ }
118
+ yield* rest;
119
+ }
120
+ /**
121
+ * The column probe for one scanned table — seeded on the probed table itself, unseeded
122
+ * everywhere else. See this module's header for why that split is load-bearing.
123
+ *
124
+ * The column question is not "does the reach CONTAIN this key?" (which is all the table
125
+ * verb needs) but "does any body in the reach NAME this column?", so this loops the reach's
126
+ * bodies the way `assertNoAssertionNamesColumn` already does, each under its own resolver.
127
+ * `homeSchemaName` is the SCANNED table's schema — the root expression's own — because that
128
+ * is what its unqualified names resolve against.
129
+ *
130
+ * Each body is probed against EVERY republication target, not the base table alone: a
131
+ * reference through `create view v as select * from t` spells the column against `v`, and
132
+ * only the target sweep sees it. Targets are swept OUTER, bodies inner, base table first —
133
+ * so every match the base-table sweep finds is found at the body it always was, and the
134
+ * existing message spellings (`reaches it through view 'v'`) do not move; the widened
135
+ * targets only ever ADD refusals.
136
+ *
137
+ * After the target sweep, the broken-listed arm: a view / MV with an explicit column list
138
+ * over a `*` covering the column breaks OUTRIGHT on the drop (the star's arity changes),
139
+ * so an expression whose reach names such an object at all refuses, marked
140
+ * {@link ProbeMatch.breaksEntirely} so the guard can phrase it honestly.
141
+ */
142
+ function columnProbe(db, resolvers, homeSchemaName, ownTable, republication, columnName, resolveColumnInSource) {
143
+ return expr => {
144
+ if (!expr)
145
+ return undefined;
146
+ const reach = reachableObjects(db, expr, homeSchemaName, resolvers, { rowImageContext: true });
147
+ for (const [targetIndex, target] of republication.targets.entries()) {
148
+ for (const reached of reach.bodies) {
149
+ // Seeded ONLY on the probed table's own ROOT body, probing the BASE target: a
150
+ // CHECK / DEFAULT there is written against the row being written, so an
151
+ // unqualified name binds the owning table implicitly. A view body below it is
152
+ // a relation with no such seed, the root body of ANOTHER table's expression
153
+ // binds that table's row, not ours — and no expression ever binds a VIEW
154
+ // target implicitly, so a republication target takes the unseeded probe even
155
+ // at the root (a reference to it can only sit inside a subquery FROM).
156
+ const seeded = targetIndex === 0 && ownTable && reached.ownerKey === undefined;
157
+ // Row-image mode, the one non-constant site: a ROOT body is a table's CHECK /
158
+ // DEFAULT / generated expression and therefore always written-row context —
159
+ // `'own'` only for the seeded base-target case, `'foreign'` otherwise (another
160
+ // table's root body binds that table's image; a republication target at the
161
+ // root still binds the OWNING table's image, not the view being probed).
162
+ // Anything REACHED below the root is a view / MV body — a relation with no
163
+ // written row — so `'none'`.
164
+ const rowImage = reached.ownerKey === undefined ? (seeded ? 'own' : 'foreign') : 'none';
165
+ const hit = seeded
166
+ ? columnReferencedInCheckExpression(reached.body, target.tableName, columnName, reached.resolve, target.targetKey, rowImage, resolveColumnInSource)
167
+ : columnReferencedInAst(reached.body, target.tableName, columnName, reached.resolve, target.targetKey, rowImage, resolveColumnInSource);
168
+ if (hit)
169
+ return { path: reached.path };
170
+ }
171
+ }
172
+ for (const broken of republication.brokenListed) {
173
+ const namer = reach.namerOf(broken.key);
174
+ if (namer)
175
+ return { path: namer.path, breaksEntirely: broken.describe };
176
+ }
177
+ return undefined;
178
+ };
179
+ }
180
+ /** The first CHECK constraint of `table` the probe matches, described for an error. */
181
+ function findInChecks(table, names) {
182
+ for (const check of table.checkConstraints ?? []) {
183
+ // `!== undefined`, not truthiness: a match with an EMPTY path is a direct match.
184
+ const match = names(check.expr);
185
+ if (match !== undefined)
186
+ return { describe: describeCheck(check), ...match };
187
+ }
188
+ return undefined;
189
+ }
190
+ /**
191
+ * The first column DEFAULT / generated body of `table` the probe matches, described for
192
+ * an error. `skipColumnName` is the column being dropped, skipped only on the probed
193
+ * table itself; own-table generated bodies are skipped outright (see
194
+ * {@link findColumnExpressionDependent}).
195
+ */
196
+ function findInColumnExpressions(table, ownTable, skipColumnName, names) {
197
+ const lowerSkip = ownTable ? skipColumnName?.toLowerCase() : undefined;
198
+ for (const col of table.columns) {
199
+ if (lowerSkip !== undefined && col.name.toLowerCase() === lowerSkip)
200
+ continue;
201
+ // `!== undefined` for the same reason as in {@link findInChecks}: an empty-path
202
+ // match is a direct hit.
203
+ const defaultMatch = names(col.defaultValue ?? undefined);
204
+ if (defaultMatch !== undefined) {
205
+ return { describe: `the DEFAULT of column '${col.name}'`, ...defaultMatch };
206
+ }
207
+ if (ownTable)
208
+ continue;
209
+ const generatedMatch = names(col.generatedExpr);
210
+ if (generatedMatch !== undefined) {
211
+ return { describe: `the GENERATED expression of column '${col.name}'`, ...generatedMatch };
212
+ }
213
+ }
214
+ return undefined;
215
+ }
216
+ /**
217
+ * A table-level unnamed CHECK genuinely carries `name: undefined` — the `_check_<table>`
218
+ * spelling `manager.ts` produces is error text, not stored identity — so there is no name
219
+ * to quote and the expression stands in for one. That is also the only handle the user
220
+ * has on it: `DROP CONSTRAINT` resolves by name only, so an unnamed CHECK can only be
221
+ * removed by rebuilding its table.
222
+ */
223
+ function describeCheck(check) {
224
+ return check.name
225
+ ? `CHECK constraint '${check.name}'`
226
+ : `the CHECK constraint (${expressionToString(check.expr)})`;
227
+ }
228
+ //# sourceMappingURL=expression-dependents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expression-dependents.js","sourceRoot":"","sources":["../../../src/schema/expression-dependents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ import type * as AST from '../parser/ast.js';
2
+ import type { Database } from '../core/database.js';
3
+ import { type ResolveObjectRef, type TableRenameOpts } from './rename-rewriter.js';
4
+ import type { ObjectRefResolvers } from './object-ref-resolver.js';
5
+ /**
6
+ * The catalog query "can this stored body REACH that object?", as opposed to the
7
+ * one-hop "does this stored body NAME that object?" the rename walkers answer.
8
+ *
9
+ * A DROP guard that only asks the one-hop question is defeated by a view: an
10
+ * assertion body reading `v` where `create view v as select * from t` names `t`
11
+ * nowhere, so `drop table t` is accepted and every later write to the whole
12
+ * database fails on an assertion whose text does not mention `t`. Reach is the
13
+ * closure over STORED bodies — an assertion body names views and materialized
14
+ * views, whose bodies name further views and base tables — and this module is
15
+ * the one place that closure is computed, so the guard families that consume it
16
+ * cannot end up disagreeing about what "reaches" means.
17
+ *
18
+ * Each body is walked under ITS OWN home-schema resolver, so an unqualified
19
+ * name in a view body means what the planner would make of it *in that body*,
20
+ * not what it would mean in the root body. That is the whole reason
21
+ * {@link ObjectRefResolvers} is a per-home-schema factory over one snapshot.
22
+ *
23
+ * Scope-awareness survives the widening because the walk is
24
+ * {@link collectTableRefsInAst}, the third sink over the very traversal the
25
+ * rename uses: a body whose `with t as (…)` merely shadows the name still
26
+ * contributes no reference, so the closure cannot false-refuse where the direct
27
+ * probe already does not.
28
+ */
29
+ /**
30
+ * One stored body the closure walked, and how the root got to it.
31
+ *
32
+ * The root body itself is a `ReachedBody` with an empty {@link path} and no
33
+ * {@link ownerKey} — it is an assertion's CHECK, or a table's CHECK / DEFAULT /
34
+ * generated expression, none of which is a catalog object with a key of its own.
35
+ */
36
+ export interface ReachedBody {
37
+ /**
38
+ * Canonical `<schema>.<name>` key of the view / materialized view owning this
39
+ * body, or `undefined` for the ROOT body the closure started from.
40
+ */
41
+ ownerKey: string | undefined;
42
+ /**
43
+ * The stored bodies traversed from the root to this one INCLUSIVE, root-first,
44
+ * each described for an error message: `view 'v'`, `materialized view 'm'`.
45
+ * Empty for the root body itself, which is what tells a caller its match is
46
+ * direct and needs no "reaches it through …" clause.
47
+ */
48
+ path: ReadonlyArray<string>;
49
+ /** The AST to walk — an assertion/CHECK expression at the root, a view body below it. */
50
+ body: AST.AstNode;
51
+ /** Planner-parity resolver for THIS body's home schema. */
52
+ resolve: ResolveObjectRef;
53
+ }
54
+ /** What {@link reachableObjects} returns: the walked bodies, and who named what. */
55
+ export interface ObjectReach {
56
+ /**
57
+ * The root body followed by every stored body reached from it, breadth-first.
58
+ * A caller asking a question the closure does not answer itself — "does any
59
+ * body in the reach name `t.x`?" — probes these in order.
60
+ */
61
+ readonly bodies: ReadonlyArray<ReachedBody>;
62
+ /**
63
+ * The body that NAMES `key`, or `undefined` when nothing in the reach does.
64
+ * Breadth-first, so the answer is the shortest chain from the root and its
65
+ * {@link ReachedBody.path} is the shortest path to quote in an error.
66
+ *
67
+ * NOTE: two chains of EQUAL length to the same object tie on queue order,
68
+ * i.e. the order the parent bodies happened to name their children. Harmless
69
+ * while the path is advisory — either chain is a true one, and dropping the
70
+ * rule is the escape hatch for both — but a caller that ever needs a
71
+ * deterministic chain (a stable error-message fixture, say) has to order the
72
+ * candidates itself.
73
+ */
74
+ namerOf(key: string): ReachedBody | undefined;
75
+ }
76
+ /**
77
+ * Every object reachable from `root` through stored view / materialized-view
78
+ * bodies, plus the bodies traversed to get there.
79
+ *
80
+ * `homeSchemaName` is the root body's OWN schema — the assertion's schema, or
81
+ * the schema of the table carrying the CHECK / DEFAULT — because that is what
82
+ * the root's unqualified names resolve against.
83
+ *
84
+ * `resolvers` is REQUIRED rather than built here, and the reason is snapshot
85
+ * discipline (see {@link import('./object-ref-resolver.js').snapshotObjectRefResolvers}):
86
+ * a caller looping over many roots must share ONE pre-mutation snapshot, and a
87
+ * default would silently give each root its own.
88
+ *
89
+ * Termination is a visited set of object keys, so a mutually-referencing or
90
+ * otherwise malformed catalog is walked once per object rather than forever. A
91
+ * body naming an object that does not exist is not an error: the resolver's
92
+ * miss→home fallback keys it stably and the expansion below simply finds
93
+ * nothing to expand.
94
+ *
95
+ * `rootOpts` applies to the ROOT body's collection ONLY, never to a body
96
+ * reached below it. Every reached body is a view / materialized-view body — a
97
+ * relation, with no written-row context — so
98
+ * {@link import('./rename/table-rename.js').TableRenameOpts.rowImageContext}
99
+ * would be wrong there. It is REQUIRED at a root that is a table's CHECK
100
+ * constraint or column DEFAULT / generated body, where a bare `new.a` names the
101
+ * row image: rooted there without the flag, a table literally called `new`
102
+ * would land in the reach and false-refuse its own drop
103
+ * (`schema/expression-dependents.ts` passes it for exactly that reason). The
104
+ * assertion guards pass nothing: an assertion CHECK owns no `new.` / `old.`
105
+ * namespace either.
106
+ *
107
+ * Root-only stays correct even though a reached body CAN carry a bare unbound
108
+ * `new.` qualifier — a `with inverse` / `with defaults` expression holds one
109
+ * legitimately (`CREATE VIEW` accepts it there; the refs name the enclosing
110
+ * select's output row) — because the walker suppresses those SUBTREES itself,
111
+ * unconditionally, wherever the walk entered (`rename/table-rename.ts`,
112
+ * `visitRowImageSubtree`). The suppression travels with the position, not with
113
+ * this flag, so a reached view body self-suppresses and leaking `rootOpts`
114
+ * downward would change no answer. Pinned by
115
+ * `test/logic/41.10.4-drop-object-expression-dependents.sqllogic` (drop of a
116
+ * table named `new` reached through a `with inverse` view).
117
+ */
118
+ export declare function reachableObjects(db: Database, root: AST.AstNode, homeSchemaName: string, resolvers: ObjectRefResolvers, rootOpts?: TableRenameOpts): ObjectReach;
119
+ /**
120
+ * The `reaches it through view 'v2' -> view 'v1'` clause for a
121
+ * {@link ReachedBody.path}, or the empty string when the match is direct.
122
+ * Shared so every guard consuming the closure phrases an indirect refusal the
123
+ * same way — a user told to fix a rule whose body does not mention the dropped
124
+ * object needs the chain, or the message is unactionable.
125
+ */
126
+ export declare function describeReachPath(path: ReadonlyArray<string>): string;
127
+ //# sourceMappingURL=object-dependency-closure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-dependency-closure.d.ts","sourceRoot":"","sources":["../../../src/schema/object-dependency-closure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,GAAG,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAA6C,KAAK,gBAAgB,EAAE,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC9H,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH;;;;;;GAMG;AACH,MAAM,WAAW,WAAW;IAC3B;;;OAGG;IACH,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B;;;;;OAKG;IACH,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5B,yFAAyF;IACzF,IAAI,EAAE,GAAG,CAAC,OAAO,CAAC;IAClB,2DAA2D;IAC3D,OAAO,EAAE,gBAAgB,CAAC;CAC1B;AAED,oFAAoF;AACpF,MAAM,WAAW,WAAW;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,CAAC;IAC5C;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAAC;CAC9C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,wBAAgB,gBAAgB,CAC/B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,GAAG,CAAC,OAAO,EACjB,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,CAAC,EAAE,eAAe,GACxB,WAAW,CA+Bb;AAgDD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,GAAG,MAAM,CAErE"}
@@ -0,0 +1,117 @@
1
+ import { collectTableRefsInAst, objectRefKeySchema } from './rename-rewriter.js';
2
+ import { isMaintainedTable } from './derivation.js';
3
+ /**
4
+ * Every object reachable from `root` through stored view / materialized-view
5
+ * bodies, plus the bodies traversed to get there.
6
+ *
7
+ * `homeSchemaName` is the root body's OWN schema — the assertion's schema, or
8
+ * the schema of the table carrying the CHECK / DEFAULT — because that is what
9
+ * the root's unqualified names resolve against.
10
+ *
11
+ * `resolvers` is REQUIRED rather than built here, and the reason is snapshot
12
+ * discipline (see {@link import('./object-ref-resolver.js').snapshotObjectRefResolvers}):
13
+ * a caller looping over many roots must share ONE pre-mutation snapshot, and a
14
+ * default would silently give each root its own.
15
+ *
16
+ * Termination is a visited set of object keys, so a mutually-referencing or
17
+ * otherwise malformed catalog is walked once per object rather than forever. A
18
+ * body naming an object that does not exist is not an error: the resolver's
19
+ * miss→home fallback keys it stably and the expansion below simply finds
20
+ * nothing to expand.
21
+ *
22
+ * `rootOpts` applies to the ROOT body's collection ONLY, never to a body
23
+ * reached below it. Every reached body is a view / materialized-view body — a
24
+ * relation, with no written-row context — so
25
+ * {@link import('./rename/table-rename.js').TableRenameOpts.rowImageContext}
26
+ * would be wrong there. It is REQUIRED at a root that is a table's CHECK
27
+ * constraint or column DEFAULT / generated body, where a bare `new.a` names the
28
+ * row image: rooted there without the flag, a table literally called `new`
29
+ * would land in the reach and false-refuse its own drop
30
+ * (`schema/expression-dependents.ts` passes it for exactly that reason). The
31
+ * assertion guards pass nothing: an assertion CHECK owns no `new.` / `old.`
32
+ * namespace either.
33
+ *
34
+ * Root-only stays correct even though a reached body CAN carry a bare unbound
35
+ * `new.` qualifier — a `with inverse` / `with defaults` expression holds one
36
+ * legitimately (`CREATE VIEW` accepts it there; the refs name the enclosing
37
+ * select's output row) — because the walker suppresses those SUBTREES itself,
38
+ * unconditionally, wherever the walk entered (`rename/table-rename.ts`,
39
+ * `visitRowImageSubtree`). The suppression travels with the position, not with
40
+ * this flag, so a reached view body self-suppresses and leaking `rootOpts`
41
+ * downward would change no answer. Pinned by
42
+ * `test/logic/41.10.4-drop-object-expression-dependents.sqllogic` (drop of a
43
+ * table named `new` reached through a `with inverse` view).
44
+ */
45
+ export function reachableObjects(db, root, homeSchemaName, resolvers, rootOpts) {
46
+ const bodies = [];
47
+ const namer = new Map();
48
+ /** Object keys already expanded (or found not to be a stored body). */
49
+ const visited = new Set();
50
+ const queue = [{
51
+ ownerKey: undefined,
52
+ path: [],
53
+ body: root,
54
+ resolve: resolvers.forHomeSchema(homeSchemaName),
55
+ }];
56
+ // Breadth-first: `namer` and `path` then carry the SHORTEST chain to each
57
+ // object, which is the one an error message should quote.
58
+ for (let head = 0; head < queue.length; head++) {
59
+ const current = queue[head];
60
+ bodies.push(current);
61
+ // `ownerKey === undefined` identifies the ROOT uniquely — every body reached
62
+ // below it gets one from `childBody`.
63
+ const opts = current.ownerKey === undefined ? rootOpts : undefined;
64
+ for (const [key, nameLower] of collectTableRefsInAst(current.body, current.resolve, opts)) {
65
+ if (!namer.has(key))
66
+ namer.set(key, current);
67
+ if (visited.has(key))
68
+ continue;
69
+ visited.add(key);
70
+ const child = expandStoredBody(db, key, nameLower, resolvers, current);
71
+ if (child)
72
+ queue.push(child);
73
+ }
74
+ }
75
+ return { bodies, namerOf: key => namer.get(key) };
76
+ }
77
+ /**
78
+ * The stored body of the object `key` names, as a child of `parent` — or
79
+ * `undefined` when the key names something with no body to follow: a base table,
80
+ * or nothing at all.
81
+ *
82
+ * A materialized view is looked up as a TABLE (one `TableSchema`, one catalog
83
+ * name) and its body hangs off `derivation.selectAst`; see `schema/derivation.ts`.
84
+ */
85
+ function expandStoredBody(db, key, nameLower, resolvers, parent) {
86
+ const schema = db.schemaManager.getSchema(objectRefKeySchema(key, nameLower));
87
+ if (!schema)
88
+ return undefined;
89
+ const view = schema.getView(nameLower);
90
+ if (view)
91
+ return childBody(key, `view '${view.name}'`, view.selectAst, view.schemaName, resolvers, parent);
92
+ const table = schema.getTable(nameLower);
93
+ if (isMaintainedTable(table)) {
94
+ return childBody(key, `materialized view '${table.name}'`, table.derivation.selectAst, table.schemaName, resolvers, parent);
95
+ }
96
+ return undefined;
97
+ }
98
+ /** A reached stored body, with the parent's path extended by its own description. */
99
+ function childBody(ownerKey, describe, body, ownerSchemaName, resolvers, parent) {
100
+ return {
101
+ ownerKey,
102
+ path: [...parent.path, describe],
103
+ body,
104
+ resolve: resolvers.forHomeSchema(ownerSchemaName),
105
+ };
106
+ }
107
+ /**
108
+ * The `reaches it through view 'v2' -> view 'v1'` clause for a
109
+ * {@link ReachedBody.path}, or the empty string when the match is direct.
110
+ * Shared so every guard consuming the closure phrases an indirect refusal the
111
+ * same way — a user told to fix a rule whose body does not mention the dropped
112
+ * object needs the chain, or the message is unactionable.
113
+ */
114
+ export function describeReachPath(path) {
115
+ return path.length === 0 ? '' : ` through ${path.join(' -> ')}`;
116
+ }
117
+ //# sourceMappingURL=object-dependency-closure.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-dependency-closure.js","sourceRoot":"","sources":["../../../src/schema/object-dependency-closure.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import type { Database } from '../core/database.js';
2
+ import { type ResolveObjectRef, type TableRenameTarget } from './rename-rewriter.js';
3
+ /**
4
+ * Per-home-schema {@link ResolveObjectRef} factory over ONE catalog snapshot.
5
+ * The rename propagation walks dependents living in many schemas, and each
6
+ * dependent's unqualified names resolve under ITS OWN home schema path — so
7
+ * one statement needs one snapshot and many resolvers derived from it.
8
+ */
9
+ export interface ObjectRefResolvers {
10
+ /** Resolver for a stored body owned by `homeSchemaName`. Cached per home schema. */
11
+ forHomeSchema(homeSchemaName: string): ResolveObjectRef;
12
+ /**
13
+ * A sibling snapshot with one table rename applied — the catalog as it will
14
+ * look AFTER the statement. Used only to check the rename post-condition
15
+ * ({@link import('./rename/table-rename.js').renameTableInAst}: a rewritten
16
+ * reference must still resolve to the renamed object). Derived from this
17
+ * snapshot's name sets, never the live catalog, so the SNAPSHOT DISCIPLINE
18
+ * below holds for both.
19
+ */
20
+ withTableRenamed(schemaName: string, oldName: string, newName: string): ObjectRefResolvers;
21
+ }
22
+ /**
23
+ * The catalog-backed {@link ResolveObjectRef} the rename walkers consult to decide
24
+ * whether a table/view reference written in a stored body denotes the object being
25
+ * renamed / probed. It answers the way the PLANNER answers for that body — against
26
+ * the owning object's home schema path ({@link Database._homeSchemaPath}:
27
+ * `[homeSchema, ...session default path]`) — not against a single "default schema".
28
+ *
29
+ * Resolution rules:
30
+ * - **Qualified** (`schema` present) → `objectRefKey(schema, name)`, no catalog
31
+ * lookup. A qualified name means what it says even when the object does not
32
+ * exist (it may be the one about to be created, or the one just renamed away).
33
+ * - **Unqualified** → the first schema in the home path holding a table or view
34
+ * (a materialized view is a table) of that name.
35
+ * - **Miss in every schema** → the home schema's key, so a body naming an object
36
+ * that does not exist still gets a stable key rather than `undefined` —
37
+ * `undefined` is reserved for "no resolver could be consulted at all".
38
+ *
39
+ * NOTE: "the way the planner answers" is exact for the body kinds that plan under
40
+ * {@link Database._homeSchemaPath} — view bodies, materialized-view bodies, and
41
+ * assertion checks (`planner/stored-body-context.ts`). A CHECK constraint or a
42
+ * column DEFAULT / generated expression is stricter still: it plans under
43
+ * `schemaAuthoredContext` (`planner/building/schema-authored-context.ts`), the
44
+ * owning schema and NOTHING else, so an unqualified name the owning schema does
45
+ * not hold is an error there rather than a fallthrough down the session path.
46
+ * This resolver applies the looser home-path rule to all of them, which is
47
+ * deliberately safe rather than exact: the two rules can only disagree when the
48
+ * home schema lacks the name, and such a CHECK / DEFAULT cannot be evaluated at
49
+ * all — so no VALID body is ever matched (or missed) differently. Tighten to a
50
+ * per-body-kind path only if a rename ever needs to be right about a body the
51
+ * planner itself rejects.
52
+ *
53
+ * SNAPSHOT DISCIPLINE — the single most likely way to get this wrong: build the
54
+ * snapshot (`snapshotObjectRefResolvers` / `buildObjectRefResolver`) BEFORE the
55
+ * statement's first catalog mutation, and pass the same instance through the whole
56
+ * statement. Name-set lookups answer from the snapshot, NOT the live catalog, and
57
+ * that is load-bearing: rename propagation runs after the catalog swap, when the
58
+ * old name is gone — a live lookup of a bare `t` under `[main, temp]` after
59
+ * `main.t` was renamed would fall through to `temp.t` and silently match (or miss)
60
+ * the wrong object. The pre-mutation snapshot resolves every reference the way the
61
+ * body planned before the statement started, which is the question the rename is
62
+ * asking. (The session `schema_path` option is read at resolver-derivation time;
63
+ * it cannot change mid-statement.)
64
+ */
65
+ export declare function snapshotObjectRefResolvers(db: Database): ObjectRefResolvers;
66
+ /**
67
+ * Per-home-schema {@link TableRenameTarget} factory for one `ALTER TABLE … RENAME TO`
68
+ * statement: binds the renamed object's identity to a pre-mutation `resolve` and a
69
+ * post-rename `resolveAfter` (the {@link ObjectRefResolvers.withTableRenamed} sibling),
70
+ * both derived per body-owning home schema. One statement builds this once and hands
71
+ * every walk the target for the walked body's owner, which is what lets the rewrite
72
+ * enforce its post-condition — a rewritten reference must still resolve to the renamed
73
+ * object — at the sink.
74
+ */
75
+ export declare function tableRenameTargetsFor(resolvers: ObjectRefResolvers, schemaName: string, oldName: string, newName: string): (homeSchemaName: string) => TableRenameTarget;
76
+ /**
77
+ * Single-home convenience over {@link snapshotObjectRefResolvers}, for callers
78
+ * that walk bodies of one owning schema only (the DROP guards, a store module's
79
+ * own-table rewrite). Same snapshot discipline: build before the statement's
80
+ * first catalog mutation.
81
+ */
82
+ export declare function buildObjectRefResolver(db: Database, homeSchemaName: string): ResolveObjectRef;
83
+ //# sourceMappingURL=object-ref-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"object-ref-resolver.d.ts","sourceRoot":"","sources":["../../../src/schema/object-ref-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAgB,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAEnG;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAClC,oFAAoF;IACpF,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACxD;;;;;;;OAOG;IACH,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAAC;CAC3F;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,QAAQ,GAAG,kBAAkB,CAa3E;AAqCD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACpC,SAAS,EAAE,kBAAkB,EAC7B,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACb,CAAC,cAAc,EAAE,MAAM,KAAK,iBAAiB,CAiB/C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,EAAE,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,GAAG,gBAAgB,CAE7F"}