@quereus/quereus 4.3.2 → 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.
- package/README.md +5 -0
- package/dist/src/common/types.d.ts +9 -0
- package/dist/src/common/types.d.ts.map +1 -1
- package/dist/src/common/types.js.map +1 -1
- package/dist/src/core/database-events.d.ts +49 -0
- package/dist/src/core/database-events.d.ts.map +1 -1
- package/dist/src/core/database-events.js +148 -0
- package/dist/src/core/database-events.js.map +1 -1
- package/dist/src/core/database-external-changes.d.ts +2 -2
- package/dist/src/core/database-external-changes.d.ts.map +1 -1
- package/dist/src/core/database-external-changes.js +12 -9
- package/dist/src/core/database-external-changes.js.map +1 -1
- package/dist/src/core/database-materialized-views-apply.d.ts +156 -14
- package/dist/src/core/database-materialized-views-apply.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-apply.js +370 -65
- package/dist/src/core/database-materialized-views-apply.js.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.d.ts +66 -1
- package/dist/src/core/database-materialized-views-plan-builders.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views-plan-builders.js +368 -8
- package/dist/src/core/database-materialized-views-plan-builders.js.map +1 -1
- package/dist/src/core/database-materialized-views-plans.d.ts +257 -30
- package/dist/src/core/database-materialized-views-plans.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views.d.ts +99 -42
- package/dist/src/core/database-materialized-views.d.ts.map +1 -1
- package/dist/src/core/database-materialized-views.js +198 -121
- package/dist/src/core/database-materialized-views.js.map +1 -1
- package/dist/src/core/database.d.ts +5 -0
- package/dist/src/core/database.d.ts.map +1 -1
- package/dist/src/core/database.js +41 -18
- package/dist/src/core/database.js.map +1 -1
- package/dist/src/core/internal-statement-cache.d.ts +18 -0
- package/dist/src/core/internal-statement-cache.d.ts.map +1 -0
- package/dist/src/core/internal-statement-cache.js +211 -0
- package/dist/src/core/internal-statement-cache.js.map +1 -0
- package/dist/src/func/builtins/aggregate.d.ts +13 -12
- package/dist/src/func/builtins/aggregate.d.ts.map +1 -1
- package/dist/src/func/builtins/aggregate.js +146 -62
- package/dist/src/func/builtins/aggregate.js.map +1 -1
- package/dist/src/func/builtins/builtin-window-functions.d.ts.map +1 -1
- package/dist/src/func/builtins/builtin-window-functions.js +63 -48
- package/dist/src/func/builtins/builtin-window-functions.js.map +1 -1
- package/dist/src/func/registration.d.ts +19 -1
- package/dist/src/func/registration.d.ts.map +1 -1
- package/dist/src/func/registration.js +41 -0
- package/dist/src/func/registration.js.map +1 -1
- package/dist/src/index.d.ts +8 -8
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +12 -11
- package/dist/src/index.js.map +1 -1
- package/dist/src/parser/parser.d.ts +6 -0
- package/dist/src/parser/parser.d.ts.map +1 -1
- package/dist/src/parser/parser.js +45 -34
- package/dist/src/parser/parser.js.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.d.ts +2 -0
- package/dist/src/planner/analysis/comparison-collation.d.ts.map +1 -1
- package/dist/src/planner/analysis/comparison-collation.js +1 -1
- package/dist/src/planner/analysis/comparison-collation.js.map +1 -1
- package/dist/src/planner/analysis/equi-correlation.d.ts +26 -0
- package/dist/src/planner/analysis/equi-correlation.d.ts.map +1 -0
- package/dist/src/planner/analysis/equi-correlation.js +62 -0
- package/dist/src/planner/analysis/equi-correlation.js.map +1 -0
- package/dist/src/planner/analysis/expression-fingerprint.d.ts.map +1 -1
- package/dist/src/planner/analysis/expression-fingerprint.js +29 -0
- package/dist/src/planner/analysis/expression-fingerprint.js.map +1 -1
- package/dist/src/planner/analysis/query-rewrite-matcher.d.ts +60 -18
- package/dist/src/planner/analysis/query-rewrite-matcher.d.ts.map +1 -1
- package/dist/src/planner/analysis/query-rewrite-matcher.js +95 -42
- package/dist/src/planner/analysis/query-rewrite-matcher.js.map +1 -1
- package/dist/src/planner/analysis/sat-checker.d.ts.map +1 -1
- package/dist/src/planner/analysis/sat-checker.js +43 -4
- package/dist/src/planner/analysis/sat-checker.js.map +1 -1
- package/dist/src/planner/analysis/scalar-subqueries.d.ts +28 -0
- package/dist/src/planner/analysis/scalar-subqueries.d.ts.map +1 -0
- package/dist/src/planner/analysis/scalar-subqueries.js +60 -0
- package/dist/src/planner/analysis/scalar-subqueries.js.map +1 -0
- package/dist/src/planner/building/delete.d.ts.map +1 -1
- package/dist/src/planner/building/delete.js +10 -3
- package/dist/src/planner/building/delete.js.map +1 -1
- package/dist/src/planner/building/expression.d.ts.map +1 -1
- package/dist/src/planner/building/expression.js +87 -6
- package/dist/src/planner/building/expression.js.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.d.ts +44 -1
- package/dist/src/planner/building/foreign-key-builder.d.ts.map +1 -1
- package/dist/src/planner/building/foreign-key-builder.js +54 -0
- package/dist/src/planner/building/foreign-key-builder.js.map +1 -1
- package/dist/src/planner/building/insert.d.ts.map +1 -1
- package/dist/src/planner/building/insert.js +9 -12
- package/dist/src/planner/building/insert.js.map +1 -1
- package/dist/src/planner/building/select.d.ts.map +1 -1
- package/dist/src/planner/building/select.js +15 -1
- package/dist/src/planner/building/select.js.map +1 -1
- package/dist/src/planner/building/update.d.ts.map +1 -1
- package/dist/src/planner/building/update.js +13 -4
- package/dist/src/planner/building/update.js.map +1 -1
- package/dist/src/planner/building/with.d.ts.map +1 -1
- package/dist/src/planner/building/with.js +5 -8
- package/dist/src/planner/building/with.js.map +1 -1
- package/dist/src/planner/cache/materialization-advisory.d.ts +33 -0
- package/dist/src/planner/cache/materialization-advisory.d.ts.map +1 -1
- package/dist/src/planner/cache/materialization-advisory.js +164 -28
- package/dist/src/planner/cache/materialization-advisory.js.map +1 -1
- package/dist/src/planner/cache/reference-graph.d.ts +0 -4
- package/dist/src/planner/cache/reference-graph.d.ts.map +1 -1
- package/dist/src/planner/cache/reference-graph.js +5 -13
- package/dist/src/planner/cache/reference-graph.js.map +1 -1
- package/dist/src/planner/cost/index.d.ts +28 -7
- package/dist/src/planner/cost/index.d.ts.map +1 -1
- package/dist/src/planner/cost/index.js +26 -0
- package/dist/src/planner/cost/index.js.map +1 -1
- package/dist/src/planner/framework/pass.js +3 -3
- package/dist/src/planner/nodes/cache-node.d.ts +18 -2
- package/dist/src/planner/nodes/cache-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/cache-node.js +42 -3
- package/dist/src/planner/nodes/cache-node.js.map +1 -1
- package/dist/src/planner/nodes/cte-node.d.ts +15 -1
- package/dist/src/planner/nodes/cte-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/cte-node.js +12 -2
- package/dist/src/planner/nodes/cte-node.js.map +1 -1
- package/dist/src/planner/nodes/dml-executor-node.d.ts +0 -9
- package/dist/src/planner/nodes/dml-executor-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/dml-executor-node.js.map +1 -1
- package/dist/src/planner/nodes/recursive-cte-node.d.ts +30 -1
- package/dist/src/planner/nodes/recursive-cte-node.d.ts.map +1 -1
- package/dist/src/planner/nodes/recursive-cte-node.js +30 -3
- package/dist/src/planner/nodes/recursive-cte-node.js.map +1 -1
- package/dist/src/planner/nodes/scalar.d.ts.map +1 -1
- package/dist/src/planner/nodes/scalar.js +26 -3
- package/dist/src/planner/nodes/scalar.js.map +1 -1
- package/dist/src/planner/optimizer.d.ts.map +1 -1
- package/dist/src/planner/optimizer.js +129 -8
- package/dist/src/planner/optimizer.js.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.d.ts.map +1 -1
- package/dist/src/planner/rules/access/rule-select-access-path.js +43 -18
- package/dist/src/planner/rules/access/rule-select-access-path.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-cte-optimization.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-cte-optimization.js +8 -1
- package/dist/src/planner/rules/cache/rule-cte-optimization.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js +72 -54
- package/dist/src/planner/rules/cache/rule-materialized-view-rewrite.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.d.ts.map +1 -1
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js +6 -0
- package/dist/src/planner/rules/cache/rule-mutating-subquery-cache.js.map +1 -1
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts +25 -0
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js +176 -0
- package/dist/src/planner/rules/cache/rule-nested-loop-right-cache.js.map +1 -0
- package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts +31 -0
- package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.d.ts.map +1 -0
- package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js +68 -0
- package/dist/src/planner/rules/cache/rule-scalar-subquery-cache.js.map +1 -0
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.d.ts.map +1 -1
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js +1 -54
- package/dist/src/planner/rules/join/rule-fanout-lookup-join.js.map +1 -1
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js +8 -1
- package/dist/src/planner/rules/predicate/rule-predicate-inference-equivalence.js.map +1 -1
- package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.d.ts +191 -0
- package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.d.ts.map +1 -0
- package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js +627 -0
- package/dist/src/planner/rules/subquery/rule-scalar-agg-decorrelation.js.map +1 -0
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts +10 -1
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.d.ts.map +1 -1
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js +242 -51
- package/dist/src/planner/rules/subquery/rule-subquery-decorrelation.js.map +1 -1
- package/dist/src/runtime/cache/shared-cache.d.ts +7 -0
- package/dist/src/runtime/cache/shared-cache.d.ts.map +1 -1
- package/dist/src/runtime/cache/shared-cache.js +34 -1
- package/dist/src/runtime/cache/shared-cache.js.map +1 -1
- package/dist/src/runtime/deferred-constraint-queue.d.ts.map +1 -1
- package/dist/src/runtime/deferred-constraint-queue.js +10 -0
- package/dist/src/runtime/deferred-constraint-queue.js.map +1 -1
- package/dist/src/runtime/emit/aggregate-setup.d.ts +49 -0
- package/dist/src/runtime/emit/aggregate-setup.d.ts.map +1 -0
- package/dist/src/runtime/emit/aggregate-setup.js +99 -0
- package/dist/src/runtime/emit/aggregate-setup.js.map +1 -0
- package/dist/src/runtime/emit/aggregate.d.ts +2 -3
- package/dist/src/runtime/emit/aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/aggregate.js +10 -84
- package/dist/src/runtime/emit/aggregate.js.map +1 -1
- package/dist/src/runtime/emit/alter-table.d.ts.map +1 -1
- package/dist/src/runtime/emit/alter-table.js +348 -165
- package/dist/src/runtime/emit/alter-table.js.map +1 -1
- package/dist/src/runtime/emit/asof-scan.d.ts.map +1 -1
- package/dist/src/runtime/emit/asof-scan.js +6 -0
- package/dist/src/runtime/emit/asof-scan.js.map +1 -1
- package/dist/src/runtime/emit/between.d.ts.map +1 -1
- package/dist/src/runtime/emit/between.js +32 -3
- package/dist/src/runtime/emit/between.js.map +1 -1
- package/dist/src/runtime/emit/binary.d.ts.map +1 -1
- package/dist/src/runtime/emit/binary.js +22 -2
- package/dist/src/runtime/emit/binary.js.map +1 -1
- package/dist/src/runtime/emit/bloom-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/bloom-join.js +24 -3
- package/dist/src/runtime/emit/bloom-join.js.map +1 -1
- package/dist/src/runtime/emit/cache.d.ts +7 -3
- package/dist/src/runtime/emit/cache.d.ts.map +1 -1
- package/dist/src/runtime/emit/cache.js +23 -6
- package/dist/src/runtime/emit/cache.js.map +1 -1
- package/dist/src/runtime/emit/cast.d.ts.map +1 -1
- package/dist/src/runtime/emit/cast.js +10 -23
- package/dist/src/runtime/emit/cast.js.map +1 -1
- package/dist/src/runtime/emit/constraint-check.d.ts.map +1 -1
- package/dist/src/runtime/emit/constraint-check.js +41 -44
- package/dist/src/runtime/emit/constraint-check.js.map +1 -1
- package/dist/src/runtime/emit/create-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/create-view.js +6 -0
- package/dist/src/runtime/emit/create-view.js.map +1 -1
- package/dist/src/runtime/emit/cte.d.ts.map +1 -1
- package/dist/src/runtime/emit/cte.js +43 -20
- package/dist/src/runtime/emit/cte.js.map +1 -1
- package/dist/src/runtime/emit/distinct.d.ts.map +1 -1
- package/dist/src/runtime/emit/distinct.js +6 -3
- package/dist/src/runtime/emit/distinct.js.map +1 -1
- package/dist/src/runtime/emit/dml-executor.d.ts.map +1 -1
- package/dist/src/runtime/emit/dml-executor.js +241 -115
- package/dist/src/runtime/emit/dml-executor.js.map +1 -1
- package/dist/src/runtime/emit/hash-aggregate.d.ts.map +1 -1
- package/dist/src/runtime/emit/hash-aggregate.js +16 -65
- package/dist/src/runtime/emit/hash-aggregate.js.map +1 -1
- package/dist/src/runtime/emit/insert.d.ts.map +1 -1
- package/dist/src/runtime/emit/insert.js +18 -7
- package/dist/src/runtime/emit/insert.js.map +1 -1
- package/dist/src/runtime/emit/join.js +5 -0
- package/dist/src/runtime/emit/join.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts +14 -1
- package/dist/src/runtime/emit/materialized-view-helpers.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view-helpers.js +38 -7
- package/dist/src/runtime/emit/materialized-view-helpers.js.map +1 -1
- package/dist/src/runtime/emit/materialized-view.d.ts.map +1 -1
- package/dist/src/runtime/emit/materialized-view.js +47 -6
- package/dist/src/runtime/emit/materialized-view.js.map +1 -1
- package/dist/src/runtime/emit/merge-join.d.ts.map +1 -1
- package/dist/src/runtime/emit/merge-join.js +18 -8
- package/dist/src/runtime/emit/merge-join.js.map +1 -1
- package/dist/src/runtime/emit/recursive-cte.d.ts.map +1 -1
- package/dist/src/runtime/emit/recursive-cte.js +59 -1
- package/dist/src/runtime/emit/recursive-cte.js.map +1 -1
- package/dist/src/runtime/emit/set-object-tags.d.ts +8 -0
- package/dist/src/runtime/emit/set-object-tags.d.ts.map +1 -1
- package/dist/src/runtime/emit/set-object-tags.js +8 -0
- package/dist/src/runtime/emit/set-object-tags.js.map +1 -1
- package/dist/src/runtime/emit/set-operation.d.ts.map +1 -1
- package/dist/src/runtime/emit/set-operation.js +7 -4
- package/dist/src/runtime/emit/set-operation.js.map +1 -1
- package/dist/src/runtime/emit/sort.d.ts.map +1 -1
- package/dist/src/runtime/emit/sort.js +5 -2
- package/dist/src/runtime/emit/sort.js.map +1 -1
- package/dist/src/runtime/emit/subquery.d.ts.map +1 -1
- package/dist/src/runtime/emit/subquery.js +113 -13
- package/dist/src/runtime/emit/subquery.js.map +1 -1
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts +7 -0
- package/dist/src/runtime/emit/temporal-arithmetic.d.ts.map +1 -1
- package/dist/src/runtime/emit/temporal-arithmetic.js +15 -7
- package/dist/src/runtime/emit/temporal-arithmetic.js.map +1 -1
- package/dist/src/runtime/emit/update.d.ts.map +1 -1
- package/dist/src/runtime/emit/update.js +44 -3
- package/dist/src/runtime/emit/update.js.map +1 -1
- package/dist/src/runtime/emit/window.d.ts.map +1 -1
- package/dist/src/runtime/emit/window.js +72 -55
- package/dist/src/runtime/emit/window.js.map +1 -1
- package/dist/src/runtime/foreign-key-actions.d.ts +36 -1
- package/dist/src/runtime/foreign-key-actions.d.ts.map +1 -1
- package/dist/src/runtime/foreign-key-actions.js +238 -116
- package/dist/src/runtime/foreign-key-actions.js.map +1 -1
- package/dist/src/runtime/parallel-driver.d.ts +20 -1
- package/dist/src/runtime/parallel-driver.d.ts.map +1 -1
- package/dist/src/runtime/parallel-driver.js +23 -1
- package/dist/src/runtime/parallel-driver.js.map +1 -1
- package/dist/src/runtime/types.d.ts +54 -0
- package/dist/src/runtime/types.d.ts.map +1 -1
- package/dist/src/runtime/types.js.map +1 -1
- package/dist/src/schema/catalog-persistability.d.ts +58 -0
- package/dist/src/schema/catalog-persistability.d.ts.map +1 -0
- package/dist/src/schema/catalog-persistability.js +87 -0
- package/dist/src/schema/catalog-persistability.js.map +1 -0
- package/dist/src/schema/catalog.d.ts +17 -1
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +25 -3
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/constraint-builder.d.ts +20 -22
- package/dist/src/schema/constraint-builder.d.ts.map +1 -1
- package/dist/src/schema/constraint-builder.js +43 -36
- package/dist/src/schema/constraint-builder.js.map +1 -1
- package/dist/src/schema/ddl-generator.d.ts +23 -0
- package/dist/src/schema/ddl-generator.d.ts.map +1 -1
- package/dist/src/schema/ddl-generator.js +27 -0
- package/dist/src/schema/ddl-generator.js.map +1 -1
- package/dist/src/schema/function.d.ts +109 -1
- package/dist/src/schema/function.d.ts.map +1 -1
- package/dist/src/schema/function.js.map +1 -1
- package/dist/src/schema/manager.d.ts +28 -0
- package/dist/src/schema/manager.d.ts.map +1 -1
- package/dist/src/schema/manager.js +82 -14
- package/dist/src/schema/manager.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +51 -12
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +31 -0
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +37 -1
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/schema/window-function.d.ts +30 -10
- package/dist/src/schema/window-function.d.ts.map +1 -1
- package/dist/src/schema/window-function.js +20 -16
- package/dist/src/schema/window-function.js.map +1 -1
- package/dist/src/types/cast-semantics.d.ts +43 -0
- package/dist/src/types/cast-semantics.d.ts.map +1 -0
- package/dist/src/types/cast-semantics.js +59 -0
- package/dist/src/types/cast-semantics.js.map +1 -0
- package/dist/src/types/index.d.ts +1 -1
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/index.js +1 -1
- package/dist/src/types/index.js.map +1 -1
- package/dist/src/types/json-type.d.ts.map +1 -1
- package/dist/src/types/json-type.js +31 -10
- package/dist/src/types/json-type.js.map +1 -1
- package/dist/src/types/logical-type.d.ts +22 -0
- package/dist/src/types/logical-type.d.ts.map +1 -1
- package/dist/src/types/logical-type.js.map +1 -1
- package/dist/src/types/temporal-types.d.ts.map +1 -1
- package/dist/src/types/temporal-types.js +33 -11
- package/dist/src/types/temporal-types.js.map +1 -1
- package/dist/src/types/validation.d.ts +39 -1
- package/dist/src/types/validation.d.ts.map +1 -1
- package/dist/src/types/validation.js +60 -0
- package/dist/src/types/validation.js.map +1 -1
- package/dist/src/util/ast-spine-clone.d.ts +23 -0
- package/dist/src/util/ast-spine-clone.d.ts.map +1 -0
- package/dist/src/util/ast-spine-clone.js +41 -0
- package/dist/src/util/ast-spine-clone.js.map +1 -0
- package/dist/src/util/coercion.d.ts +13 -0
- package/dist/src/util/coercion.d.ts.map +1 -1
- package/dist/src/util/coercion.js +10 -1
- package/dist/src/util/coercion.js.map +1 -1
- package/dist/src/util/comparison.d.ts +89 -0
- package/dist/src/util/comparison.d.ts.map +1 -1
- package/dist/src/util/comparison.js +156 -27
- package/dist/src/util/comparison.js.map +1 -1
- package/dist/src/vtab/manifest.d.ts +7 -0
- package/dist/src/vtab/manifest.d.ts.map +1 -1
- package/dist/src/vtab/memory/connection.d.ts +14 -1
- package/dist/src/vtab/memory/connection.d.ts.map +1 -1
- package/dist/src/vtab/memory/connection.js +20 -2
- package/dist/src/vtab/memory/connection.js.map +1 -1
- package/dist/src/vtab/memory/layer/base.d.ts +6 -1
- package/dist/src/vtab/memory/layer/base.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/base.js +14 -5
- package/dist/src/vtab/memory/layer/base.js.map +1 -1
- package/dist/src/vtab/memory/layer/connection.d.ts +17 -0
- package/dist/src/vtab/memory/layer/connection.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/connection.js +26 -0
- package/dist/src/vtab/memory/layer/connection.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +96 -13
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +595 -119
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/dist/src/vtab/memory/layer/plan-filter.d.ts +41 -20
- package/dist/src/vtab/memory/layer/plan-filter.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/plan-filter.js +57 -27
- package/dist/src/vtab/memory/layer/plan-filter.js.map +1 -1
- package/dist/src/vtab/memory/layer/row-convert.d.ts +21 -0
- package/dist/src/vtab/memory/layer/row-convert.d.ts.map +1 -0
- package/dist/src/vtab/memory/layer/row-convert.js +30 -0
- package/dist/src/vtab/memory/layer/row-convert.js.map +1 -0
- package/dist/src/vtab/memory/layer/scan-layer.d.ts +6 -3
- package/dist/src/vtab/memory/layer/scan-layer.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/scan-layer.js +69 -36
- package/dist/src/vtab/memory/layer/scan-layer.js.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.d.ts +127 -13
- package/dist/src/vtab/memory/layer/transaction.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/transaction.js +214 -65
- package/dist/src/vtab/memory/layer/transaction.js.map +1 -1
- package/dist/src/vtab/memory/module.d.ts.map +1 -1
- package/dist/src/vtab/memory/module.js +9 -2
- package/dist/src/vtab/memory/module.js.map +1 -1
- package/dist/src/vtab/memory/table.d.ts.map +1 -1
- package/dist/src/vtab/memory/table.js +3 -2
- package/dist/src/vtab/memory/table.js.map +1 -1
- package/dist/src/vtab/memory/types.d.ts +27 -1
- package/dist/src/vtab/memory/types.d.ts.map +1 -1
- package/dist/src/vtab/memory/types.js +16 -1
- package/dist/src/vtab/memory/types.js.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key.d.ts +9 -0
- package/dist/src/vtab/memory/utils/primary-key.d.ts.map +1 -1
- package/dist/src/vtab/memory/utils/primary-key.js +11 -0
- package/dist/src/vtab/memory/utils/primary-key.js.map +1 -1
- package/dist/src/vtab/module.d.ts +63 -0
- package/dist/src/vtab/module.d.ts.map +1 -1
- package/dist/src/vtab/table.d.ts +24 -6
- package/dist/src/vtab/table.d.ts.map +1 -1
- package/dist/src/vtab/table.js.map +1 -1
- package/package.json +3 -3
- package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts +0 -19
- package/dist/src/planner/rules/cache/rule-in-subquery-cache.d.ts.map +0 -1
- package/dist/src/planner/rules/cache/rule-in-subquery-cache.js +0 -53
- package/dist/src/planner/rules/cache/rule-in-subquery-cache.js.map +0 -1
|
@@ -11,7 +11,9 @@ import { hasNativeEventSupport } from '../../util/event-support.js';
|
|
|
11
11
|
import { sqlValueIdentical, compareSqlValuesFast, BINARY_COLLATION } from '../../util/comparison.js';
|
|
12
12
|
import { validateAndParse } from '../../types/validation.js';
|
|
13
13
|
import { withAsyncRowContext } from '../context-helpers.js';
|
|
14
|
-
import { executeForeignKeyActionsAndLens, assertTransitiveRestrictsForParentMutation } from '../foreign-key-actions.js';
|
|
14
|
+
import { executeForeignKeyActionsAndLens, assertTransitiveRestrictsForParentMutation, createParentRestrictBatch, accumulateParentRestrictKeys, flushParentRestrictBatch } from '../foreign-key-actions.js';
|
|
15
|
+
import { getBatchableRestrictFks } from '../../planner/building/foreign-key-builder.js';
|
|
16
|
+
import { poisonResidualDeltaAccumulations } from '../../core/database-materialized-views-apply.js';
|
|
15
17
|
/**
|
|
16
18
|
* Module-scope counter producing unique statement-savepoint names across
|
|
17
19
|
* concurrent emissions. Names must be unique within the TransactionManager's
|
|
@@ -21,36 +23,19 @@ import { executeForeignKeyActionsAndLens, assertTransitiveRestrictsForParentMuta
|
|
|
21
23
|
let stmtSavepointCounter = 0;
|
|
22
24
|
/**
|
|
23
25
|
* True when a proposed value equals the stored existing value at one conflict-target
|
|
24
|
-
* column the way the targeted constraint ENFORCES: the column's
|
|
25
|
-
*
|
|
26
|
-
* `existing` is
|
|
27
|
-
*
|
|
26
|
+
* column the way the targeted constraint ENFORCES: compared under the column's
|
|
27
|
+
* precomputed enforcement collation. Both sides are already in declared (stored)
|
|
28
|
+
* form — `existing` is a stored row and the proposed row was converted by the
|
|
29
|
+
* INSERT emitter's static-type pass — so no per-row conversion happens here.
|
|
28
30
|
*
|
|
29
|
-
* `targetPos` indexes the clause's per-target
|
|
30
|
-
* `conflictTargetIndices`). When
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* {@link compareSqlValuesFast}. A coercion failure likewise falls back to the raw
|
|
34
|
-
* proposed value (it then compares unequal and the caller aborts, as before).
|
|
31
|
+
* `targetPos` indexes the clause's per-target collation array (aligned with
|
|
32
|
+
* `conflictTargetIndices`). When absent — a defensively-old plan — it degrades to
|
|
33
|
+
* BINARY, i.e. the byte-identity semantics of `sqlValueIdentical`, preserving its
|
|
34
|
+
* numeric-storage-class tolerance via {@link compareSqlValuesFast}.
|
|
35
35
|
*/
|
|
36
36
|
function conflictTargetValuesMatch(existing, proposed, clause, targetPos) {
|
|
37
|
-
const type = clause.conflictTargetTypes?.[targetPos];
|
|
38
37
|
const collationFn = clause.conflictTargetCollationFns?.[targetPos] ?? BINARY_COLLATION;
|
|
39
|
-
|
|
40
|
-
// reached on the (cold) UNIQUE-violation-with-upsert-clause path, so it is off the
|
|
41
|
-
// happy-path insert. If a workload ever hammers ON CONFLICT on a wide composite target,
|
|
42
|
-
// precompute per-target coercion closures at emit instead of dispatching on type here.
|
|
43
|
-
let coercedProposed = proposed;
|
|
44
|
-
if (type) {
|
|
45
|
-
try {
|
|
46
|
-
coercedProposed = validateAndParse(proposed, type);
|
|
47
|
-
}
|
|
48
|
-
catch {
|
|
49
|
-
// Coercion failed — compare the raw proposed value (aborts, as pre-fix).
|
|
50
|
-
coercedProposed = proposed;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return compareSqlValuesFast(existing, coercedProposed, collationFn) === 0;
|
|
38
|
+
return compareSqlValuesFast(existing, proposed, collationFn) === 0;
|
|
54
39
|
}
|
|
55
40
|
/**
|
|
56
41
|
* Returns true when the table's owning *module* natively emits data events.
|
|
@@ -119,6 +104,44 @@ async function resolveDmlSourceRows(ctx, vtab, tableSchema, rows) {
|
|
|
119
104
|
throw e;
|
|
120
105
|
}
|
|
121
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* The row the substrate actually STORED for this write, which is what every
|
|
109
|
+
* post-write consumer must see. The executor's own writes are converted by the
|
|
110
|
+
* emitters up front (and passed with `preCoerced`), so for the standard
|
|
111
|
+
* backends `result.row` and the row we handed in now usually agree — but a
|
|
112
|
+
* module remains free to normalize further (or to ignore `preCoerced` and
|
|
113
|
+
* convert on its own), and `result.row` is its word on what landed.
|
|
114
|
+
*
|
|
115
|
+
* Falls back to the raw row when the substrate's row is the wrong width — a
|
|
116
|
+
* minimal test/sample module that echoes its input in a different shape. The
|
|
117
|
+
* absent case is only reached defensively: every caller has already
|
|
118
|
+
* short-circuited on a missing `result.row`, which the module contract
|
|
119
|
+
* reserves for "nothing was written" (see `VirtualTable.update`).
|
|
120
|
+
*/
|
|
121
|
+
function storedRowOrRaw(resultRow, rawRow, columnCount) {
|
|
122
|
+
return resultRow && resultRow.length === columnCount ? resultRow : rawRow;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Rebuild the flat OLD/NEW row with its NEW section (indices n..2n-1) replaced by
|
|
126
|
+
* the substrate's stored row, so `RETURNING new.<col>` reports the same value and
|
|
127
|
+
* `typeof` as a subsequent `select`. Returns `flatRow` untouched when the stored
|
|
128
|
+
* row IS the raw NEW section (the fallback above), avoiding a per-row copy.
|
|
129
|
+
*
|
|
130
|
+
* NOTE: on a coercing substrate this allocates one array per written row (the
|
|
131
|
+
* DML executor previously yielded `flatRow` verbatim). It is one copy against a
|
|
132
|
+
* per-row `vtab.update()` await, so it does not register today; if bulk-INSERT
|
|
133
|
+
* throughput ever shows this as hot, have the substrates report which columns
|
|
134
|
+
* coercion actually changed and patch only those in place.
|
|
135
|
+
*/
|
|
136
|
+
function withStoredNewSection(flatRow, storedRow, rawNewRow, columnCount) {
|
|
137
|
+
if (storedRow === rawNewRow)
|
|
138
|
+
return flatRow;
|
|
139
|
+
const out = flatRow.slice();
|
|
140
|
+
for (let i = 0; i < columnCount; i++) {
|
|
141
|
+
out[columnCount + i] = storedRow[i];
|
|
142
|
+
}
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
122
145
|
/**
|
|
123
146
|
* Emit an automatic data change event for modules without native event support.
|
|
124
147
|
*/
|
|
@@ -150,13 +173,20 @@ function emitAutoDataEvent(ctx, tableSchema, type, key, oldRow, newRow, changedC
|
|
|
150
173
|
*
|
|
151
174
|
* `deferred` is the per-statement deferred-rebuild set the generator owns: a full-rebuild
|
|
152
175
|
* covering MV is marked dirty here (not applied per row) and rebuilt once at the
|
|
153
|
-
* end-of-statement flush ({@link runWithStatementSavepoints}).
|
|
154
|
-
*
|
|
176
|
+
* end-of-statement flush ({@link runWithStatementSavepoints}).
|
|
177
|
+
*
|
|
178
|
+
* `residualBatch` is the per-statement residual key batch the generator owns: a
|
|
179
|
+
* residual-arm covering MV (`'residual-recompute'` / `'prefix-delete'` /
|
|
180
|
+
* `'join-residual'`) accumulates the changed row's affected binding key(s) here (deduped
|
|
181
|
+
* across the statement) instead of recomputing per row, and each distinct key's residual
|
|
182
|
+
* runs once at the same end-of-statement flush. Only the `'inverse-projection'` arm stays
|
|
183
|
+
* per-row-immediate — the covering-UNIQUE enforcement scan reads its backing
|
|
184
|
+
* mid-statement, and its per-row delta is a cheap pure projection.
|
|
155
185
|
*/
|
|
156
|
-
async function maintainRowTimeStructures(ctx, tableKey, change, cache, deferred) {
|
|
186
|
+
async function maintainRowTimeStructures(ctx, tableKey, change, cache, deferred, residualBatch) {
|
|
157
187
|
if (!ctx.db._hasRowTimeCoveringStructures(tableKey))
|
|
158
188
|
return;
|
|
159
|
-
await ctx.db._maintainRowTimeCoveringStructures(tableKey, change, cache, deferred);
|
|
189
|
+
await ctx.db._maintainRowTimeCoveringStructures(tableKey, change, cache, deferred, residualBatch);
|
|
160
190
|
}
|
|
161
191
|
/**
|
|
162
192
|
* Evaluate the per-statement mutation-context evaluators once, producing the
|
|
@@ -238,21 +268,26 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
238
268
|
existingRowDescriptor: clause.existingRowDescriptor,
|
|
239
269
|
};
|
|
240
270
|
// Resolve the per-target-column enforcement collation NAMES to functions once
|
|
241
|
-
// here (recording the collation dependency so a redefined collation re-emits)
|
|
242
|
-
//
|
|
243
|
-
//
|
|
244
|
-
// targeted constraint routes to DO UPDATE / DO NOTHING instead of aborting.
|
|
271
|
+
// here (recording the collation dependency so a redefined collation re-emits).
|
|
272
|
+
// Consumed by matchUpsertClause so a collation-equal conflict on the targeted
|
|
273
|
+
// constraint routes to DO UPDATE / DO NOTHING instead of aborting.
|
|
245
274
|
if (clause.conflictTargetIndices && clause.conflictTargetCollations) {
|
|
246
275
|
runtime.conflictTargetCollationFns = clause.conflictTargetCollations.map(name => ctx.resolveCollation(name ?? 'BINARY'));
|
|
247
|
-
runtime.conflictTargetTypes = clause.conflictTargetTypes;
|
|
248
276
|
}
|
|
249
277
|
if (clause.action === 'update' && clause.assignments) {
|
|
250
278
|
runtime.assignmentIndices = new Map();
|
|
279
|
+
runtime.assignmentCoercions = new Map();
|
|
251
280
|
for (const [colIndex, valueNode] of clause.assignments) {
|
|
252
281
|
const evaluatorIndex = upsertEvaluatorInstructions.length;
|
|
253
282
|
const instruction = emitCallFromPlan(valueNode, ctx);
|
|
254
283
|
upsertEvaluatorInstructions.push(instruction);
|
|
255
284
|
runtime.assignmentIndices.set(colIndex, evaluatorIndex);
|
|
285
|
+
// Same static-type rule as emitUpdate: convert only when the
|
|
286
|
+
// assignment expression's type is not the column's declared type.
|
|
287
|
+
const column = tableSchema.columns[colIndex];
|
|
288
|
+
if (valueNode.getType().logicalType !== column.logicalType) {
|
|
289
|
+
runtime.assignmentCoercions.set(colIndex, column);
|
|
290
|
+
}
|
|
256
291
|
}
|
|
257
292
|
}
|
|
258
293
|
if (clause.whereCondition) {
|
|
@@ -287,15 +322,16 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
287
322
|
}
|
|
288
323
|
// Match when the proposed values equal the existing row at the clause's
|
|
289
324
|
// conflict-target columns — i.e. the conflict is on those columns —
|
|
290
|
-
// compared the way the constraint ENFORCES: the
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
// byte-identical
|
|
325
|
+
// compared the way the constraint ENFORCES: under the constraint's
|
|
326
|
+
// enforcement collation. Both rows are already in declared (stored)
|
|
327
|
+
// form — the INSERT emitter converted the proposed row by static type —
|
|
328
|
+
// so an affinity-coerced conflict (`'1'` proposed into an INTEGER key
|
|
329
|
+
// holding `1`) arrives converted, and a collation-equal conflict (NOCASE
|
|
330
|
+
// case-variant, RTRIM trailing-space) is routed by the collation compare
|
|
331
|
+
// to the DO UPDATE / DO NOTHING arm rather than aborting with a UNIQUE
|
|
332
|
+
// error. The per-column collation functions are precomputed at emit; a
|
|
333
|
+
// BINARY, byte-identical key still compares 0 (well-formed seeds
|
|
334
|
+
// re-present byte-identical literals, so seed idempotency is unaffected).
|
|
299
335
|
//
|
|
300
336
|
// NOTE: one residual corner remains out of scope — multi-constraint
|
|
301
337
|
// coincidence. If an insert violates the targeted constraint AND another
|
|
@@ -336,22 +372,31 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
336
372
|
// Evaluate assignment expressions with proper contexts
|
|
337
373
|
for (const [colIndex, evaluatorIndex] of clause.assignmentIndices) {
|
|
338
374
|
const evaluator = upsertEvaluators[evaluatorIndex];
|
|
339
|
-
|
|
375
|
+
let value = await withAsyncRowContext(rctx, clause.existingRowDescriptor, () => existingRow, async () => {
|
|
340
376
|
return await withAsyncRowContext(rctx, clause.newRowDescriptor, () => proposedRow, async () => {
|
|
341
377
|
return await evaluator(rctx);
|
|
342
378
|
});
|
|
343
379
|
});
|
|
380
|
+
// Convert to the declared column type when the assignment's static
|
|
381
|
+
// type requires it (see assignmentCoercions) — `existingRow` is a
|
|
382
|
+
// stored row, so the composed updatedRow is then fully declared-form.
|
|
383
|
+
const coerceColumn = clause.assignmentCoercions?.get(colIndex);
|
|
384
|
+
if (coerceColumn) {
|
|
385
|
+
value = validateAndParse(value, coerceColumn.logicalType, coerceColumn.name);
|
|
386
|
+
}
|
|
344
387
|
updatedRow[colIndex] = value;
|
|
345
388
|
}
|
|
346
389
|
}
|
|
347
390
|
// Extract the primary key from existing row
|
|
348
391
|
const keyValues = pkColumnIndicesInSchema.map(idx => existingRow[idx]);
|
|
349
|
-
// Perform the UPDATE operation
|
|
392
|
+
// Perform the UPDATE operation. `preCoerced`: the row is composed of
|
|
393
|
+
// stored cells plus assignments already converted above.
|
|
350
394
|
const updateArgs = {
|
|
351
395
|
operation: 'update',
|
|
352
396
|
values: updatedRow,
|
|
353
397
|
oldKeyValues: keyValues,
|
|
354
398
|
onConflict: ConflictResolution.ABORT,
|
|
399
|
+
preCoerced: true,
|
|
355
400
|
mutationStatement: vtab.wantStatements ?
|
|
356
401
|
buildUpdateStatement(tableSchema, updatedRow, keyValues, contextRow) : undefined
|
|
357
402
|
};
|
|
@@ -362,9 +407,12 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
362
407
|
if (!updateResult.row) {
|
|
363
408
|
return undefined;
|
|
364
409
|
}
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
410
|
+
// Report the row the substrate STORED, not the one we composed — see
|
|
411
|
+
// storedRowOrRaw. `existingRow` is already a stored (coerced) row.
|
|
412
|
+
const storedRow = storedRowOrRaw(updateResult.row, updatedRow, tableSchema.columns.length);
|
|
413
|
+
// Build a flat row for RETURNING (OLD = existing, NEW = stored)
|
|
414
|
+
const flatRow = [...existingRow, ...storedRow];
|
|
415
|
+
return { updatedRow: storedRow, flatRow };
|
|
368
416
|
}
|
|
369
417
|
/**
|
|
370
418
|
* Translate a generic ConstraintError into the right OR-clause subclass
|
|
@@ -419,7 +467,12 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
419
467
|
// non-snapshot-isolated UPDATE/DELETE has drained its source up front to
|
|
420
468
|
// separate the read phase from the write phase. `for await ... of`
|
|
421
469
|
// consumes either — the savepoint/FAIL-mode logic below is unchanged.
|
|
422
|
-
rows, isFailMode, processRow, deferredRebuilds, backingConnCache
|
|
470
|
+
rows, isFailMode, processRow, deferredRebuilds, residualBatch, backingConnCache,
|
|
471
|
+
// Set only when the statement uses batched parent-side RESTRICT
|
|
472
|
+
// enforcement (see getBatchableRestrictFks): flushes the accumulated
|
|
473
|
+
// key batch at the end-of-statement boundary. The gate excludes FAIL,
|
|
474
|
+
// so a non-null flush always runs under the statement savepoint.
|
|
475
|
+
flushFkBatch) {
|
|
423
476
|
let failSavepointCounter = 0;
|
|
424
477
|
// For non-FAIL modes wrap the whole statement so a mid-statement failure
|
|
425
478
|
// unwinds partial writes from earlier rows. FAIL keeps prior rows, so it
|
|
@@ -464,6 +517,14 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
464
517
|
if (savepointName) {
|
|
465
518
|
await ctx.db._rollbackAndReleaseSavepointBroadcast(savepointName);
|
|
466
519
|
savepointName = undefined;
|
|
520
|
+
// The savepoint reverted the failing row's source/backing writes,
|
|
521
|
+
// but the residual batch's JS-side DELTA accumulations cannot be
|
|
522
|
+
// unwound and may carry the reverted row's contribution. The
|
|
523
|
+
// residual KEYS stay valid (a reverted key recomputes from live
|
|
524
|
+
// state to a value-identical row, which the host suppresses), so
|
|
525
|
+
// drop only the delta fast path — the FAIL-mode flush below then
|
|
526
|
+
// routes those entries through the plain residual.
|
|
527
|
+
poisonResidualDeltaAccumulations(residualBatch);
|
|
467
528
|
}
|
|
468
529
|
// Translate plain constraint violations to FAIL/ROLLBACK error
|
|
469
530
|
// subclasses so the iterator-level cleanup picks the right
|
|
@@ -477,13 +538,22 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
477
538
|
yield rowToYield;
|
|
478
539
|
}
|
|
479
540
|
}
|
|
480
|
-
// End-of-statement boundary
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
//
|
|
484
|
-
// error routes to the rollback branch below.
|
|
485
|
-
if (
|
|
486
|
-
await
|
|
541
|
+
// End-of-statement boundary. First the batched parent-side RESTRICT
|
|
542
|
+
// probe (fail fast — skip wasted MV work on a violation), then the
|
|
543
|
+
// deferred maintenance; both BEFORE the statement savepoint releases
|
|
544
|
+
// so a failure rolls the whole statement back. Inside the try, so an
|
|
545
|
+
// error routes to the rollback branch below.
|
|
546
|
+
if (flushFkBatch) {
|
|
547
|
+
await flushFkBatch();
|
|
548
|
+
}
|
|
549
|
+
// Drain the deferred maintenance (residual key
|
|
550
|
+
// batch + full-rebuild set) NOW — after every source row has been applied
|
|
551
|
+
// (so each recompute/rebuild reads all this statement's writes) and BEFORE
|
|
552
|
+
// the statement savepoint releases (so a failed flush rolls the whole
|
|
553
|
+
// statement back). Inside the try, so a flush error routes to the rollback
|
|
554
|
+
// branch below. A no-op when nothing deferred.
|
|
555
|
+
if (deferredRebuilds.size > 0 || residualBatch.size > 0) {
|
|
556
|
+
await ctx.db._flushDeferredMaintenance(deferredRebuilds, residualBatch, backingConnCache);
|
|
487
557
|
}
|
|
488
558
|
if (stmtSavepointName) {
|
|
489
559
|
await ctx.db._releaseSavepointBroadcast(stmtSavepointName);
|
|
@@ -493,18 +563,20 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
493
563
|
if (stmtSavepointName) {
|
|
494
564
|
await ctx.db._rollbackAndReleaseSavepointBroadcast(stmtSavepointName);
|
|
495
565
|
}
|
|
496
|
-
else if (deferredRebuilds.size > 0) {
|
|
566
|
+
else if (deferredRebuilds.size > 0 || residualBatch.size > 0) {
|
|
497
567
|
// OR FAIL keeps the rows that already succeeded (it runs with no
|
|
498
568
|
// statement-scope savepoint), so a mid-statement abort does NOT unwind
|
|
499
|
-
// them. Their deferred full-rebuild MVs must therefore still be
|
|
500
|
-
// before the conflict error propagates — otherwise the backing
|
|
501
|
-
// the surviving source rows mid-transaction
|
|
502
|
-
// The failing row's own per-row savepoint
|
|
503
|
-
//
|
|
504
|
-
//
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
|
|
569
|
+
// them. Their deferred residual/full-rebuild MVs must therefore still be
|
|
570
|
+
// flushed before the conflict error propagates — otherwise the backing
|
|
571
|
+
// would lag the surviving source rows mid-transaction
|
|
572
|
+
// (read(MV) != evaluate(body)). The failing row's own per-row savepoint
|
|
573
|
+
// already reverted its writes, so the flush recomputes over exactly the
|
|
574
|
+
// surviving rows (a reverted row's accumulated key is harmless — its
|
|
575
|
+
// recompute is value-identical and suppressed). The original conflict
|
|
576
|
+
// error is re-thrown after the flush. (A flush failure here is a genuine
|
|
577
|
+
// maintenance error and supersedes the conflict error, matching "a
|
|
578
|
+
// maintenance error fails the source write".)
|
|
579
|
+
await ctx.db._flushDeferredMaintenance(deferredRebuilds, residualBatch, backingConnCache);
|
|
508
580
|
}
|
|
509
581
|
throw e;
|
|
510
582
|
}
|
|
@@ -528,17 +600,19 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
528
600
|
// Per-statement backing-connection cache: resolve each covering MV's backing
|
|
529
601
|
// connection once for the whole statement rather than once per source row.
|
|
530
602
|
const backingConnCache = new Map();
|
|
531
|
-
// Per-statement deferred full-rebuild set (MV keys)
|
|
532
|
-
// are marked dirty
|
|
533
|
-
//
|
|
603
|
+
// Per-statement deferred full-rebuild set (MV keys) + residual key batch:
|
|
604
|
+
// full-rebuild covering MVs are marked dirty and residual-arm covering MVs
|
|
605
|
+
// accumulate their affected binding keys during the row loop; both drain once
|
|
606
|
+
// at the end-of-statement flush in runWithStatementSavepoints.
|
|
534
607
|
const deferredRebuilds = new Set();
|
|
608
|
+
const residualBatch = new Map();
|
|
535
609
|
const isFailMode = plan.onConflict === ConflictResolution.FAIL;
|
|
536
610
|
// Stamp the per-row mutation ordinal so a column `default` referencing
|
|
537
611
|
// `mutation_ordinal()` (a per-row surrogate allocator) resolves to the 1-based
|
|
538
612
|
// position of the row being produced. The defaults are evaluated upstream as
|
|
539
613
|
// each row is *pulled*, so the ordinal is set BEFORE pulling — see
|
|
540
614
|
// `stampMutationOrdinal`.
|
|
541
|
-
yield* runWithStatementSavepoints(ctx, vtab, stampMutationOrdinal(ctx, rows), isFailMode, (flatRow) => processInsertRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, runtimeUpsertClauses, upsertEvaluators, backingConnCache, deferredRebuilds), deferredRebuilds, backingConnCache);
|
|
615
|
+
yield* runWithStatementSavepoints(ctx, vtab, stampMutationOrdinal(ctx, rows), isFailMode, (flatRow) => processInsertRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, runtimeUpsertClauses, upsertEvaluators, backingConnCache, deferredRebuilds, residualBatch), deferredRebuilds, residualBatch, backingConnCache);
|
|
542
616
|
}
|
|
543
617
|
/**
|
|
544
618
|
* Wrap the INSERT source so `rctx.mutationOrdinal` holds the 1-based ordinal of the
|
|
@@ -576,7 +650,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
576
650
|
*
|
|
577
651
|
* Throws ConstraintError on violations the executor cannot recover from.
|
|
578
652
|
*/
|
|
579
|
-
async function processInsertRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, runtimeUpsertClauses, upsertEvaluators, backingConnCache, deferredRebuilds) {
|
|
653
|
+
async function processInsertRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, runtimeUpsertClauses, upsertEvaluators, backingConnCache, deferredRebuilds, residualBatch) {
|
|
580
654
|
const newRow = extractNewRowFromFlat(flatRow, tableSchema.columns.length);
|
|
581
655
|
const tableKey = `${tableSchema.schemaName}.${tableSchema.name}`;
|
|
582
656
|
let mutationStatement;
|
|
@@ -591,6 +665,10 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
591
665
|
// can fall back to per-constraint defaultConflict directives. The memory
|
|
592
666
|
// module treats undefined as ABORT when no constraint default is set.
|
|
593
667
|
onConflict: plan.onConflict,
|
|
668
|
+
// The emitters converted the NEW section to declared types up front
|
|
669
|
+
// (emitInsert / constraint-check DEFAULT substitution), so the storage
|
|
670
|
+
// layer must not convert again — JSON conversion is not repeatable.
|
|
671
|
+
preCoerced: true,
|
|
594
672
|
mutationStatement
|
|
595
673
|
};
|
|
596
674
|
const result = await vtab.update(args);
|
|
@@ -606,7 +684,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
606
684
|
return undefined;
|
|
607
685
|
const existingKeyValues = pkColumnIndicesInSchema.map(idx => result.existingRow[idx]);
|
|
608
686
|
ctx.db._recordUpdate(tableKey, result.existingRow, updateResult.updatedRow, pkColumnIndicesInSchema);
|
|
609
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow: result.existingRow, newRow: updateResult.updatedRow }, backingConnCache, deferredRebuilds);
|
|
687
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow: result.existingRow, newRow: updateResult.updatedRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
610
688
|
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'update', result.existingRow, updateResult.updatedRow, plan.lensRouted);
|
|
611
689
|
if (needsAutoEvents) {
|
|
612
690
|
const changedColumns = [];
|
|
@@ -627,35 +705,41 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
627
705
|
if (!result.row) {
|
|
628
706
|
return undefined;
|
|
629
707
|
}
|
|
708
|
+
// Everything downstream of the write — change tracking, row-time MV
|
|
709
|
+
// maintenance, auto-events, and the row RETURNING projects — must see the
|
|
710
|
+
// row the substrate STORED, not the raw input we handed it. See
|
|
711
|
+
// storedRowOrRaw.
|
|
712
|
+
const numCols = tableSchema.columns.length;
|
|
713
|
+
const storedRow = storedRowOrRaw(result.row, newRow, numCols);
|
|
630
714
|
// Internal REPLACE evictions (rows at OTHER PKs removed to resolve a non-PK
|
|
631
715
|
// UNIQUE conflict) run the full delete pipeline here, before the new row's
|
|
632
716
|
// own bookkeeping — evict-then-write, matching the substrate journal order.
|
|
633
|
-
await processEvictions(ctx, needsAutoEvents, tableKey, result.evictedRows, backingConnCache, deferredRebuilds);
|
|
717
|
+
await processEvictions(ctx, needsAutoEvents, tableKey, result.evictedRows, backingConnCache, deferredRebuilds, residualBatch);
|
|
634
718
|
const replacedRow = result.replacedRow;
|
|
635
719
|
if (replacedRow) {
|
|
636
|
-
const newKeyValues = pkColumnIndicesInSchema.map(idx =>
|
|
637
|
-
ctx.db._recordUpdate(tableKey, replacedRow,
|
|
638
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow: replacedRow, newRow }, backingConnCache, deferredRebuilds);
|
|
720
|
+
const newKeyValues = pkColumnIndicesInSchema.map(idx => storedRow[idx]);
|
|
721
|
+
ctx.db._recordUpdate(tableKey, replacedRow, storedRow, pkColumnIndicesInSchema);
|
|
722
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow: replacedRow, newRow: storedRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
639
723
|
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'delete', replacedRow, undefined, plan.lensRouted);
|
|
640
724
|
if (needsAutoEvents) {
|
|
641
725
|
const changedColumns = [];
|
|
642
|
-
for (let i = 0; i <
|
|
643
|
-
if (!sqlValueIdentical(replacedRow[i],
|
|
726
|
+
for (let i = 0; i < numCols; i++) {
|
|
727
|
+
if (!sqlValueIdentical(replacedRow[i], storedRow[i])) {
|
|
644
728
|
changedColumns.push(tableSchema.columns[i].name);
|
|
645
729
|
}
|
|
646
730
|
}
|
|
647
|
-
emitAutoDataEvent(ctx, tableSchema, 'update', newKeyValues, [...replacedRow], [...
|
|
731
|
+
emitAutoDataEvent(ctx, tableSchema, 'update', newKeyValues, [...replacedRow], [...storedRow], changedColumns);
|
|
648
732
|
}
|
|
649
733
|
}
|
|
650
734
|
else {
|
|
651
|
-
const pkValues = pkColumnIndicesInSchema.map(idx =>
|
|
652
|
-
ctx.db._recordInsert(tableKey,
|
|
653
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'insert', newRow }, backingConnCache, deferredRebuilds);
|
|
735
|
+
const pkValues = pkColumnIndicesInSchema.map(idx => storedRow[idx]);
|
|
736
|
+
ctx.db._recordInsert(tableKey, storedRow, pkColumnIndicesInSchema);
|
|
737
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'insert', newRow: storedRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
654
738
|
if (needsAutoEvents) {
|
|
655
|
-
emitAutoDataEvent(ctx, tableSchema, 'insert', pkValues, undefined, [...
|
|
739
|
+
emitAutoDataEvent(ctx, tableSchema, 'insert', pkValues, undefined, [...storedRow]);
|
|
656
740
|
}
|
|
657
741
|
}
|
|
658
|
-
return flatRow;
|
|
742
|
+
return withStoredNewSection(flatRow, storedRow, newRow, numCols);
|
|
659
743
|
}
|
|
660
744
|
/**
|
|
661
745
|
* Drive the full delete pipeline for every internal REPLACE eviction reported
|
|
@@ -671,7 +755,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
671
755
|
* evict-then-write order (a later same-key backing write must not be undone by
|
|
672
756
|
* an earlier-PK eviction's delete).
|
|
673
757
|
*/
|
|
674
|
-
async function processEvictions(ctx, needsAutoEvents, tableKey, evictedRows, backingConnCache, deferredRebuilds) {
|
|
758
|
+
async function processEvictions(ctx, needsAutoEvents, tableKey, evictedRows, backingConnCache, deferredRebuilds, residualBatch) {
|
|
675
759
|
if (!evictedRows || evictedRows.length === 0)
|
|
676
760
|
return;
|
|
677
761
|
for (const evicted of evictedRows) {
|
|
@@ -690,7 +774,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
690
774
|
await assertTransitiveRestrictsForParentMutation(ctx.db, tableSchema, 'delete', evicted);
|
|
691
775
|
const evictedKeyValues = pkColumnIndicesInSchema.map(idx => evicted[idx]);
|
|
692
776
|
ctx.db._recordDelete(tableKey, evicted, pkColumnIndicesInSchema);
|
|
693
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow: evicted }, backingConnCache, deferredRebuilds);
|
|
777
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow: evicted }, backingConnCache, deferredRebuilds, residualBatch);
|
|
694
778
|
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'delete', evicted);
|
|
695
779
|
if (needsAutoEvents) {
|
|
696
780
|
emitAutoDataEvent(ctx, tableSchema, 'delete', evictedKeyValues, [...evicted]);
|
|
@@ -704,10 +788,20 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
704
788
|
const vtab = await getVTable(ctx, tableSchema);
|
|
705
789
|
const needsAutoEvents = ctx.db._needsDataEvents() && !moduleHasNativeDataEvents(ctx, tableSchema);
|
|
706
790
|
const contextRow = await evaluateContextRow(ctx, contextEvaluators);
|
|
707
|
-
// Per-statement backing-connection cache + deferred full-rebuild set
|
|
791
|
+
// Per-statement backing-connection cache + deferred full-rebuild set + residual
|
|
792
|
+
// key batch (see runInsert).
|
|
708
793
|
const backingConnCache = new Map();
|
|
709
794
|
const deferredRebuilds = new Set();
|
|
795
|
+
const residualBatch = new Map();
|
|
710
796
|
const isFailMode = plan.onConflict === ConflictResolution.FAIL;
|
|
797
|
+
// Batched parent-side RESTRICT enforcement (per-execution state, never on
|
|
798
|
+
// the emit closure — a re-run prepared statement must start empty). When
|
|
799
|
+
// the gate admits the statement, the per-row transitive pre-walk is
|
|
800
|
+
// replaced by key accumulation + one chunked probe per FK at the
|
|
801
|
+
// end-of-statement boundary. The plan side made the same gate decision
|
|
802
|
+
// and skipped the per-row NOT EXISTS checks.
|
|
803
|
+
const batchableFks = getBatchableRestrictFks(ctx.db.schemaManager, tableSchema, 'update', plan.onConflict, plan.lensRouted);
|
|
804
|
+
const fkRestrictBatch = batchableFks ? createParentRestrictBatch(batchableFks) : undefined;
|
|
711
805
|
// Physical Halloween avoidance: unless the target module guarantees per-scan
|
|
712
806
|
// snapshot isolation, fully drain the source match set (closing the scan
|
|
713
807
|
// cursor) BEFORE applying any write — otherwise the first UPDATE invalidates
|
|
@@ -717,7 +811,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
717
811
|
// the accepted cost of correctness (such a module cannot safely stream-update
|
|
718
812
|
// anyway); memory tables set scanSnapshotIsolation and keep streaming.
|
|
719
813
|
const sourceRows = await resolveDmlSourceRows(ctx, vtab, tableSchema, rows);
|
|
720
|
-
yield* runWithStatementSavepoints(ctx, vtab, sourceRows, isFailMode, (flatRow) => processUpdateRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds), deferredRebuilds, backingConnCache);
|
|
814
|
+
yield* runWithStatementSavepoints(ctx, vtab, sourceRows, isFailMode, (flatRow) => processUpdateRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds, residualBatch, fkRestrictBatch), deferredRebuilds, residualBatch, backingConnCache, fkRestrictBatch ? () => flushParentRestrictBatch(ctx.db, tableSchema, 'update', fkRestrictBatch) : undefined);
|
|
721
815
|
}
|
|
722
816
|
/**
|
|
723
817
|
* Performs a single row's UPDATE side-effects (RESTRICT pre-check +
|
|
@@ -727,7 +821,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
727
821
|
* cannot recover from; the caller (runWithStatementSavepoints) translates it
|
|
728
822
|
* for FAIL/ROLLBACK modes.
|
|
729
823
|
*/
|
|
730
|
-
async function processUpdateRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds) {
|
|
824
|
+
async function processUpdateRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds, residualBatch, fkRestrictBatch) {
|
|
731
825
|
const oldRow = extractOldRowFromFlat(flatRow, tableSchema.columns.length);
|
|
732
826
|
const newRow = extractNewRowFromFlat(flatRow, tableSchema.columns.length);
|
|
733
827
|
const tableKey = `${tableSchema.schemaName}.${tableSchema.name}`;
|
|
@@ -743,14 +837,23 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
743
837
|
if (vtab.wantStatements) {
|
|
744
838
|
mutationStatement = buildUpdateStatement(tableSchema, newRow, keyValues, contextRow);
|
|
745
839
|
}
|
|
746
|
-
//
|
|
747
|
-
//
|
|
748
|
-
//
|
|
749
|
-
//
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
//
|
|
753
|
-
|
|
840
|
+
// Parent-side RESTRICT enforcement. Batched statements accumulate the
|
|
841
|
+
// OLD referenced keys (probed once per FK at the end-of-statement
|
|
842
|
+
// boundary — same pre-vtab observation point per row, so a row the vtab
|
|
843
|
+
// later reports not-found contributes identically to the per-row path).
|
|
844
|
+
// Otherwise: defense-in-depth per-row pre-walk — the plan-time
|
|
845
|
+
// `NOT EXISTS` check is the primary path, but some vtab modules evaluate
|
|
846
|
+
// the embedded subquery differently from a plain row scan. Pre-walk the
|
|
847
|
+
// transitive cascade closure so RESTRICTs at any depth fire BEFORE
|
|
848
|
+
// vtab.update — needed for rowid-mode backends (lamina) where
|
|
849
|
+
// post-mutation OLD-value scans dereference through the just-mutated
|
|
850
|
+
// parent and find zero rows.
|
|
851
|
+
if (fkRestrictBatch) {
|
|
852
|
+
accumulateParentRestrictKeys(fkRestrictBatch, 'update', oldRow, newRow);
|
|
853
|
+
}
|
|
854
|
+
else {
|
|
855
|
+
await assertTransitiveRestrictsForParentMutation(ctx.db, tableSchema, 'update', oldRow, newRow, plan.lensRouted);
|
|
856
|
+
}
|
|
754
857
|
const args = {
|
|
755
858
|
operation: 'update',
|
|
756
859
|
values: newRow,
|
|
@@ -759,6 +862,10 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
759
862
|
// can fall back to per-constraint defaultConflict directives. The memory
|
|
760
863
|
// module treats undefined as ABORT when no constraint default is set.
|
|
761
864
|
onConflict: plan.onConflict,
|
|
865
|
+
// emitUpdate converted the NEW section to declared types (assigned
|
|
866
|
+
// columns by static type, unassigned columns are stored values) — the
|
|
867
|
+
// storage layer must not convert again.
|
|
868
|
+
preCoerced: true,
|
|
762
869
|
mutationStatement
|
|
763
870
|
};
|
|
764
871
|
const result = await vtab.update(args);
|
|
@@ -770,6 +877,11 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
770
877
|
if (!result.row) {
|
|
771
878
|
return undefined;
|
|
772
879
|
}
|
|
880
|
+
// Post-write bookkeeping and the RETURNING row must see the row the
|
|
881
|
+
// substrate STORED, not the raw input — see storedRowOrRaw. `oldRow` came
|
|
882
|
+
// from the source scan, so it is already a stored (coerced) row.
|
|
883
|
+
const numCols = tableSchema.columns.length;
|
|
884
|
+
const storedRow = storedRowOrRaw(result.row, newRow, numCols);
|
|
773
885
|
// If the UPDATE moved this row onto an occupied PK under REPLACE,
|
|
774
886
|
// the vtab returns the displaced row. Surface its deletion BEFORE
|
|
775
887
|
// the move bookkeeping so change tracking, FK cascade, and auto-events
|
|
@@ -781,7 +893,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
781
893
|
if (result.replacedRow) {
|
|
782
894
|
const evictedKeyValues = pkColumnIndicesInSchema.map(idx => result.replacedRow[idx]);
|
|
783
895
|
ctx.db._recordDelete(tableKey, result.replacedRow, pkColumnIndicesInSchema);
|
|
784
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow: result.replacedRow }, backingConnCache, deferredRebuilds);
|
|
896
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow: result.replacedRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
785
897
|
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'delete', result.replacedRow, undefined, plan.lensRouted);
|
|
786
898
|
if (needsAutoEvents) {
|
|
787
899
|
emitAutoDataEvent(ctx, tableSchema, 'delete', evictedKeyValues, [...result.replacedRow]);
|
|
@@ -791,27 +903,27 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
791
903
|
// UNIQUE conflict for this same update) run the full delete pipeline here,
|
|
792
904
|
// after any same-PK replacedRow handling and before the moved row's own
|
|
793
905
|
// bookkeeping — evict-then-write, matching the substrate journal order.
|
|
794
|
-
await processEvictions(ctx, needsAutoEvents, tableKey, result.evictedRows, backingConnCache, deferredRebuilds);
|
|
906
|
+
await processEvictions(ctx, needsAutoEvents, tableKey, result.evictedRows, backingConnCache, deferredRebuilds, residualBatch);
|
|
795
907
|
// Track change (UPDATE): pass full rows so the change capture can
|
|
796
908
|
// project the columns any active subscription cares about.
|
|
797
|
-
ctx.db._recordUpdate(tableKey, oldRow,
|
|
798
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow, newRow }, backingConnCache, deferredRebuilds);
|
|
909
|
+
ctx.db._recordUpdate(tableKey, oldRow, storedRow, pkColumnIndicesInSchema);
|
|
910
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'update', oldRow, newRow: storedRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
799
911
|
// Execute FK cascading actions (CASCADE, SET NULL, SET DEFAULT)
|
|
800
|
-
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'update', oldRow,
|
|
912
|
+
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'update', oldRow, storedRow, plan.lensRouted);
|
|
801
913
|
// Emit auto event for modules without native event support
|
|
802
914
|
if (needsAutoEvents) {
|
|
803
915
|
// Compute changed columns
|
|
804
916
|
const changedColumns = [];
|
|
805
|
-
for (let i = 0; i <
|
|
917
|
+
for (let i = 0; i < numCols; i++) {
|
|
806
918
|
const oldVal = oldRow[i];
|
|
807
|
-
const newVal =
|
|
919
|
+
const newVal = storedRow[i];
|
|
808
920
|
if (!sqlValueIdentical(oldVal, newVal)) {
|
|
809
921
|
changedColumns.push(tableSchema.columns[i].name);
|
|
810
922
|
}
|
|
811
923
|
}
|
|
812
|
-
emitAutoDataEvent(ctx, tableSchema, 'update', keyValues, [...oldRow], [...
|
|
924
|
+
emitAutoDataEvent(ctx, tableSchema, 'update', keyValues, [...oldRow], [...storedRow], changedColumns);
|
|
813
925
|
}
|
|
814
|
-
return flatRow;
|
|
926
|
+
return withStoredNewSection(flatRow, storedRow, newRow, numCols);
|
|
815
927
|
}
|
|
816
928
|
// DELETE ----------------------------------------------------
|
|
817
929
|
async function* runDelete(ctx, rows, ...contextEvaluators) {
|
|
@@ -820,16 +932,23 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
820
932
|
const vtab = await getVTable(ctx, tableSchema);
|
|
821
933
|
const needsAutoEvents = ctx.db._needsDataEvents() && !moduleHasNativeDataEvents(ctx, tableSchema);
|
|
822
934
|
const contextRow = await evaluateContextRow(ctx, contextEvaluators);
|
|
823
|
-
// Per-statement backing-connection cache + deferred full-rebuild set
|
|
935
|
+
// Per-statement backing-connection cache + deferred full-rebuild set + residual
|
|
936
|
+
// key batch (see runInsert).
|
|
824
937
|
const backingConnCache = new Map();
|
|
825
938
|
const deferredRebuilds = new Set();
|
|
939
|
+
const residualBatch = new Map();
|
|
826
940
|
const isFailMode = plan.onConflict === ConflictResolution.FAIL;
|
|
941
|
+
// Batched parent-side RESTRICT enforcement — see the matching note in
|
|
942
|
+
// runUpdate (per-execution state; one chunked probe per FK at the
|
|
943
|
+
// end-of-statement boundary when the gate admits the statement).
|
|
944
|
+
const batchableFks = getBatchableRestrictFks(ctx.db.schemaManager, tableSchema, 'delete', plan.onConflict, plan.lensRouted);
|
|
945
|
+
const fkRestrictBatch = batchableFks ? createParentRestrictBatch(batchableFks) : undefined;
|
|
827
946
|
// Physical Halloween avoidance — see the matching note in runUpdate. Unless
|
|
828
947
|
// the target module guarantees per-scan snapshot isolation, drain the source
|
|
829
948
|
// match set (closing the scan cursor) before applying any DELETE, so the
|
|
830
949
|
// first delete cannot invalidate the cursor path the scan is still walking.
|
|
831
950
|
const sourceRows = await resolveDmlSourceRows(ctx, vtab, tableSchema, rows);
|
|
832
|
-
yield* runWithStatementSavepoints(ctx, vtab, sourceRows, isFailMode, (flatRow) => processDeleteRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds), deferredRebuilds, backingConnCache);
|
|
951
|
+
yield* runWithStatementSavepoints(ctx, vtab, sourceRows, isFailMode, (flatRow) => processDeleteRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds, residualBatch, fkRestrictBatch), deferredRebuilds, residualBatch, backingConnCache, fkRestrictBatch ? () => flushParentRestrictBatch(ctx.db, tableSchema, 'delete', fkRestrictBatch) : undefined);
|
|
833
952
|
}
|
|
834
953
|
/**
|
|
835
954
|
* Performs a single row's DELETE side-effects (RESTRICT pre-check +
|
|
@@ -839,7 +958,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
839
958
|
* (runWithStatementSavepoints) translates constraint violations for
|
|
840
959
|
* FAIL/ROLLBACK modes.
|
|
841
960
|
*/
|
|
842
|
-
async function processDeleteRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds) {
|
|
961
|
+
async function processDeleteRow(ctx, vtab, needsAutoEvents, flatRow, contextRow, backingConnCache, deferredRebuilds, residualBatch, fkRestrictBatch) {
|
|
843
962
|
const oldRow = extractOldRowFromFlat(flatRow, tableSchema.columns.length);
|
|
844
963
|
const tableKey = `${tableSchema.schemaName}.${tableSchema.name}`;
|
|
845
964
|
const keyValues = pkColumnIndicesInSchema.map(pkColIdx => {
|
|
@@ -853,14 +972,21 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
853
972
|
if (vtab.wantStatements) {
|
|
854
973
|
mutationStatement = buildDeleteStatement(tableSchema, keyValues, contextRow);
|
|
855
974
|
}
|
|
856
|
-
//
|
|
857
|
-
// path above.
|
|
858
|
-
|
|
975
|
+
// Parent-side RESTRICT enforcement: batched key accumulation, or the
|
|
976
|
+
// per-row defense-in-depth pre-walk — see comment on the UPDATE path above.
|
|
977
|
+
if (fkRestrictBatch) {
|
|
978
|
+
accumulateParentRestrictKeys(fkRestrictBatch, 'delete', oldRow);
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
await assertTransitiveRestrictsForParentMutation(ctx.db, tableSchema, 'delete', oldRow, undefined, plan.lensRouted);
|
|
982
|
+
}
|
|
859
983
|
const args = {
|
|
860
984
|
operation: 'delete',
|
|
861
985
|
values: undefined,
|
|
862
986
|
oldKeyValues: keyValues,
|
|
863
987
|
onConflict: plan.onConflict ?? ConflictResolution.ABORT,
|
|
988
|
+
// The key cells come from the scanned OLD row — already stored form.
|
|
989
|
+
preCoerced: true,
|
|
864
990
|
mutationStatement
|
|
865
991
|
};
|
|
866
992
|
const result = await vtab.update(args);
|
|
@@ -875,7 +1001,7 @@ export function emitDmlExecutor(plan, ctx) {
|
|
|
875
1001
|
// Track change (DELETE): record OLD row + PK indices so capture
|
|
876
1002
|
// can project the columns subscribers care about.
|
|
877
1003
|
ctx.db._recordDelete(tableKey, oldRow, pkColumnIndicesInSchema);
|
|
878
|
-
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow }, backingConnCache, deferredRebuilds);
|
|
1004
|
+
await maintainRowTimeStructures(ctx, tableKey, { op: 'delete', oldRow }, backingConnCache, deferredRebuilds, residualBatch);
|
|
879
1005
|
// Execute FK cascading actions (CASCADE, SET NULL, SET DEFAULT)
|
|
880
1006
|
await executeForeignKeyActionsAndLens(ctx.db, tableSchema, 'delete', oldRow, undefined, plan.lensRouted);
|
|
881
1007
|
// Emit auto event for modules without native event support
|