@quereus/quereus 4.4.0 → 4.4.1

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 (316) hide show
  1. package/README.md +5 -0
  2. package/dist/src/common/types.d.ts +9 -0
  3. package/dist/src/common/types.d.ts.map +1 -1
  4. package/dist/src/common/types.js.map +1 -1
  5. package/dist/src/core/database-events.d.ts +49 -0
  6. package/dist/src/core/database-events.d.ts.map +1 -1
  7. package/dist/src/core/database-events.js +148 -0
  8. package/dist/src/core/database-events.js.map +1 -1
  9. package/dist/src/core/database-materialized-views-plan-builders.d.ts +2 -1
  10. package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
  11. package/dist/src/core/database-materialized-views-plan-builders.js +26 -5
  12. package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
  13. package/dist/src/core/database-materialized-views-plans.d.ts +6 -3
  14. package/dist/src/core/database-materialized-views-plans.d.ts.map +1 -1
  15. package/dist/src/core/database-materialized-views.d.ts +3 -2
  16. package/dist/src/core/database-materialized-views.d.ts.map +1 -1
  17. package/dist/src/core/database-materialized-views.js +26 -5
  18. package/dist/src/core/database-materialized-views.js.map +1 -1
  19. package/dist/src/core/database.d.ts +5 -0
  20. package/dist/src/core/database.d.ts.map +1 -1
  21. package/dist/src/core/database.js +21 -1
  22. package/dist/src/core/database.js.map +1 -1
  23. package/dist/src/core/internal-statement-cache.d.ts +18 -0
  24. package/dist/src/core/internal-statement-cache.d.ts.map +1 -0
  25. package/dist/src/core/internal-statement-cache.js +211 -0
  26. package/dist/src/core/internal-statement-cache.js.map +1 -0
  27. package/dist/src/func/builtins/aggregate.d.ts +13 -12
  28. package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
  29. package/dist/src/func/builtins/aggregate.js +45 -67
  30. package/dist/src/func/builtins/aggregate.js.map +1 -1
  31. package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
  32. package/dist/src/func/builtins/builtin-window-functions.js +63 -48
  33. package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
  34. package/dist/src/func/registration.d.ts +14 -1
  35. package/dist/src/func/registration.d.ts.map +1 -1
  36. package/dist/src/func/registration.js +25 -0
  37. package/dist/src/func/registration.js.map +1 -1
  38. package/dist/src/index.d.ts +8 -8
  39. package/dist/src/index.d.ts.map +1 -1
  40. package/dist/src/index.js +12 -11
  41. package/dist/src/index.js.map +1 -1
  42. package/dist/src/parser/parser.d.ts +6 -0
  43. package/dist/src/parser/parser.d.ts.map +1 -1
  44. package/dist/src/parser/parser.js +45 -34
  45. package/dist/src/parser/parser.js.map +1 -1
  46. package/dist/src/planner/analysis/comparison-collation.d.ts +2 -0
  47. package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
  48. package/dist/src/planner/analysis/comparison-collation.js +1 -1
  49. package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
  50. package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
  51. package/dist/src/planner/analysis/expression-fingerprint.js +29 -0
  52. package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
  53. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +6 -0
  54. package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
  55. package/dist/src/planner/analysis/query-rewrite-matcher.js +11 -2
  56. package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
  57. package/dist/src/planner/analysis/sat-checker.d.ts.map +1 -1
  58. package/dist/src/planner/analysis/sat-checker.js +43 -4
  59. package/dist/src/planner/analysis/sat-checker.js.map +1 -1
  60. package/dist/src/planner/building/delete.d.ts.map +1 -1
  61. package/dist/src/planner/building/delete.js +10 -3
  62. package/dist/src/planner/building/delete.js.map +1 -1
  63. package/dist/src/planner/building/expression.d.ts.map +1 -1
  64. package/dist/src/planner/building/expression.js +87 -6
  65. package/dist/src/planner/building/expression.js.map +1 -1
  66. package/dist/src/planner/building/foreign-key-builder.d.ts +44 -1
  67. package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
  68. package/dist/src/planner/building/foreign-key-builder.js +54 -0
  69. package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
  70. package/dist/src/planner/building/insert.d.ts.map +1 -1
  71. package/dist/src/planner/building/insert.js +9 -12
  72. package/dist/src/planner/building/insert.js.map +1 -1
  73. package/dist/src/planner/building/update.d.ts.map +1 -1
  74. package/dist/src/planner/building/update.js +13 -4
  75. package/dist/src/planner/building/update.js.map +1 -1
  76. package/dist/src/planner/framework/pass.js +3 -3
  77. package/dist/src/planner/nodes/dml-executor-node.d.ts +0 -9
  78. package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
  79. package/dist/src/planner/nodes/dml-executor-node.js.map +1 -1
  80. package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
  81. package/dist/src/planner/nodes/scalar.js +26 -3
  82. package/dist/src/planner/nodes/scalar.js.map +1 -1
  83. package/dist/src/planner/optimizer.d.ts.map +1 -1
  84. package/dist/src/planner/optimizer.js +5 -12
  85. package/dist/src/planner/optimizer.js.map +1 -1
  86. package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
  87. package/dist/src/planner/rules/access/rule-select-access-path.js +43 -18
  88. package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
  89. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -1
  90. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +23 -4
  91. package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -1
  92. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts +10 -8
  93. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts.map +1 -1
  94. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js +10 -8
  95. package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js.map +1 -1
  96. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +8 -1
  97. package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
  98. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js +4 -0
  99. package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js.map +1 -1
  100. package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
  101. package/dist/src/runtime/deferred-constraint-queue.js +10 -0
  102. package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
  103. package/dist/src/runtime/emit/aggregate-setup.d.ts +49 -0
  104. package/dist/src/runtime/emit/aggregate-setup.d.ts.map +1 -0
  105. package/dist/src/runtime/emit/aggregate-setup.js +99 -0
  106. package/dist/src/runtime/emit/aggregate-setup.js.map +1 -0
  107. package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
  108. package/dist/src/runtime/emit/aggregate.js +4 -69
  109. package/dist/src/runtime/emit/aggregate.js.map +1 -1
  110. package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
  111. package/dist/src/runtime/emit/alter-table.js +348 -165
  112. package/dist/src/runtime/emit/alter-table.js.map +1 -1
  113. package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
  114. package/dist/src/runtime/emit/asof-scan.js +6 -0
  115. package/dist/src/runtime/emit/asof-scan.js.map +1 -1
  116. package/dist/src/runtime/emit/between.d.ts.map +1 -1
  117. package/dist/src/runtime/emit/between.js +32 -3
  118. package/dist/src/runtime/emit/between.js.map +1 -1
  119. package/dist/src/runtime/emit/binary.d.ts.map +1 -1
  120. package/dist/src/runtime/emit/binary.js +22 -2
  121. package/dist/src/runtime/emit/binary.js.map +1 -1
  122. package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
  123. package/dist/src/runtime/emit/bloom-join.js +24 -3
  124. package/dist/src/runtime/emit/bloom-join.js.map +1 -1
  125. package/dist/src/runtime/emit/cast.d.ts.map +1 -1
  126. package/dist/src/runtime/emit/cast.js +10 -23
  127. package/dist/src/runtime/emit/cast.js.map +1 -1
  128. package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
  129. package/dist/src/runtime/emit/constraint-check.js +41 -44
  130. package/dist/src/runtime/emit/constraint-check.js.map +1 -1
  131. package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
  132. package/dist/src/runtime/emit/create-view.js +6 -0
  133. package/dist/src/runtime/emit/create-view.js.map +1 -1
  134. package/dist/src/runtime/emit/distinct.d.ts.map +1 -1
  135. package/dist/src/runtime/emit/distinct.js +6 -3
  136. package/dist/src/runtime/emit/distinct.js.map +1 -1
  137. package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
  138. package/dist/src/runtime/emit/dml-executor.js +182 -81
  139. package/dist/src/runtime/emit/dml-executor.js.map +1 -1
  140. package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
  141. package/dist/src/runtime/emit/hash-aggregate.js +16 -65
  142. package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
  143. package/dist/src/runtime/emit/insert.d.ts.map +1 -1
  144. package/dist/src/runtime/emit/insert.js +18 -7
  145. package/dist/src/runtime/emit/insert.js.map +1 -1
  146. package/dist/src/runtime/emit/join.js +5 -0
  147. package/dist/src/runtime/emit/join.js.map +1 -1
  148. package/dist/src/runtime/emit/materialized-view-helpers.d.ts +14 -1
  149. package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
  150. package/dist/src/runtime/emit/materialized-view-helpers.js +33 -3
  151. package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
  152. package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
  153. package/dist/src/runtime/emit/materialized-view.js +47 -6
  154. package/dist/src/runtime/emit/materialized-view.js.map +1 -1
  155. package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
  156. package/dist/src/runtime/emit/merge-join.js +18 -8
  157. package/dist/src/runtime/emit/merge-join.js.map +1 -1
  158. package/dist/src/runtime/emit/set-object-tags.d.ts +8 -0
  159. package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
  160. package/dist/src/runtime/emit/set-object-tags.js +8 -0
  161. package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
  162. package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
  163. package/dist/src/runtime/emit/set-operation.js +7 -4
  164. package/dist/src/runtime/emit/set-operation.js.map +1 -1
  165. package/dist/src/runtime/emit/sort.d.ts.map +1 -1
  166. package/dist/src/runtime/emit/sort.js +5 -2
  167. package/dist/src/runtime/emit/sort.js.map +1 -1
  168. package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
  169. package/dist/src/runtime/emit/subquery.js +113 -13
  170. package/dist/src/runtime/emit/subquery.js.map +1 -1
  171. package/dist/src/runtime/emit/temporal-arithmetic.d.ts +7 -0
  172. package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -1
  173. package/dist/src/runtime/emit/temporal-arithmetic.js +15 -7
  174. package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -1
  175. package/dist/src/runtime/emit/update.d.ts.map +1 -1
  176. package/dist/src/runtime/emit/update.js +44 -3
  177. package/dist/src/runtime/emit/update.js.map +1 -1
  178. package/dist/src/runtime/emit/window.d.ts.map +1 -1
  179. package/dist/src/runtime/emit/window.js +72 -55
  180. package/dist/src/runtime/emit/window.js.map +1 -1
  181. package/dist/src/runtime/foreign-key-actions.d.ts +36 -1
  182. package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
  183. package/dist/src/runtime/foreign-key-actions.js +238 -116
  184. package/dist/src/runtime/foreign-key-actions.js.map +1 -1
  185. package/dist/src/runtime/parallel-driver.d.ts +6 -1
  186. package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
  187. package/dist/src/runtime/parallel-driver.js +7 -1
  188. package/dist/src/runtime/parallel-driver.js.map +1 -1
  189. package/dist/src/runtime/types.d.ts +18 -0
  190. package/dist/src/runtime/types.d.ts.map +1 -1
  191. package/dist/src/runtime/types.js.map +1 -1
  192. package/dist/src/schema/catalog-persistability.d.ts +58 -0
  193. package/dist/src/schema/catalog-persistability.d.ts.map +1 -0
  194. package/dist/src/schema/catalog-persistability.js +87 -0
  195. package/dist/src/schema/catalog-persistability.js.map +1 -0
  196. package/dist/src/schema/catalog.d.ts +17 -1
  197. package/dist/src/schema/catalog.d.ts.map +1 -1
  198. package/dist/src/schema/catalog.js +25 -3
  199. package/dist/src/schema/catalog.js.map +1 -1
  200. package/dist/src/schema/constraint-builder.d.ts +20 -22
  201. package/dist/src/schema/constraint-builder.d.ts.map +1 -1
  202. package/dist/src/schema/constraint-builder.js +43 -36
  203. package/dist/src/schema/constraint-builder.js.map +1 -1
  204. package/dist/src/schema/ddl-generator.d.ts +23 -0
  205. package/dist/src/schema/ddl-generator.d.ts.map +1 -1
  206. package/dist/src/schema/ddl-generator.js +27 -0
  207. package/dist/src/schema/ddl-generator.js.map +1 -1
  208. package/dist/src/schema/function.d.ts +30 -1
  209. package/dist/src/schema/function.d.ts.map +1 -1
  210. package/dist/src/schema/function.js.map +1 -1
  211. package/dist/src/schema/manager.d.ts +28 -0
  212. package/dist/src/schema/manager.d.ts.map +1 -1
  213. package/dist/src/schema/manager.js +82 -14
  214. package/dist/src/schema/manager.js.map +1 -1
  215. package/dist/src/schema/schema-differ.d.ts.map +1 -1
  216. package/dist/src/schema/schema-differ.js +51 -12
  217. package/dist/src/schema/schema-differ.js.map +1 -1
  218. package/dist/src/schema/unique-enforcement.d.ts +31 -0
  219. package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
  220. package/dist/src/schema/unique-enforcement.js +37 -1
  221. package/dist/src/schema/unique-enforcement.js.map +1 -1
  222. package/dist/src/schema/window-function.d.ts +30 -10
  223. package/dist/src/schema/window-function.d.ts.map +1 -1
  224. package/dist/src/schema/window-function.js +20 -16
  225. package/dist/src/schema/window-function.js.map +1 -1
  226. package/dist/src/types/cast-semantics.d.ts +43 -0
  227. package/dist/src/types/cast-semantics.d.ts.map +1 -0
  228. package/dist/src/types/cast-semantics.js +59 -0
  229. package/dist/src/types/cast-semantics.js.map +1 -0
  230. package/dist/src/types/index.d.ts +1 -1
  231. package/dist/src/types/index.d.ts.map +1 -1
  232. package/dist/src/types/index.js +1 -1
  233. package/dist/src/types/index.js.map +1 -1
  234. package/dist/src/types/json-type.d.ts.map +1 -1
  235. package/dist/src/types/json-type.js +31 -10
  236. package/dist/src/types/json-type.js.map +1 -1
  237. package/dist/src/types/logical-type.d.ts +22 -0
  238. package/dist/src/types/logical-type.d.ts.map +1 -1
  239. package/dist/src/types/logical-type.js.map +1 -1
  240. package/dist/src/types/temporal-types.d.ts.map +1 -1
  241. package/dist/src/types/temporal-types.js +33 -11
  242. package/dist/src/types/temporal-types.js.map +1 -1
  243. package/dist/src/types/validation.d.ts +39 -1
  244. package/dist/src/types/validation.d.ts.map +1 -1
  245. package/dist/src/types/validation.js +60 -0
  246. package/dist/src/types/validation.js.map +1 -1
  247. package/dist/src/util/ast-spine-clone.d.ts +23 -0
  248. package/dist/src/util/ast-spine-clone.d.ts.map +1 -0
  249. package/dist/src/util/ast-spine-clone.js +41 -0
  250. package/dist/src/util/ast-spine-clone.js.map +1 -0
  251. package/dist/src/util/coercion.d.ts +13 -0
  252. package/dist/src/util/coercion.d.ts.map +1 -1
  253. package/dist/src/util/coercion.js +10 -1
  254. package/dist/src/util/coercion.js.map +1 -1
  255. package/dist/src/util/comparison.d.ts +89 -0
  256. package/dist/src/util/comparison.d.ts.map +1 -1
  257. package/dist/src/util/comparison.js +156 -27
  258. package/dist/src/util/comparison.js.map +1 -1
  259. package/dist/src/vtab/manifest.d.ts +7 -0
  260. package/dist/src/vtab/manifest.d.ts.map +1 -1
  261. package/dist/src/vtab/memory/connection.d.ts +14 -1
  262. package/dist/src/vtab/memory/connection.d.ts.map +1 -1
  263. package/dist/src/vtab/memory/connection.js +20 -2
  264. package/dist/src/vtab/memory/connection.js.map +1 -1
  265. package/dist/src/vtab/memory/layer/base.d.ts +6 -1
  266. package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
  267. package/dist/src/vtab/memory/layer/base.js +14 -5
  268. package/dist/src/vtab/memory/layer/base.js.map +1 -1
  269. package/dist/src/vtab/memory/layer/connection.d.ts +17 -0
  270. package/dist/src/vtab/memory/layer/connection.d.ts.map +1 -1
  271. package/dist/src/vtab/memory/layer/connection.js +26 -0
  272. package/dist/src/vtab/memory/layer/connection.js.map +1 -1
  273. package/dist/src/vtab/memory/layer/manager.d.ts +96 -13
  274. package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
  275. package/dist/src/vtab/memory/layer/manager.js +595 -119
  276. package/dist/src/vtab/memory/layer/manager.js.map +1 -1
  277. package/dist/src/vtab/memory/layer/plan-filter.d.ts +41 -20
  278. package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
  279. package/dist/src/vtab/memory/layer/plan-filter.js +57 -27
  280. package/dist/src/vtab/memory/layer/plan-filter.js.map +1 -1
  281. package/dist/src/vtab/memory/layer/row-convert.d.ts +21 -0
  282. package/dist/src/vtab/memory/layer/row-convert.d.ts.map +1 -0
  283. package/dist/src/vtab/memory/layer/row-convert.js +30 -0
  284. package/dist/src/vtab/memory/layer/row-convert.js.map +1 -0
  285. package/dist/src/vtab/memory/layer/scan-layer.d.ts +6 -3
  286. package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
  287. package/dist/src/vtab/memory/layer/scan-layer.js +48 -34
  288. package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
  289. package/dist/src/vtab/memory/layer/transaction.d.ts +127 -13
  290. package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
  291. package/dist/src/vtab/memory/layer/transaction.js +214 -65
  292. package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
  293. package/dist/src/vtab/memory/module.d.ts.map +1 -1
  294. package/dist/src/vtab/memory/module.js +9 -2
  295. package/dist/src/vtab/memory/module.js.map +1 -1
  296. package/dist/src/vtab/memory/table.d.ts.map +1 -1
  297. package/dist/src/vtab/memory/table.js +3 -2
  298. package/dist/src/vtab/memory/table.js.map +1 -1
  299. package/dist/src/vtab/memory/types.d.ts +27 -1
  300. package/dist/src/vtab/memory/types.d.ts.map +1 -1
  301. package/dist/src/vtab/memory/types.js +16 -1
  302. package/dist/src/vtab/memory/types.js.map +1 -1
  303. package/dist/src/vtab/memory/utils/primary-key.d.ts +9 -0
  304. package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
  305. package/dist/src/vtab/memory/utils/primary-key.js +11 -0
  306. package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
  307. package/dist/src/vtab/module.d.ts +63 -0
  308. package/dist/src/vtab/module.d.ts.map +1 -1
  309. package/dist/src/vtab/table.d.ts +24 -6
  310. package/dist/src/vtab/table.d.ts.map +1 -1
  311. package/dist/src/vtab/table.js.map +1 -1
  312. package/package.json +3 -3
  313. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts +0 -26
  314. package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts.map +0 -1
  315. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js +0 -66
  316. package/dist/src/planner/rules/cache/rule-in-subquery-cache.js.map +0 -1
@@ -1,6 +1,41 @@
1
1
  import type { Database } from '../core/database.js';
2
2
  import type { TableSchema } from '../schema/table.js';
3
- import type { Row } from '../common/types.js';
3
+ import type { Row, SqlValue } from '../common/types.js';
4
+ import type { BatchableRestrictFk } from '../planner/building/foreign-key-builder.js';
5
+ /** Per-FK accumulation state for one statement execution. */
6
+ export interface ParentRestrictBatchEntry extends BatchableRestrictFk {
7
+ /** Deduplicated OLD referenced-key tuples, keyed by an injective serialization. */
8
+ readonly keys: Map<string, SqlValue[]>;
9
+ }
10
+ export type ParentRestrictBatch = ParentRestrictBatchEntry[];
11
+ /**
12
+ * Allocate the per-statement-execution accumulation state for the batchable
13
+ * inbound RESTRICT FKs. Must be called once per execution (never cached on the
14
+ * emit closure) so a re-run prepared statement starts empty.
15
+ */
16
+ export declare function createParentRestrictBatch(fks: readonly BatchableRestrictFk[]): ParentRestrictBatch;
17
+ /**
18
+ * Accumulate one mutated parent row's OLD referenced-key tuples into the batch
19
+ * — the batched replacement for the per-row RESTRICT probes, applying the same
20
+ * skip rules: MATCH SIMPLE (a tuple containing NULL is unreferenceable) and,
21
+ * for UPDATE, the referenced-column-change short-circuit.
22
+ */
23
+ export declare function accumulateParentRestrictKeys(batch: ParentRestrictBatch, operation: 'delete' | 'update', oldRow: Row, newRow?: Row): void;
24
+ /**
25
+ * End-of-statement flush: one chunked probe per inbound RESTRICT FK over the
26
+ * accumulated key tuples. Fired by `runWithStatementSavepoints` after the row
27
+ * loop and BEFORE the deferred-maintenance flush and the statement savepoint
28
+ * release, so a hit throws the existing RESTRICT error shape and rolls the
29
+ * whole statement back. Re-checks the `foreign_keys` pragma and the
30
+ * trust-the-origin suppression at flush time, mirroring the per-row pre-check.
31
+ *
32
+ * The probe selects the FK columns (not `select 1`) so the violating key stays
33
+ * available for a future richer message; the message itself is unchanged and
34
+ * matcher-compatible (`/constraint|foreign|fk/i`). Comparison rides plain SQL
35
+ * `=` / `IN` against the child column — identical collation semantics to the
36
+ * per-row `NOT EXISTS` by construction.
37
+ */
38
+ export declare function flushParentRestrictBatch(db: Database, parentTable: TableSchema, operation: 'delete' | 'update', batch: ParentRestrictBatch): Promise<void>;
4
39
  /**
5
40
  * Executes cascading foreign key actions when a parent row is deleted or updated.
6
41
  *
@@ -1 +1 @@
1
- {"version":3,"file":"foreign-key-actions.d.ts","sourceRoot":"","sources":["../../../src/runtime/foreign-key-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAA8B,MAAM,oBAAoB,CAAC;AAGlF,OAAO,KAAK,EAAE,GAAG,EAAY,MAAM,oBAAoB,CAAC;AAkBxD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,wBAAwB,CAC7C,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,EACZ,UAAU,UAAQ,EAClB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,OAAO,CAAC,IAAI,CAAC,CAyDf;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,+BAA+B,CACpD,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,EACZ,UAAU,UAAQ,GAChB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAsB,0CAA0C,CAC/D,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,EACZ,UAAU,UAAQ,EAClB,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GACnB,OAAO,CAAC,IAAI,CAAC,CAqJf;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,2CAA2C,CAChE,EAAE,EAAE,QAAQ,EACZ,WAAW,EAAE,WAAW,EACxB,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,EACZ,UAAU,UAAQ,GAChB,OAAO,CAAC,IAAI,CAAC,CAwEf;AA+GD;;;;;;;;;;;;GAYG;AACH,wBAAsB,4BAA4B,CACjD,EAAE,EAAE,QAAQ,EACZ,gBAAgB,EAAE,WAAW,EAC7B,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,GACV,OAAO,CAAC,IAAI,CAAC,CAuBf;AAmMD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,oCAAoC,CACzD,EAAE,EAAE,QAAQ,EACZ,gBAAgB,EAAE,WAAW,EAC7B,SAAS,EAAE,QAAQ,GAAG,QAAQ,EAC9B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,GAAG,GACV,OAAO,CAAC,IAAI,CAAC,CA0Bf"}
1
+ {"version":3,"file":"foreign-key-actions.d.ts","sourceRoot":"","sources":["../../../src/runtime/foreign-key-actions.ts"],"names":[],"mappings":"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"}
@@ -7,6 +7,154 @@ import { sqlValueIdentical } from '../util/comparison.js';
7
7
  import { resolveSlotBasisSource } from '../schema/lens-prover.js';
8
8
  import { findLogicalParentFkRefs, logicalToBasisColumnMap, matchingBasisFksForLensRef, basisFksOverriddenByDivergentLensFk, } from '../schema/lens-fk-discovery.js';
9
9
  const log = createLogger('runtime:fk-actions');
10
+ /** Keys probed per child-table query — a handful of compiles per statement, not one per row. */
11
+ const RESTRICT_BATCH_CHUNK = 500;
12
+ /**
13
+ * Allocate the per-statement-execution accumulation state for the batchable
14
+ * inbound RESTRICT FKs. Must be called once per execution (never cached on the
15
+ * emit closure) so a re-run prepared statement starts empty.
16
+ */
17
+ export function createParentRestrictBatch(fks) {
18
+ return fks.map(fk => ({ ...fk, keys: new Map() }));
19
+ }
20
+ /**
21
+ * Injective serialization of a referenced-key tuple for dedup. Each segment is
22
+ * type-tagged and (for variable-length payloads) length-prefixed, so two
23
+ * distinct tuples can never collide — a collision would silently drop a probe.
24
+ */
25
+ function serializeKeyTuple(values) {
26
+ let out = '';
27
+ for (const v of values) {
28
+ if (typeof v === 'number')
29
+ out += `n${v};`;
30
+ else if (typeof v === 'bigint')
31
+ out += `i${v};`;
32
+ else if (typeof v === 'string')
33
+ out += `s${v.length}:${v};`;
34
+ else if (v instanceof Uint8Array)
35
+ out += `b${v.length}:${Array.from(v).join(',')};`;
36
+ else
37
+ out += `x${String(v)};`;
38
+ }
39
+ return out;
40
+ }
41
+ /**
42
+ * The shared UPDATE short-circuit behind every parent-side RESTRICT / cascade site:
43
+ * true when at least one of `colIndices` would actually STORE a different value.
44
+ *
45
+ * Both rows arrive in declared (stored) form: `oldRow` comes from the source scan,
46
+ * and `newRow` was converted to declared column types by the DML emitters before
47
+ * the executor's pre-vtab checks run (ticket `json-coerce-once-at-dml-source` —
48
+ * this used to re-convert `newRow` per cell because it arrived raw). So the test
49
+ * is plain value identity.
50
+ *
51
+ * Deliberately BINARY identity rather than the column's collation: a NOCASE parent
52
+ * column still stores `'A'` and `'a'` as distinct values, and the child's own match
53
+ * runs under the CHILD column's collation, which may well be BINARY. A spurious
54
+ * "changed" costs one redundant RESTRICT probe; a spurious "unchanged" would SKIP
55
+ * enforcement entirely — so identity is the safe direction.
56
+ *
57
+ * NOTE: every caller today supplies declared-form rows (the DML executor's converted
58
+ * NEW row; materialized-view maintenance passes the changes `applyMaintenance`
59
+ * reports, i.e. what the backing actually stored). A future caller that hands in a
60
+ * RAW proposed row would see spurious "changed" on any cell conversion normalizes —
61
+ * harmless on the cascade path, but a spurious RESTRICT error on the assert path. If
62
+ * one appears, convert at that caller rather than re-introducing a per-cell coercion
63
+ * here.
64
+ */
65
+ function anyReferencedColumnChanged(colIndices, oldRow, newRow) {
66
+ for (const idx of colIndices) {
67
+ if (!sqlValueIdentical(oldRow[idx], newRow[idx]))
68
+ return true;
69
+ }
70
+ return false;
71
+ }
72
+ /**
73
+ * Accumulate one mutated parent row's OLD referenced-key tuples into the batch
74
+ * — the batched replacement for the per-row RESTRICT probes, applying the same
75
+ * skip rules: MATCH SIMPLE (a tuple containing NULL is unreferenceable) and,
76
+ * for UPDATE, the referenced-column-change short-circuit.
77
+ */
78
+ export function accumulateParentRestrictKeys(batch, operation, oldRow, newRow) {
79
+ for (const entry of batch) {
80
+ // UPDATE: only rows that actually re-key a referenced column contribute.
81
+ if (operation === 'update' && newRow !== undefined) {
82
+ if (!anyReferencedColumnChanged(entry.parentColIndices, oldRow, newRow))
83
+ continue;
84
+ }
85
+ // MATCH SIMPLE: NULL parent values cannot be referenced.
86
+ const values = entry.parentColIndices.map(idx => oldRow[idx]);
87
+ if (values.some(v => v === null || v === undefined))
88
+ continue;
89
+ const key = serializeKeyTuple(values);
90
+ if (!entry.keys.has(key))
91
+ entry.keys.set(key, values);
92
+ }
93
+ }
94
+ /**
95
+ * End-of-statement flush: one chunked probe per inbound RESTRICT FK over the
96
+ * accumulated key tuples. Fired by `runWithStatementSavepoints` after the row
97
+ * loop and BEFORE the deferred-maintenance flush and the statement savepoint
98
+ * release, so a hit throws the existing RESTRICT error shape and rolls the
99
+ * whole statement back. Re-checks the `foreign_keys` pragma and the
100
+ * trust-the-origin suppression at flush time, mirroring the per-row pre-check.
101
+ *
102
+ * The probe selects the FK columns (not `select 1`) so the violating key stays
103
+ * available for a future richer message; the message itself is unchanged and
104
+ * matcher-compatible (`/constraint|foreign|fk/i`). Comparison rides plain SQL
105
+ * `=` / `IN` against the child column — identical collation semantics to the
106
+ * per-row `NOT EXISTS` by construction.
107
+ */
108
+ export async function flushParentRestrictBatch(db, parentTable, operation, batch) {
109
+ if (db._isFkRestrictSuppressed())
110
+ return;
111
+ if (!db.options.getBooleanOption('foreign_keys'))
112
+ return;
113
+ for (const entry of batch) {
114
+ if (entry.keys.size === 0)
115
+ continue;
116
+ const { childTable, fk } = entry;
117
+ const childColQuoted = fk.columns.map(idx => quoteIdentifier(childTable.columns[idx].name));
118
+ const schemaPrefix = childTable.schemaName.toLowerCase() !== 'main'
119
+ ? `${quoteIdentifier(childTable.schemaName)}.`
120
+ : '';
121
+ const from = `${schemaPrefix}${quoteIdentifier(childTable.name)}`;
122
+ const tuples = [...entry.keys.values()];
123
+ for (let i = 0; i < tuples.length; i += RESTRICT_BATCH_CHUNK) {
124
+ const chunk = tuples.slice(i, i + RESTRICT_BATCH_CHUNK);
125
+ let whereClause;
126
+ let params;
127
+ if (childColQuoted.length === 1) {
128
+ whereClause = `${childColQuoted[0]} in (${chunk.map(() => '?').join(', ')})`;
129
+ params = chunk.map(t => t[0]);
130
+ }
131
+ else {
132
+ whereClause = chunk
133
+ .map(() => `(${childColQuoted.map(c => `${c} = ?`).join(' and ')})`)
134
+ .join(' or ');
135
+ params = chunk.flat();
136
+ }
137
+ const sql = `select ${childColQuoted.join(', ')} from ${from} where ${whereClause} limit 1`;
138
+ log('RESTRICT batch check (%s): child=%s keys=%d', operation, childTable.name, chunk.length);
139
+ const stmt = db.prepare(sql);
140
+ try {
141
+ stmt.bindAll(params);
142
+ let referenced = false;
143
+ for await (const _row of stmt._iterateRowsRaw()) {
144
+ referenced = true;
145
+ break;
146
+ }
147
+ if (referenced) {
148
+ const opName = operation === 'delete' ? 'DELETE' : 'UPDATE';
149
+ throw new QuereusError(`FOREIGN KEY constraint failed: ${opName} on '${parentTable.name}' violates RESTRICT from '${childTable.name}'`, StatusCode.CONSTRAINT);
150
+ }
151
+ }
152
+ finally {
153
+ await stmt.finalize();
154
+ }
155
+ }
156
+ }
157
+ }
10
158
  /**
11
159
  * Executes cascading foreign key actions when a parent row is deleted or updated.
12
160
  *
@@ -56,6 +204,17 @@ export async function executeForeignKeyActions(db, parentTable, operation, oldRo
56
204
  const parentColIndices = resolveReferencedColumns(fk, parentTable);
57
205
  if (parentColIndices.length !== fk.columns.length)
58
206
  continue;
207
+ // UPDATE: an ON UPDATE action propagates a change to the parent's REFERENCED
208
+ // columns, so an update that leaves them alone must not touch the child at
209
+ // all. Without this the child DML re-issues unconditionally: SET DEFAULT /
210
+ // SET NULL silently re-point or null the child, and CASCADE rewrites the
211
+ // child to the value it already holds (a real storage write plus a
212
+ // data-change event with an empty changed-column list). Same short-circuit
213
+ // the RESTRICT sites and the lens walker already apply.
214
+ if (operation === 'update' && newRow !== undefined) {
215
+ if (!anyReferencedColumnChanged(parentColIndices, oldRow, newRow))
216
+ continue;
217
+ }
59
218
  // Get old parent values for the referenced columns
60
219
  const oldParentValues = parentColIndices.map(idx => oldRow[idx]);
61
220
  // Skip if any old value is NULL (NULLs don't participate in FK matching)
@@ -186,14 +345,7 @@ export async function assertTransitiveRestrictsForParentMutation(db, parentTable
186
345
  // UPDATE-only short-circuit: skip if no referenced parent column changed.
187
346
  let newParentValues;
188
347
  if (operation === 'update' && newRow !== undefined) {
189
- let anyChanged = false;
190
- for (const idx of parentColIndices) {
191
- if (!sqlValueIdentical(oldRow[idx], newRow[idx])) {
192
- anyChanged = true;
193
- break;
194
- }
195
- }
196
- if (!anyChanged)
348
+ if (!anyReferencedColumnChanged(parentColIndices, oldRow, newRow))
197
349
  continue;
198
350
  newParentValues = parentColIndices.map(idx => newRow[idx]);
199
351
  }
@@ -205,62 +357,61 @@ export async function assertTransitiveRestrictsForParentMutation(db, parentTable
205
357
  : '';
206
358
  const sql = `select * from ${schemaPrefix}${quoteIdentifier(childTable.name)} where ${whereClause}`;
207
359
  log('TRANSITIVE pre-walk: %s with params %o', sql, oldParentValues);
208
- const stmt = db.prepare(sql);
209
- try {
210
- stmt.bindAll(oldParentValues);
211
- for await (const childOldRow of stmt._iterateRowsRaw()) {
212
- let childNewRow;
213
- let childOp;
214
- if (action === 'cascade' && operation === 'delete') {
215
- childOp = 'delete';
216
- childNewRow = undefined;
217
- }
218
- else if (action === 'cascade' && operation === 'update' && newParentValues) {
219
- childOp = 'update';
220
- const next = [...childOldRow];
221
- for (let i = 0; i < fk.columns.length; i++) {
222
- next[fk.columns[i]] = newParentValues[i];
223
- }
224
- childNewRow = next;
225
- }
226
- else if (action === 'setNull') {
227
- childOp = 'update';
228
- const next = [...childOldRow];
229
- for (let i = 0; i < fk.columns.length; i++) {
230
- next[fk.columns[i]] = null;
231
- }
232
- childNewRow = next;
233
- }
234
- else if (action === 'setDefault') {
235
- // SET DEFAULT recursion: pass the child OLD row as both
236
- // old and new. The recursion's column-change short-circuit
237
- // will treat this as "no FK column moved" and the per-target
238
- // cascade SQL (executeSingleFKAction) still fires its own
239
- // RESTRICT enforcement for non-rowid-chained backends. This
240
- // matches the coverage gap SET DEFAULT already has in
241
- // rowid-chained backends — no regression beyond status quo.
242
- childOp = 'update';
243
- childNewRow = childOldRow;
360
+ // Cached internal statement leased for the whole scan (compiled once per
361
+ // shape, not re-prepared per parent row). The recursion inside this loop
362
+ // can re-enter with the SAME SQL text (self-referential / diamond FK graph)
363
+ // while this statement is still iterating — that re-entry hits the cache's
364
+ // busy-guard and gets its own fresh one-shot statement, never this live
365
+ // cursor. See InternalStatementCache.iterate.
366
+ for await (const childOldRow of db._internalStatementCache.iterate(sql, oldParentValues)) {
367
+ let childNewRow;
368
+ let childOp;
369
+ if (action === 'cascade' && operation === 'delete') {
370
+ childOp = 'delete';
371
+ childNewRow = undefined;
372
+ }
373
+ else if (action === 'cascade' && operation === 'update' && newParentValues) {
374
+ childOp = 'update';
375
+ const next = [...childOldRow];
376
+ for (let i = 0; i < fk.columns.length; i++) {
377
+ next[fk.columns[i]] = newParentValues[i];
244
378
  }
245
- else {
246
- continue;
379
+ childNewRow = next;
380
+ }
381
+ else if (action === 'setNull') {
382
+ childOp = 'update';
383
+ const next = [...childOldRow];
384
+ for (let i = 0; i < fk.columns.length; i++) {
385
+ next[fk.columns[i]] = null;
247
386
  }
248
- // Recurse carrying the SAME `lensRouted`: the nested levels are the
249
- // transitive closure of *this* write, so when the top-level write is
250
- // lens-routed every level inherits the logical FK semantics. This is
251
- // load-bearing for a logical RESTRICT sitting below an *agreeing*
252
- // basis cascade (basis + logical both cascade): at runtime that basis
253
- // cascade executes as a basis-direct write — which does NOT fire the
254
- // deeper lens RESTRICT — and the agreeing lens cascade is elided, so it
255
- // never re-enters through the child view either. The ONLY place that
256
- // deeper RESTRICT is caught is this pre-walk recursing with the lens flag
257
- // still set. For a basis-direct top-level write `lensRouted` is already
258
- // false, so the recursion correctly stays basis-only throughout.
259
- await assertTransitiveRestrictsForParentMutation(db, childTable, childOp, childOldRow, childNewRow, lensRouted, visitedSet);
387
+ childNewRow = next;
260
388
  }
261
- }
262
- finally {
263
- await stmt.finalize();
389
+ else if (action === 'setDefault') {
390
+ // SET DEFAULT recursion: pass the child OLD row as both
391
+ // old and new. The recursion's column-change short-circuit
392
+ // will treat this as "no FK column moved" and the per-target
393
+ // cascade SQL (executeSingleFKAction) still fires its own
394
+ // RESTRICT enforcement for non-rowid-chained backends. This
395
+ // matches the coverage gap SET DEFAULT already has in
396
+ // rowid-chained backends — no regression beyond status quo.
397
+ childOp = 'update';
398
+ childNewRow = childOldRow;
399
+ }
400
+ else {
401
+ continue;
402
+ }
403
+ // Recurse carrying the SAME `lensRouted`: the nested levels are the
404
+ // transitive closure of *this* write, so when the top-level write is
405
+ // lens-routed every level inherits the logical FK semantics. This is
406
+ // load-bearing for a logical RESTRICT sitting below an *agreeing*
407
+ // basis cascade (basis + logical both cascade): at runtime that basis
408
+ // cascade executes as a basis-direct write — which does NOT fire the
409
+ // deeper lens RESTRICT — and the agreeing lens cascade is elided, so it
410
+ // never re-enters through the child view either. The ONLY place that
411
+ // deeper RESTRICT is caught is this pre-walk recursing with the lens flag
412
+ // still set. For a basis-direct top-level write `lensRouted` is already
413
+ // false, so the recursion correctly stays basis-only throughout.
414
+ await assertTransitiveRestrictsForParentMutation(db, childTable, childOp, childOldRow, childNewRow, lensRouted, visitedSet);
264
415
  }
265
416
  }
266
417
  }
@@ -317,14 +468,7 @@ export async function assertNoRestrictedChildrenForParentMutation(db, parentTabl
317
468
  continue;
318
469
  // UPDATE: only enforce when at least one referenced parent column changed.
319
470
  if (operation === 'update' && newRow !== undefined) {
320
- let anyChanged = false;
321
- for (const idx of parentColIndices) {
322
- if (!sqlValueIdentical(oldRow[idx], newRow[idx])) {
323
- anyChanged = true;
324
- break;
325
- }
326
- }
327
- if (!anyChanged)
471
+ if (!anyReferencedColumnChanged(parentColIndices, oldRow, newRow))
328
472
  continue;
329
473
  }
330
474
  // MATCH SIMPLE: NULL parent values cannot be referenced.
@@ -338,21 +482,11 @@ export async function assertNoRestrictedChildrenForParentMutation(db, parentTabl
338
482
  : '';
339
483
  const sql = `select 1 from ${schemaPrefix}${quoteIdentifier(childTable.name)} where ${whereClause} limit 1`;
340
484
  log('RESTRICT check (%s): %s with params %o', operation, sql, oldParentValues);
341
- const stmt = db.prepare(sql);
342
- try {
343
- stmt.bindAll(oldParentValues);
344
- let referenced = false;
345
- for await (const _row of stmt._iterateRowsRaw()) {
346
- referenced = true;
347
- break;
348
- }
349
- if (referenced) {
350
- const opName = operation === 'delete' ? 'DELETE' : 'UPDATE';
351
- throw new QuereusError(`FOREIGN KEY constraint failed: ${opName} on '${parentTable.name}' violates RESTRICT from '${childTable.name}'`, StatusCode.CONSTRAINT);
352
- }
353
- }
354
- finally {
355
- await stmt.finalize();
485
+ // Cached internal statement (compiled once per shape) rather than a fresh
486
+ // per-row prepare/finalize — see InternalStatementCache.
487
+ if (await db._internalStatementCache.probe(sql, oldParentValues)) {
488
+ const opName = operation === 'delete' ? 'DELETE' : 'UPDATE';
489
+ throw new QuereusError(`FOREIGN KEY constraint failed: ${opName} on '${parentTable.name}' violates RESTRICT from '${childTable.name}'`, StatusCode.CONSTRAINT);
356
490
  }
357
491
  }
358
492
  }
@@ -390,11 +524,16 @@ async function executeSingleFKAction(db, childTable, fk, action, parentTable, pa
390
524
  const qualifiedChildTable = `"${childTable.schemaName}"."${childTable.name}"`;
391
525
  switch (action) {
392
526
  case 'cascade': {
527
+ // NOTE: this branches purely on `newRow === undefined` to tell cascade DELETE
528
+ // from cascade UPDATE. Every current caller passes operation === 'update' with
529
+ // a defined `newRow` (see the three UPDATE sites in emit/dml-executor.ts) — if a
530
+ // future caller ever invoked executeForeignKeyActions with operation: 'update'
531
+ // and no newRow, this would silently issue a child DELETE instead.
393
532
  if (newRow === undefined) {
394
533
  // CASCADE DELETE: delete matching child rows
395
534
  const sql = `DELETE FROM ${qualifiedChildTable} WHERE ${whereClause}`;
396
535
  log('CASCADE DELETE: %s with params %o', sql, oldParentValues);
397
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
536
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
398
537
  }
399
538
  else {
400
539
  // CASCADE UPDATE: update child FK columns to new parent values
@@ -408,7 +547,7 @@ async function executeSingleFKAction(db, childTable, fk, action, parentTable, pa
408
547
  const sql = `UPDATE ${qualifiedChildTable} SET ${setClauses} WHERE ${whereParamsClause}`;
409
548
  const params = [...newParentValues, ...oldParentValues];
410
549
  log('CASCADE UPDATE: %s with params %o', sql, params);
411
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, params));
550
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, params));
412
551
  }
413
552
  break;
414
553
  }
@@ -416,7 +555,7 @@ async function executeSingleFKAction(db, childTable, fk, action, parentTable, pa
416
555
  const setClauses = childColNames.map(name => `"${name}" = NULL`).join(', ');
417
556
  const sql = `UPDATE ${qualifiedChildTable} SET ${setClauses} WHERE ${whereClause}`;
418
557
  log('SET NULL: %s with params %o', sql, oldParentValues);
419
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
558
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
420
559
  break;
421
560
  }
422
561
  case 'setDefault': {
@@ -431,7 +570,7 @@ async function executeSingleFKAction(db, childTable, fk, action, parentTable, pa
431
570
  }).join(', ');
432
571
  const sql = `UPDATE ${qualifiedChildTable} SET ${setClauses} WHERE ${whereClause}`;
433
572
  log('SET DEFAULT: %s with params %o', sql, oldParentValues);
434
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
573
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
435
574
  break;
436
575
  }
437
576
  }
@@ -503,7 +642,7 @@ export async function executeLensForeignKeyActions(db, basisParentTable, operati
503
642
  * (⇒ skip this ref) when:
504
643
  * - a referenced column has no plain basis projection (cannot read its basis value);
505
644
  * - MATCH SIMPLE: any OLD referenced value is NULL (participates in no FK match); or
506
- * - UPDATE: no referenced parent column actually changed (`sqlValueIdentical` short-circuit).
645
+ * - UPDATE: no referenced parent column actually changed ({@link anyReferencedColumnChanged}).
507
646
  */
508
647
  function resolveLensFkParentReferencedValues(ref, parentMap, basisParentTable, operation, oldRow, newRow) {
509
648
  // Logical referenced column → basis column → basis index. A column with no plain basis
@@ -525,14 +664,7 @@ function resolveLensFkParentReferencedValues(ref, parentMap, basisParentTable, o
525
664
  // UPDATE short-circuit: skip when no referenced parent column actually changed.
526
665
  let newParentValues;
527
666
  if (operation === 'update' && newRow !== undefined) {
528
- let anyChanged = false;
529
- for (const i of basisIndices) {
530
- if (!sqlValueIdentical(oldRow[i], newRow[i])) {
531
- anyChanged = true;
532
- break;
533
- }
534
- }
535
- if (!anyChanged)
667
+ if (!anyReferencedColumnChanged(basisIndices, oldRow, newRow))
536
668
  return undefined;
537
669
  newParentValues = basisIndices.map(i => newRow[i]);
538
670
  }
@@ -607,7 +739,7 @@ async function issueLensFkAction(db, ref, action, operation, oldParentValues, ne
607
739
  if (operation === 'delete') {
608
740
  const sql = `delete from ${qualifiedChild} where ${whereClause}`;
609
741
  log('LENS CASCADE DELETE: %s with params %o', sql, oldParentValues);
610
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
742
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
611
743
  }
612
744
  else {
613
745
  // CASCADE UPDATE: rewrite the child FK columns to the NEW parent values
@@ -616,7 +748,7 @@ async function issueLensFkAction(db, ref, action, operation, oldParentValues, ne
616
748
  const sql = `update ${qualifiedChild} set ${setClauses} where ${whereClause}`;
617
749
  const params = [...(newParentValues ?? []), ...oldParentValues];
618
750
  log('LENS CASCADE UPDATE: %s with params %o', sql, params);
619
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, params));
751
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, params));
620
752
  }
621
753
  break;
622
754
  }
@@ -624,7 +756,7 @@ async function issueLensFkAction(db, ref, action, operation, oldParentValues, ne
624
756
  const setClauses = childLogicalColumns.map(c => `${quoteIdentifier(c)} = null`).join(', ');
625
757
  const sql = `update ${qualifiedChild} set ${setClauses} where ${whereClause}`;
626
758
  log('LENS SET NULL: %s with params %o', sql, oldParentValues);
627
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
759
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
628
760
  break;
629
761
  }
630
762
  case 'setDefault': {
@@ -638,7 +770,7 @@ async function issueLensFkAction(db, ref, action, operation, oldParentValues, ne
638
770
  }).join(', ');
639
771
  const sql = `update ${qualifiedChild} set ${setClauses} where ${whereClause}`;
640
772
  log('LENS SET DEFAULT: %s with params %o', sql, oldParentValues);
641
- await withFkCascadeReentry(db, () => db._execWithinTransaction(sql, oldParentValues));
773
+ await withFkCascadeReentry(db, () => db._internalStatementCache.run(sql, oldParentValues));
642
774
  break;
643
775
  }
644
776
  }
@@ -745,21 +877,11 @@ async function assertNoLensChildReferences(db, ref, parentSlot, operation, oldPa
745
877
  const whereClause = ref.childLogicalColumns.map(c => `${quoteIdentifier(c)} = ?`).join(' and ');
746
878
  const sql = `select 1 from ${qualifiedChild} where ${whereClause} limit 1`;
747
879
  log('LENS RESTRICT check (%s): %s with params %o', operation, sql, oldParentValues);
748
- const stmt = db.prepare(sql);
749
- try {
750
- stmt.bindAll(oldParentValues);
751
- let referenced = false;
752
- for await (const _row of stmt._iterateRowsRaw()) {
753
- referenced = true;
754
- break;
755
- }
756
- if (referenced) {
757
- const opName = operation === 'delete' ? 'DELETE' : 'UPDATE';
758
- throw new QuereusError(`FOREIGN KEY constraint failed: ${opName} on '${parentSlot.logicalTable.name}' violates RESTRICT from '${childTable.name}'`, StatusCode.CONSTRAINT);
759
- }
760
- }
761
- finally {
762
- await stmt.finalize();
880
+ // Cached internal statement (compiled once per shape) rather than a fresh
881
+ // per-row prepare/finalize — see InternalStatementCache.
882
+ if (await db._internalStatementCache.probe(sql, oldParentValues)) {
883
+ const opName = operation === 'delete' ? 'DELETE' : 'UPDATE';
884
+ throw new QuereusError(`FOREIGN KEY constraint failed: ${opName} on '${parentSlot.logicalTable.name}' violates RESTRICT from '${childTable.name}'`, StatusCode.CONSTRAINT);
763
885
  }
764
886
  }
765
887
  //# sourceMappingURL=foreign-key-actions.js.map