@quereus/quereus 4.4.1 → 4.5.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 (265) hide show
  1. package/README.md +1 -1
  2. package/dist/src/core/database-materialized-views-analysis.d.ts.map +1 -1
  3. package/dist/src/core/database-materialized-views-analysis.js +1 -0
  4. package/dist/src/core/database-materialized-views-analysis.js.map +1 -1
  5. package/dist/src/func/builtins/scalar.d.ts.map +1 -1
  6. package/dist/src/func/builtins/scalar.js +92 -3
  7. package/dist/src/func/builtins/scalar.js.map +1 -1
  8. package/dist/src/func/registration.d.ts +4 -0
  9. package/dist/src/func/registration.d.ts.map +1 -1
  10. package/dist/src/func/registration.js +1 -0
  11. package/dist/src/func/registration.js.map +1 -1
  12. package/dist/src/index.d.ts +3 -3
  13. package/dist/src/index.d.ts.map +1 -1
  14. package/dist/src/index.js +2 -2
  15. package/dist/src/index.js.map +1 -1
  16. package/dist/src/planner/analysis/coarsened-key.d.ts +4 -3
  17. package/dist/src/planner/analysis/coarsened-key.d.ts.map +1 -1
  18. package/dist/src/planner/analysis/coarsened-key.js +37 -3
  19. package/dist/src/planner/analysis/coarsened-key.js.map +1 -1
  20. package/dist/src/planner/analysis/comparison-collation.d.ts +33 -3
  21. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  22. package/dist/src/planner/analysis/comparison-collation.js +36 -2
  23. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  24. package/dist/src/planner/analysis/coverage-prover.d.ts +13 -2
  25. package/dist/src/planner/analysis/coverage-prover.d.ts.map +1 -1
  26. package/dist/src/planner/analysis/coverage-prover.js +10 -14
  27. package/dist/src/planner/analysis/coverage-prover.js.map +1 -1
  28. package/dist/src/planner/analysis/predicate-conjuncts.d.ts +10 -4
  29. package/dist/src/planner/analysis/predicate-conjuncts.d.ts.map +1 -1
  30. package/dist/src/planner/analysis/predicate-conjuncts.js +28 -7
  31. package/dist/src/planner/analysis/predicate-conjuncts.js.map +1 -1
  32. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
  33. package/dist/src/planner/analysis/query-rewrite-matcher.js +8 -27
  34. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
  35. package/dist/src/planner/analysis/scalar-subqueries.d.ts +22 -3
  36. package/dist/src/planner/analysis/scalar-subqueries.d.ts.map +1 -1
  37. package/dist/src/planner/analysis/scalar-subqueries.js +32 -3
  38. package/dist/src/planner/analysis/scalar-subqueries.js.map +1 -1
  39. package/dist/src/planner/analysis/set-op-type-merge.d.ts +64 -0
  40. package/dist/src/planner/analysis/set-op-type-merge.d.ts.map +1 -0
  41. package/dist/src/planner/analysis/set-op-type-merge.js +97 -0
  42. package/dist/src/planner/analysis/set-op-type-merge.js.map +1 -0
  43. package/dist/src/planner/building/coercion.d.ts +78 -0
  44. package/dist/src/planner/building/coercion.d.ts.map +1 -0
  45. package/dist/src/planner/building/coercion.js +137 -0
  46. package/dist/src/planner/building/coercion.js.map +1 -0
  47. package/dist/src/planner/building/constraint-builder.d.ts.map +1 -1
  48. package/dist/src/planner/building/constraint-builder.js +5 -18
  49. package/dist/src/planner/building/constraint-builder.js.map +1 -1
  50. package/dist/src/planner/building/expression.d.ts.map +1 -1
  51. package/dist/src/planner/building/expression.js +1 -104
  52. package/dist/src/planner/building/expression.js.map +1 -1
  53. package/dist/src/planner/building/function-call.d.ts.map +1 -1
  54. package/dist/src/planner/building/function-call.js +8 -0
  55. package/dist/src/planner/building/function-call.js.map +1 -1
  56. package/dist/src/planner/building/select-compound.d.ts.map +1 -1
  57. package/dist/src/planner/building/select-compound.js +10 -6
  58. package/dist/src/planner/building/select-compound.js.map +1 -1
  59. package/dist/src/planner/cost/conjunct-cost.d.ts +42 -0
  60. package/dist/src/planner/cost/conjunct-cost.d.ts.map +1 -0
  61. package/dist/src/planner/cost/conjunct-cost.js +62 -0
  62. package/dist/src/planner/cost/conjunct-cost.js.map +1 -0
  63. package/dist/src/planner/mutation/propagate.d.ts.map +1 -1
  64. package/dist/src/planner/mutation/propagate.js +1 -0
  65. package/dist/src/planner/mutation/propagate.js.map +1 -1
  66. package/dist/src/planner/nodes/async-gather-node.d.ts.map +1 -1
  67. package/dist/src/planner/nodes/async-gather-node.js +19 -8
  68. package/dist/src/planner/nodes/async-gather-node.js.map +1 -1
  69. package/dist/src/planner/nodes/bloom-join-node.d.ts +2 -0
  70. package/dist/src/planner/nodes/bloom-join-node.d.ts.map +1 -1
  71. package/dist/src/planner/nodes/bloom-join-node.js +10 -5
  72. package/dist/src/planner/nodes/bloom-join-node.js.map +1 -1
  73. package/dist/src/planner/nodes/join-node.d.ts +5 -0
  74. package/dist/src/planner/nodes/join-node.d.ts.map +1 -1
  75. package/dist/src/planner/nodes/join-node.js +8 -1
  76. package/dist/src/planner/nodes/join-node.js.map +1 -1
  77. package/dist/src/planner/nodes/join-utils.d.ts +57 -0
  78. package/dist/src/planner/nodes/join-utils.d.ts.map +1 -1
  79. package/dist/src/planner/nodes/join-utils.js +47 -0
  80. package/dist/src/planner/nodes/join-utils.js.map +1 -1
  81. package/dist/src/planner/nodes/key-set-semi-join-node.d.ts +87 -0
  82. package/dist/src/planner/nodes/key-set-semi-join-node.d.ts.map +1 -0
  83. package/dist/src/planner/nodes/key-set-semi-join-node.js +137 -0
  84. package/dist/src/planner/nodes/key-set-semi-join-node.js.map +1 -0
  85. package/dist/src/planner/nodes/merge-join-node.d.ts +2 -0
  86. package/dist/src/planner/nodes/merge-join-node.d.ts.map +1 -1
  87. package/dist/src/planner/nodes/merge-join-node.js +14 -6
  88. package/dist/src/planner/nodes/merge-join-node.js.map +1 -1
  89. package/dist/src/planner/nodes/plan-node-type.d.ts +1 -0
  90. package/dist/src/planner/nodes/plan-node-type.d.ts.map +1 -1
  91. package/dist/src/planner/nodes/plan-node-type.js +1 -0
  92. package/dist/src/planner/nodes/plan-node-type.js.map +1 -1
  93. package/dist/src/planner/nodes/set-operation-node.d.ts +68 -16
  94. package/dist/src/planner/nodes/set-operation-node.d.ts.map +1 -1
  95. package/dist/src/planner/nodes/set-operation-node.js +147 -26
  96. package/dist/src/planner/nodes/set-operation-node.js.map +1 -1
  97. package/dist/src/planner/nodes/table-access-nodes.d.ts +19 -1
  98. package/dist/src/planner/nodes/table-access-nodes.d.ts.map +1 -1
  99. package/dist/src/planner/nodes/table-access-nodes.js +13 -2
  100. package/dist/src/planner/nodes/table-access-nodes.js.map +1 -1
  101. package/dist/src/planner/optimizer.d.ts.map +1 -1
  102. package/dist/src/planner/optimizer.js +49 -0
  103. package/dist/src/planner/optimizer.js.map +1 -1
  104. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts +68 -0
  105. package/dist/src/planner/rules/access/rule-key-set-seek.d.ts.map +1 -0
  106. package/dist/src/planner/rules/access/rule-key-set-seek.js +408 -0
  107. package/dist/src/planner/rules/access/rule-key-set-seek.js.map +1 -0
  108. package/dist/src/planner/rules/access/rule-monotonic-range-access.d.ts.map +1 -1
  109. package/dist/src/planner/rules/access/rule-monotonic-range-access.js +2 -2
  110. package/dist/src/planner/rules/access/rule-monotonic-range-access.js.map +1 -1
  111. package/dist/src/planner/rules/access/rule-select-access-path.d.ts +27 -0
  112. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  113. package/dist/src/planner/rules/access/rule-select-access-path.js +31 -11
  114. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  115. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts +67 -41
  116. package/dist/src/planner/rules/join/equi-pair-extractor.d.ts.map +1 -1
  117. package/dist/src/planner/rules/join/equi-pair-extractor.js +72 -36
  118. package/dist/src/planner/rules/join/equi-pair-extractor.js.map +1 -1
  119. package/dist/src/planner/rules/join/rule-join-physical-selection.d.ts.map +1 -1
  120. package/dist/src/planner/rules/join/rule-join-physical-selection.js +14 -2
  121. package/dist/src/planner/rules/join/rule-join-physical-selection.js.map +1 -1
  122. package/dist/src/planner/rules/join/rule-monotonic-merge-join.d.ts.map +1 -1
  123. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js +11 -1
  124. package/dist/src/planner/rules/join/rule-monotonic-merge-join.js.map +1 -1
  125. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.d.ts +1 -1
  126. package/dist/src/planner/rules/predicate/rule-empty-relation-folding.js +1 -1
  127. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts +30 -0
  128. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.d.ts.map +1 -0
  129. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js +63 -0
  130. package/dist/src/planner/rules/predicate/rule-filter-conjunct-ordering.js.map +1 -0
  131. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts +17 -1
  132. package/dist/src/planner/rules/predicate/rule-filter-selectivity.d.ts.map +1 -1
  133. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js +206 -12
  134. package/dist/src/planner/rules/predicate/rule-filter-selectivity.js.map +1 -1
  135. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts +3 -1
  136. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.d.ts.map +1 -1
  137. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js +13 -1
  138. package/dist/src/planner/rules/predicate/rule-predicate-pushdown.js.map +1 -1
  139. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.d.ts.map +1 -1
  140. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js +18 -3
  141. package/dist/src/planner/rules/retrieve/rule-grow-retrieve.js.map +1 -1
  142. package/dist/src/planner/rules/shared/index-style-context.d.ts +17 -0
  143. package/dist/src/planner/rules/shared/index-style-context.d.ts.map +1 -1
  144. package/dist/src/planner/rules/shared/index-style-context.js +7 -0
  145. package/dist/src/planner/rules/shared/index-style-context.js.map +1 -1
  146. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts +24 -6
  147. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
  148. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +175 -60
  149. package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
  150. package/dist/src/planner/stats/catalog-stats.d.ts +52 -0
  151. package/dist/src/planner/stats/catalog-stats.d.ts.map +1 -1
  152. package/dist/src/planner/stats/catalog-stats.js +151 -5
  153. package/dist/src/planner/stats/catalog-stats.js.map +1 -1
  154. package/dist/src/planner/stats/index.d.ts +15 -0
  155. package/dist/src/planner/stats/index.d.ts.map +1 -1
  156. package/dist/src/planner/stats/index.js.map +1 -1
  157. package/dist/src/planner/stats/selectivity-combine.d.ts +53 -0
  158. package/dist/src/planner/stats/selectivity-combine.d.ts.map +1 -0
  159. package/dist/src/planner/stats/selectivity-combine.js +75 -0
  160. package/dist/src/planner/stats/selectivity-combine.js.map +1 -0
  161. package/dist/src/planner/util/column-origins.d.ts +58 -0
  162. package/dist/src/planner/util/column-origins.d.ts.map +1 -0
  163. package/dist/src/planner/util/column-origins.js +90 -0
  164. package/dist/src/planner/util/column-origins.js.map +1 -0
  165. package/dist/src/runtime/deferred-constraint-queue.d.ts +40 -0
  166. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  167. package/dist/src/runtime/deferred-constraint-queue.js +90 -9
  168. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  169. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  170. package/dist/src/runtime/emit/alter-table.js +6 -0
  171. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  172. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  173. package/dist/src/runtime/emit/asof-scan.js +8 -3
  174. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  175. package/dist/src/runtime/emit/between.d.ts.map +1 -1
  176. package/dist/src/runtime/emit/between.js +4 -38
  177. package/dist/src/runtime/emit/between.js.map +1 -1
  178. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  179. package/dist/src/runtime/emit/binary.js +2 -23
  180. package/dist/src/runtime/emit/binary.js.map +1 -1
  181. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  182. package/dist/src/runtime/emit/bloom-join.js +6 -35
  183. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  184. package/dist/src/runtime/emit/case.d.ts.map +1 -1
  185. package/dist/src/runtime/emit/case.js +40 -6
  186. package/dist/src/runtime/emit/case.js.map +1 -1
  187. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  188. package/dist/src/runtime/emit/constraint-check.js +32 -8
  189. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  190. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  191. package/dist/src/runtime/emit/dml-executor.js +26 -23
  192. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  193. package/dist/src/runtime/emit/filter.d.ts +18 -0
  194. package/dist/src/runtime/emit/filter.d.ts.map +1 -1
  195. package/dist/src/runtime/emit/filter.js +52 -10
  196. package/dist/src/runtime/emit/filter.js.map +1 -1
  197. package/dist/src/runtime/emit/join-key-extractor.d.ts +46 -0
  198. package/dist/src/runtime/emit/join-key-extractor.d.ts.map +1 -0
  199. package/dist/src/runtime/emit/join-key-extractor.js +60 -0
  200. package/dist/src/runtime/emit/join-key-extractor.js.map +1 -0
  201. package/dist/src/runtime/emit/join.d.ts.map +1 -1
  202. package/dist/src/runtime/emit/join.js +36 -15
  203. package/dist/src/runtime/emit/join.js.map +1 -1
  204. package/dist/src/runtime/emit/key-set-semi-join.d.ts +29 -0
  205. package/dist/src/runtime/emit/key-set-semi-join.d.ts.map +1 -0
  206. package/dist/src/runtime/emit/key-set-semi-join.js +143 -0
  207. package/dist/src/runtime/emit/key-set-semi-join.js.map +1 -0
  208. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  209. package/dist/src/runtime/emit/merge-join.js +15 -8
  210. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  211. package/dist/src/runtime/emit/operand-comparator.d.ts +40 -0
  212. package/dist/src/runtime/emit/operand-comparator.d.ts.map +1 -0
  213. package/dist/src/runtime/emit/operand-comparator.js +59 -0
  214. package/dist/src/runtime/emit/operand-comparator.js.map +1 -0
  215. package/dist/src/runtime/emit/scan.d.ts.map +1 -1
  216. package/dist/src/runtime/emit/scan.js +11 -2
  217. package/dist/src/runtime/emit/scan.js.map +1 -1
  218. package/dist/src/runtime/parallel-driver.d.ts +5 -2
  219. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  220. package/dist/src/runtime/parallel-driver.js +6 -2
  221. package/dist/src/runtime/parallel-driver.js.map +1 -1
  222. package/dist/src/runtime/register.d.ts.map +1 -1
  223. package/dist/src/runtime/register.js +2 -0
  224. package/dist/src/runtime/register.js.map +1 -1
  225. package/dist/src/runtime/types.d.ts +7 -0
  226. package/dist/src/runtime/types.d.ts.map +1 -1
  227. package/dist/src/runtime/types.js.map +1 -1
  228. package/dist/src/schema/function.d.ts +9 -0
  229. package/dist/src/schema/function.d.ts.map +1 -1
  230. package/dist/src/schema/function.js.map +1 -1
  231. package/dist/src/types/builtin-types.d.ts.map +1 -1
  232. package/dist/src/types/builtin-types.js +30 -4
  233. package/dist/src/types/builtin-types.js.map +1 -1
  234. package/dist/src/util/comparison.d.ts +25 -0
  235. package/dist/src/util/comparison.d.ts.map +1 -1
  236. package/dist/src/util/comparison.js +31 -0
  237. package/dist/src/util/comparison.js.map +1 -1
  238. package/dist/src/vtab/best-access-plan.d.ts +87 -0
  239. package/dist/src/vtab/best-access-plan.d.ts.map +1 -1
  240. package/dist/src/vtab/best-access-plan.js +87 -0
  241. package/dist/src/vtab/best-access-plan.js.map +1 -1
  242. package/dist/src/vtab/memory/layer/base.d.ts +6 -4
  243. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  244. package/dist/src/vtab/memory/layer/base.js +6 -4
  245. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  246. package/dist/src/vtab/memory/layer/manager.d.ts +60 -18
  247. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  248. package/dist/src/vtab/memory/layer/manager.js +100 -39
  249. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  250. package/dist/src/vtab/memory/layer/transaction.d.ts +15 -6
  251. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  252. package/dist/src/vtab/memory/layer/transaction.js +26 -11
  253. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  254. package/dist/src/vtab/memory/module.d.ts +14 -2
  255. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  256. package/dist/src/vtab/memory/module.js +51 -39
  257. package/dist/src/vtab/memory/module.js.map +1 -1
  258. package/dist/src/vtab/memory/table.d.ts +1 -1
  259. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  260. package/dist/src/vtab/memory/table.js +9 -2
  261. package/dist/src/vtab/memory/table.js.map +1 -1
  262. package/dist/src/vtab/table.d.ts +9 -1
  263. package/dist/src/vtab/table.d.ts.map +1 -1
  264. package/dist/src/vtab/table.js.map +1 -1
  265. package/package.json +3 -3
@@ -8,7 +8,7 @@ import { MemoryTableConnection } from './connection.js';
8
8
  import { MemoryVirtualTableConnection } from '../connection.js';
9
9
  import { QuereusError } from '../../../common/errors.js';
10
10
  import { ConflictResolution } from '../../../common/constants.js';
11
- import { buildUniqueConstraintSchema, buildForeignKeyConstraintSchema, buildCheckConstraintSchema, validateForeignKeyOverExistingRows, maintainedTableUniqueViolationError } from '../../../schema/constraint-builder.js';
11
+ import { buildUniqueConstraintSchema, buildForeignKeyConstraintSchema, buildCheckConstraintSchema, validateForeignKeyOverExistingRows, maintainedTableUniqueViolationError, formatKeyValue } from '../../../schema/constraint-builder.js';
12
12
  import { indexEnforcesUnique, uniqueEnforcementCollations, uniqueEnforcementComparators } from '../../../schema/unique-enforcement.js';
13
13
  import { generateIndexDDL, generateDropIndexDDL } from '../../../schema/ddl-generator.js';
14
14
  import { compareSqlValues, rowsValueIdentical, normalizeCollationName, comparisonSemanticsDiffer } from '../../../util/comparison.js';
@@ -1974,8 +1974,15 @@ export class MemoryTableManager {
1974
1974
  * Apply a single-attribute ALTER COLUMN change (NOT NULL, DEFAULT, DATA TYPE).
1975
1975
  * The caller supplies exactly one populated change; multi-attribute combinations
1976
1976
  * are rejected by the runtime before reaching this method.
1977
+ *
1978
+ * `validateOnly` runs everything up to (and including) the pre-mutation validation
1979
+ * passes — the effective-row NULL / conversion scans, the re-keyed UNIQUE probe, and
1980
+ * the primary-key re-key legality/representability passes — and returns before the
1981
+ * first mutation, throwing exactly what the real application would throw. The
1982
+ * isolation layer uses it to pre-flight an overlay's migration before the shared
1983
+ * underlying table mutates irreversibly (see `VirtualTable.alterSchema`).
1977
1984
  */
1978
- async alterColumn(change, rows) {
1985
+ async alterColumn(change, rows, validateOnly = false) {
1979
1986
  if (this.isReadOnly)
1980
1987
  throw new QuereusError(`Table '${this._tableName}' is read-only`, StatusCode.READONLY);
1981
1988
  const lockKey = `MemoryTable.SchemaChange:${this.schemaName}.${this._tableName}`;
@@ -2246,17 +2253,19 @@ export class MemoryTableManager {
2246
2253
  }
2247
2254
  else if (structuresRekeyed) {
2248
2255
  await this.validateRekeyedUniqueStructures(finalNewTableSchema, colIndex, rows);
2249
- // NOTE: `validateRekeyedPrimaryKey` deliberately ignores `rows`. It asserts that no
2250
- // LAYER of this manager's own chain holds a PK collision under the new comparator
2251
- // a physical property of the structures it is about to re-key, which a wrapper's
2252
- // merged row stream cannot speak to. A wrapper's staged rows live in the wrapper's
2253
- // own overlay, which enforces the PK itself, so the pair "one staged row + one
2254
- // committed row that collide only under the new collation" is checked by neither
2255
- // side. Rejecting it here would need the wrapper to expose its overlay's PK set, not
2256
- // just its merged rows.
2256
+ // Unlike the secondary-structure pass above which only ever judges the effective
2257
+ // rows the PK pass judges TWO row sets: the effective rows (`rows` when a wrapper
2258
+ // supplies them) decide whether the change is LEGAL, and this manager's own layer
2259
+ // chain decides whether the re-keyed trees can PHYSICALLY carry it. See
2260
+ // `validateRekeyedPrimaryKey` for why the sets differ and which status each raises.
2257
2261
  if (pkColumnRekeyed)
2258
- this.validateRekeyedPrimaryKey(finalNewTableSchema);
2262
+ await this.validateRekeyedPrimaryKey(finalNewTableSchema, rows);
2259
2263
  }
2264
+ // Dry run ends here: everything above validates without mutating (the one earlier
2265
+ // side effect, ensureSchemaChangeSafety's committed-layer drain, is semantically
2266
+ // neutral bookkeeping), and everything below mutates. See the method doc.
2267
+ if (validateOnly)
2268
+ return;
2260
2269
  this.baseLayer.updateSchema(finalNewTableSchema);
2261
2270
  // Both arms rebuild the base's structures under the schema `updateSchema` above just
2262
2271
  // swapped in — each MemoryIndex rebuilds its comparator from the CURRENT schema's
@@ -3116,21 +3125,35 @@ export class MemoryTableManager {
3116
3125
  * PRIMARY KEY arm of the `alter column … set collate` pre-pass. Runs before anything is
3117
3126
  * mutated, so a rejection leaves the table, the schema and the transaction untouched.
3118
3127
  *
3119
- * The primary tree is a map, not a multi-map, so unlike a secondary index — it cannot
3120
- * physically hold two rows whose keys collapse under the new comparator. Every layer the
3121
- * DDL connection still reads through therefore has to be collision-free, not just the
3122
- * transaction's effective view:
3128
+ * Asks two questions, in order, over two DIFFERENT row sets:
3129
+ *
3130
+ * 1. **Is the change legal at all?** probes the rows the DDL transaction can SEE: the
3131
+ * wrapper-supplied `rows` when a wrapper module (the isolation layer) holds the
3132
+ * transaction's pending rows outside this manager, otherwise this manager's own
3133
+ * layered view ({@link effectiveDdlRows}). A duplicate here is visible to a `select`
3134
+ * in this transaction, so the change is simply illegal → `CONSTRAINT`, naming the
3135
+ * colliding key.
3136
+ * 2. **Can the structures physically carry it?** — probes every layer of this manager's
3137
+ * own chain. The primary tree is a map, not a multi-map, so — unlike a secondary
3138
+ * index — no tree that a `rollback` / `rollback to savepoint` could restore may hold
3139
+ * two rows whose keys collapse under the new comparator. A collision confined to
3140
+ * committed rows the transaction has DELETED is therefore refused by physical
3141
+ * necessity, not because the data is invalid: the base must keep both rows for a
3142
+ * rollback, and a base re-keyed under the new collation could not represent the pair
3143
+ * at all → `BUSY` with the same "commit/rollback and retry" posture as
3144
+ * {@link ensureSchemaChangeSafety}. The persistent store backend refuses the same
3145
+ * shape for the same reason (its committed rows equally survive a rollback — see
3146
+ * `backlog/bug-store-pk-collate-rejects-deleted-row-collision`); accepting it would
3147
+ * take transaction-scoped DDL (`backlog/feat-transactional-ddl-native-backends`),
3148
+ * which Quereus does not have.
3123
3149
  *
3124
- * 1. **The effective view collides** `CONSTRAINT`. The duplicate is visible to a `select`
3125
- * in this transaction; the change is simply illegal.
3126
- * 2. **A layer beneath it collides** `BUSY`. Those rows are not visible now, but every
3127
- * such layer is the copy-on-write base the view reads through, and one of them may be a
3128
- * savepoint snapshot a `rollback to savepoint` must restore. A re-keyed tree could not
3129
- * represent the pair at all. Committing (or rolling back) settles the transaction and
3130
- * the ALTER can be retried — the same "commit/rollback and retry" posture as
3131
- * {@link ensureSchemaChangeSafety}.
3150
+ * The passes judge different sets because a wrapper's effective stream and this manager's
3151
+ * layers genuinely diverge: staged inserts exist only in the stream, deleted committed
3152
+ * rows only in the layers. When `rows` is supplied, the view layer holds the COMMITTED
3153
+ * rows not a subset of what pass 1 judged so pass 2 starts AT the view; when `rows`
3154
+ * is absent, pass 1 judged exactly the view's rows and pass 2 starts at its parent.
3132
3155
  *
3133
- * Case 2 is deliberately conservative. The chain holds one immutable layer per statement
3156
+ * Pass 2 is deliberately conservative. The chain holds one immutable layer per statement
3134
3157
  * boundary (see `MemoryTableConnection.createSavepoint`'s eager path), so it rejects any
3135
3158
  * transaction that has held a colliding pair at ANY statement boundary — even one whose
3136
3159
  * final view is clean and whose intermediate layer no savepoint can reach. Narrowing that
@@ -3142,40 +3165,78 @@ export class MemoryTableManager {
3142
3165
  * with no collisions anywhere in the chain, every primary key resolves to at most one row
3143
3166
  * in each layer under the new comparator.
3144
3167
  */
3145
- validateRekeyedPrimaryKey(newSchema) {
3168
+ async validateRekeyedPrimaryKey(newSchema, rows) {
3146
3169
  const newPkFunctions = createPrimaryKeyFunctions(newSchema, this.collationResolver);
3147
3170
  const connection = this.ddlConnection();
3148
3171
  const view = connection
3149
3172
  ? (connection.pendingTransactionLayer ?? connection.readLayer)
3150
3173
  : this.baseLayer;
3151
- this.assertNoPrimaryKeyCollision(view, newPkFunctions, StatusCode.CONSTRAINT, `UNIQUE constraint failed: ${this._tableName} primary key collides under new collation`);
3174
+ await this.assertNoPrimaryKeyCollisionInRows(rows ? rows() : this.effectiveDdlRows(), newPkFunctions, primaryKeyArity(newSchema) !== 1);
3152
3175
  // `ensureSchemaChangeSafety` has already drained every committed layer into the base and
3153
- // rejected sibling connections with open work, so the chain below the view holds only
3154
- // this transaction's own layers.
3155
- for (let layer = view.getParent(); layer; layer = layer.getParent()) {
3156
- this.assertNoPrimaryKeyCollision(layer, newPkFunctions, StatusCode.BUSY, `Cannot change the collation of a primary key column of table ${this._tableName}: `
3157
- + `rows this transaction has removed still collide under the new collation and must survive a rollback. `
3158
- + `Commit/rollback and retry.`);
3176
+ // rejected sibling connections with open work, so this walk covers the transaction's own
3177
+ // layers plus the base — every tree a rollback could restore.
3178
+ for (let layer = rows ? view : view.getParent(); layer; layer = layer.getParent()) {
3179
+ this.assertNoPrimaryKeyCollisionInLayer(layer, newPkFunctions);
3159
3180
  }
3160
3181
  }
3161
3182
  /**
3162
- * Raises `code` when two of `layer`'s rows share a primary key under `pkFunctions`.
3183
+ * A duplicate-key detector over the new primary key functions: feed it rows, and it returns
3184
+ * the key the moment one repeats. Shared by both arms of {@link validateRekeyedPrimaryKey}
3185
+ * so the async (effective-row) arm and the sync (layer-tree) arm cannot disagree about what
3186
+ * counts as a collision.
3187
+ *
3188
+ * NOTE: the probe holds every row it has seen, because the BTree derives its key from the
3189
+ * stored value — so an ALTER over a large table transiently doubles that table's row
3190
+ * references. If a wide table ever makes this the memory peak, key the probe by the PK
3191
+ * encoding (`primaryKeyFunctions.encode`) into a `Set` instead of by the row.
3192
+ */
3193
+ makePrimaryKeyProbe(pkFunctions) {
3194
+ const probe = new BTree((row) => pkFunctions.extractFromRow(row), pkFunctions.compare);
3195
+ return (row) => {
3196
+ const key = pkFunctions.extractFromRow(row);
3197
+ if (probe.get(key) !== undefined)
3198
+ return key;
3199
+ probe.insert(row);
3200
+ return undefined;
3201
+ };
3202
+ }
3203
+ /**
3204
+ * Legality arm of {@link validateRekeyedPrimaryKey}: raises `CONSTRAINT`, naming the key,
3205
+ * when two of the rows the DDL transaction can SEE share a primary key under `pkFunctions`.
3206
+ * Takes a wrapper's merged async stream or this manager's own synchronous layered view.
3207
+ */
3208
+ async assertNoPrimaryKeyCollisionInRows(rows, pkFunctions, keyIsTuple) {
3209
+ const seen = this.makePrimaryKeyProbe(pkFunctions);
3210
+ for await (const row of rows) {
3211
+ const key = seen(row);
3212
+ if (key === undefined)
3213
+ continue;
3214
+ const keyDesc = keyParts(key, keyIsTuple).map(formatKeyValue).join(', ');
3215
+ throw new QuereusError(`UNIQUE constraint failed: ${this._tableName} primary key collides under new collation (key: ${keyDesc})`, StatusCode.CONSTRAINT);
3216
+ }
3217
+ }
3218
+ /**
3219
+ * Representability arm of {@link validateRekeyedPrimaryKey}: raises `BUSY` when two of
3220
+ * `layer`'s physical rows share a primary key under `pkFunctions`. Its rows are ones a
3221
+ * rollback could restore, not ones the transaction can see, so this is never a statement
3222
+ * about the data's validity — hence the retryable status and wording.
3163
3223
  *
3164
3224
  * NOTE: O(rows) per layer, so O(layers × rows) for a whole chain — one more full pass than
3165
3225
  * the base rebuild the caller is about to do anyway. Fine for a statement this rare; if a
3166
3226
  * deep savepoint stack over a large table ever makes an ALTER slow, note that a layer's rows
3167
3227
  * differ from its parent's only at the keys it wrote, so the walk can be narrowed to those.
3168
3228
  */
3169
- assertNoPrimaryKeyCollision(layer, pkFunctions, code, message) {
3229
+ assertNoPrimaryKeyCollisionInLayer(layer, pkFunctions) {
3170
3230
  const tree = layer.getModificationTree('primary');
3171
3231
  if (!tree)
3172
3232
  return;
3173
- const probe = new BTree((row) => pkFunctions.extractFromRow(row), pkFunctions.compare);
3233
+ const seen = this.makePrimaryKeyProbe(pkFunctions);
3174
3234
  for (const row of iteratePrimaryRows(tree)) {
3175
- const key = pkFunctions.extractFromRow(row);
3176
- if (probe.get(key) !== undefined)
3177
- throw new QuereusError(message, code);
3178
- probe.insert(row);
3235
+ if (seen(row) !== undefined) {
3236
+ throw new QuereusError(`Cannot change the collation of a primary key column of table ${this._tableName}: `
3237
+ + `rows this transaction has removed still collide under the new collation and must survive a rollback. `
3238
+ + `Commit/rollback and retry.`, StatusCode.BUSY);
3239
+ }
3179
3240
  }
3180
3241
  }
3181
3242
  /**