@quereus/quereus 4.20.0 → 4.20.2
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 +2 -0
- package/dist/src/common/types.d.ts +4 -4
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/schema/catalog-rendering.js +13 -1
- package/dist/src/schema/catalog-rendering.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +35 -2
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +42 -33
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +195 -33
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +23 -1
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +43 -1
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +15 -12
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +87 -100
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +7 -4
- package/test/README.md +173 -0
- package/test/logic/01-basic.sqllogic +39 -0
- package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
- package/test/logic/01.2-strings.sqllogic +43 -0
- package/test/logic/01.5-insert-select.sqllogic +237 -0
- package/test/logic/01.6-update-extras.sqllogic +118 -0
- package/test/logic/01.7-update-from.sqllogic +67 -0
- package/test/logic/01.8-delete-extras.sqllogic +122 -0
- package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
- package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
- package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
- package/test/logic/01.9-query-expr-values.sqllogic +111 -0
- package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
- package/test/logic/02-filters.sqllogic +54 -0
- package/test/logic/02-smoke.sqllogic +62 -0
- package/test/logic/02.1-bind-parameters.sqllogic +223 -0
- package/test/logic/03-expressions.sqllogic +190 -0
- package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
- package/test/logic/03.4-defaults.sqllogic +554 -0
- package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
- package/test/logic/03.5-tvf.sqllogic +61 -0
- package/test/logic/03.6-type-system.sqllogic +544 -0
- package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
- package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
- package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
- package/test/logic/03.8-not-precedence.sqllogic +255 -0
- package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
- package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
- package/test/logic/04-order-by.sqllogic +38 -0
- package/test/logic/04-transactions.sqllogic +239 -0
- package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
- package/test/logic/05-vtab_memory.sqllogic +50 -0
- package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
- package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
- package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
- package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
- package/test/logic/06-builtin_functions.sqllogic +149 -0
- package/test/logic/06.1-string-functions.sqllogic +92 -0
- package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
- package/test/logic/06.2-math-functions.sqllogic +82 -0
- package/test/logic/06.3-schema.sqllogic +48 -0
- package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
- package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
- package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
- package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
- package/test/logic/06.3.4-view-info.sqllogic +378 -0
- package/test/logic/06.3.5-column-info.sqllogic +347 -0
- package/test/logic/06.3.6-schema-views.sqllogic +29 -0
- package/test/logic/06.4-schema-search-path.sqllogic +354 -0
- package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
- package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
- package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
- package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
- package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
- package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
- package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
- package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
- package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
- package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
- package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
- package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
- package/test/logic/06.7-json-extended.sqllogic +53 -0
- package/test/logic/06.8-json-path-operators.sqllogic +99 -0
- package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
- package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
- package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
- package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
- package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
- package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
- package/test/logic/07-aggregates.sqllogic +193 -0
- package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
- package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
- package/test/logic/07.3-group-by-extras.sqllogic +602 -0
- package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
- package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
- package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
- package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
- package/test/logic/07.5-window.sqllogic +1941 -0
- package/test/logic/07.5.1-window-named.sqllogic +13 -0
- package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
- package/test/logic/07.6-subqueries.sqllogic +78 -0
- package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
- package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
- package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
- package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
- package/test/logic/07.7.1-in-extras.sqllogic +158 -0
- package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
- package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
- package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
- package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
- package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
- package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
- package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
- package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
- package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
- package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
- package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
- package/test/logic/07.9-in-value-list.sqllogic +154 -0
- package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
- package/test/logic/08-views.sqllogic +64 -0
- package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
- package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
- package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
- package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
- package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
- package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
- package/test/logic/09-set_operations.sqllogic +244 -0
- package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
- package/test/logic/10-distinct_datatypes.sqllogic +127 -0
- package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
- package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
- package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
- package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
- package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
- package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
- package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
- package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
- package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
- package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
- package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
- package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
- package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
- package/test/logic/10.2-column-features.sqllogic +365 -0
- package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
- package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
- package/test/logic/10.3-function-features.sqllogic +247 -0
- package/test/logic/10.4-schema-scale.sqllogic +168 -0
- package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
- package/test/logic/10.5-indexes.sqllogic +164 -0
- package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
- package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
- package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
- package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
- package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
- package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
- package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
- package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
- package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
- package/test/logic/100.1-where-extras.sqllogic +163 -0
- package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
- package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
- package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
- package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
- package/test/logic/102-unique-constraints.sqllogic +145 -0
- package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
- package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
- package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
- package/test/logic/102.2-unique-collation.sqllogic +181 -0
- package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
- package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
- package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
- package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
- package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
- package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
- package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
- package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
- package/test/logic/108-cardinality-estimation.sqllogic +146 -0
- package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
- package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
- package/test/logic/11-joins.sqllogic +86 -0
- package/test/logic/11.1-join-using.sqllogic +267 -0
- package/test/logic/11.2-comma-join.sqllogic +35 -0
- package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
- package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
- package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
- package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
- package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
- package/test/logic/12-empty-primary-key.sqllogic +124 -0
- package/test/logic/12-join_padding_order.sqllogic +58 -0
- package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
- package/test/logic/13-cte.sqllogic +137 -0
- package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
- package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
- package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
- package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
- package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
- package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
- package/test/logic/13.4-cte-extras.sqllogic +98 -0
- package/test/logic/13.5-cte-join-order.sqllogic +166 -0
- package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
- package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
- package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
- package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
- package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
- package/test/logic/14-utilities.sqllogic +168 -0
- package/test/logic/15-timespan.sqllogic +279 -0
- package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
- package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
- package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
- package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
- package/test/logic/16-epoch.sqllogic +187 -0
- package/test/logic/17-weekday-modifier.sqllogic +78 -0
- package/test/logic/18-json-string-escapes.sqllogic +86 -0
- package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
- package/test/logic/20-empty-single-row.sqllogic +261 -0
- package/test/logic/21-null-edge-cases.sqllogic +227 -0
- package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
- package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
- package/test/logic/21.2-between-edges.sqllogic +157 -0
- package/test/logic/22-boundary-values.sqllogic +175 -0
- package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
- package/test/logic/24-builtin-branches.sqllogic +611 -0
- package/test/logic/24.1-substr-extras.sqllogic +75 -0
- package/test/logic/24.2-random-extras.sqllogic +39 -0
- package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
- package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
- package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
- package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
- package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
- package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
- package/test/logic/26-join-edge-cases.sqllogic +333 -0
- package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
- package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
- package/test/logic/26.3-join-on-constants.sqllogic +61 -0
- package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
- package/test/logic/27-window-edge-cases.sqllogic +206 -0
- package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
- package/test/logic/27.2-window-exclude.sqllogic +13 -0
- package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
- package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
- package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
- package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
- package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
- package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
- package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
- package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
- package/test/logic/40-constraints.sqllogic +418 -0
- package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
- package/test/logic/40.2-check-extras.sqllogic +683 -0
- package/test/logic/41-alter-table.sqllogic +191 -0
- package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
- package/test/logic/41-fk-cross-schema.sqllogic +155 -0
- package/test/logic/41-fk-extended-targets.sqllogic +173 -0
- package/test/logic/41-foreign-keys.sqllogic +1001 -0
- package/test/logic/41-generated-column-errors.sqllogic +225 -0
- package/test/logic/41-generated-column-extras.sqllogic +598 -0
- package/test/logic/41-generated-column-scope.sqllogic +329 -0
- package/test/logic/41-generated-columns.sqllogic +361 -0
- package/test/logic/41.1-alter-pk.sqllogic +230 -0
- package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
- package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
- package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
- package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
- package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
- package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
- package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
- package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
- package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
- package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
- package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
- package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
- package/test/logic/41.2-alter-column.sqllogic +382 -0
- package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
- package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
- package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
- package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
- package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
- package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
- package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
- package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
- package/test/logic/41.5-alter-misc.sqllogic +48 -0
- package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
- package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
- package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
- package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
- package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
- package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
- package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
- package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
- package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
- package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
- package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
- package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
- package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
- package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
- package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
- package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
- package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
- package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
- package/test/logic/42-committed-snapshot.sqllogic +254 -0
- package/test/logic/42-returning.sqllogic +159 -0
- package/test/logic/42.1-returning-extras.sqllogic +232 -0
- package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
- package/test/logic/43-default-nullability.sqllogic +131 -0
- package/test/logic/43-transition-constraints.sqllogic +341 -0
- package/test/logic/43.1-default-collation.sqllogic +140 -0
- package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
- package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
- package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
- package/test/logic/44-determinism-validation.sqllogic +164 -0
- package/test/logic/44-orthogonality.sqllogic +146 -0
- package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
- package/test/logic/45-udf-determinism.sqllogic +39 -0
- package/test/logic/46-mutation-context.sqllogic +595 -0
- package/test/logic/47-upsert.sqllogic +255 -0
- package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
- package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
- package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
- package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
- package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
- package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
- package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
- package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
- package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
- package/test/logic/49-reference-graph.sqllogic +84 -0
- package/test/logic/50-declarative-schema.sqllogic +2550 -0
- package/test/logic/50-metadata-tags.sqllogic +1033 -0
- package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
- package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
- package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
- package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +173 -0
- package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
- package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
- package/test/logic/51-lens-foundation.sqllogic +147 -0
- package/test/logic/51-materialized-views.sqllogic +376 -0
- package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
- package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
- package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
- package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
- package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
- package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
- package/test/logic/52-lens-overrides.sqllogic +566 -0
- package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
- package/test/logic/53-reserved-tags.sqllogic +252 -0
- package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
- package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
- package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
- package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
- package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
- package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
- package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
- package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
- package/test/logic/55-lens-prover.sqllogic +202 -0
- package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
- package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
- package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
- package/test/logic/81-quickpick.sqllogic +24 -0
- package/test/logic/82-bloom-join.sqllogic +185 -0
- package/test/logic/83-merge-join.sqllogic +280 -0
- package/test/logic/84-asof-scan.sqllogic +238 -0
- package/test/logic/84-key-cardinality.sqllogic +50 -0
- package/test/logic/85-relational-const-folding.sqllogic +51 -0
- package/test/logic/86-scalar-cse.sqllogic +42 -0
- package/test/logic/90-error_paths.sqllogic +114 -0
- package/test/logic/90.1-parse-errors.sqllogic +77 -0
- package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
- package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
- package/test/logic/90.3-expression-errors.sqllogic +43 -0
- package/test/logic/90.4-dml-errors.sqllogic +47 -0
- package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
- package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
- package/test/logic/90.6-select-error-paths.sqllogic +57 -0
- package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
- package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
- package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
- package/test/logic/93.1-view-error-paths.sqllogic +74 -0
- package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
- package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
- package/test/logic/93.4-view-mutation.sqllogic +4881 -0
- package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
- package/test/logic/93.5-authored-inverse.sqllogic +415 -0
- package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
- package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
- package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
- package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
- package/test/logic/95-assertions.sqllogic +1417 -0
- package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
- package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
- package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
- package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
- package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
- package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
- package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
- package/test/logic/drop-unique-index.sqllogic +88 -0
- package/test/logic/pushdown-test.sqllogic +36 -0
|
@@ -0,0 +1,862 @@
|
|
|
1
|
+
-- 93.6-set-op-flagless-write.sqllogic
|
|
2
|
+
-- Flag-less predicate-honest set-op writes (`set-op-flagless-predicate-honest-writes`):
|
|
3
|
+
-- a flag-less `union all` of literal-discriminator legs (`'red' as kind`) is writable via a
|
|
4
|
+
-- plan-time σ + literal-discriminator branch oracle (`checkSatisfiability`). The literal
|
|
5
|
+
-- discriminators are READ-ONLY; the plain base columns round-trip through the branch spine.
|
|
6
|
+
-- See docs/vu-setops.md § Set-operation membership writes and docs/sql.md.
|
|
7
|
+
|
|
8
|
+
-- ===================================================================
|
|
9
|
+
-- Schema: a 4-leg flag-less union all. `kind` (red/large) and `src` (A/B) are literal
|
|
10
|
+
-- discriminators; `id` / `x` are plain (writable) base columns.
|
|
11
|
+
-- ===================================================================
|
|
12
|
+
-- (`color` / `size` are nullable so a leg's omitted base column defaults to null when its
|
|
13
|
+
-- sibling σ does not pin it — this engine defaults columns to NOT NULL.)
|
|
14
|
+
create table items_a (id integer primary key, x integer, color text null, size text null);
|
|
15
|
+
create table items_b (id integer primary key, x integer, color text null, size text null);
|
|
16
|
+
insert into items_a values (1,10,'red','small'),(2,20,'blue','large');
|
|
17
|
+
insert into items_b values (3,30,'red','small'),(4,40,'blue','large');
|
|
18
|
+
|
|
19
|
+
create view U as
|
|
20
|
+
select id, x, 'red' as kind, 'A' as src from items_a where color = 'red'
|
|
21
|
+
union all
|
|
22
|
+
select id, x, 'red' as kind, 'B' as src from items_b where color = 'red'
|
|
23
|
+
union all
|
|
24
|
+
select id, x, 'large' as kind, 'A' as src from items_a where size = 'large'
|
|
25
|
+
union all
|
|
26
|
+
select id, x, 'large' as kind, 'B' as src from items_b where size = 'large';
|
|
27
|
+
|
|
28
|
+
select * from U order by id;
|
|
29
|
+
→ [{"id":1,"x":10,"kind":"red","src":"A"},{"id":2,"x":20,"kind":"large","src":"A"},{"id":3,"x":30,"kind":"red","src":"B"},{"id":4,"x":40,"kind":"large","src":"B"}]
|
|
30
|
+
|
|
31
|
+
-- --- static surfaces: writable shape, discriminators read-only ---
|
|
32
|
+
select is_insertable_into, is_updatable, is_deletable, effective_targets from view_info('U');
|
|
33
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES","effective_targets":"[\"items_a\",\"items_b\"]"}]
|
|
34
|
+
|
|
35
|
+
select cid, column_name, is_updatable, base_table, base_column from column_info('U');
|
|
36
|
+
→ [{"cid":0,"column_name":"id","is_updatable":"YES","base_table":null,"base_column":null},{"cid":1,"column_name":"x","is_updatable":"YES","base_table":null,"base_column":null},{"cid":2,"column_name":"kind","is_updatable":"NO","base_table":null,"base_column":null},{"cid":3,"column_name":"src","is_updatable":"NO","base_table":null,"base_column":null}]
|
|
37
|
+
|
|
38
|
+
-- ===================================================================
|
|
39
|
+
-- INSERT routing: the literal discriminators select exactly one leg; the other three are
|
|
40
|
+
-- provably inconsistent (`unsat`) and skipped. Omitted base columns recover via the leg σ.
|
|
41
|
+
-- ===================================================================
|
|
42
|
+
-- (5,50,'red','B') → only the `items_b where color='red'` leg.
|
|
43
|
+
insert into U (id, x, kind, src) values (5, 50, 'red', 'B');
|
|
44
|
+
select id, x, color, size from items_b order by id;
|
|
45
|
+
→ [{"id":3,"x":30,"color":"red","size":"small"},{"id":4,"x":40,"color":"blue","size":"large"},{"id":5,"x":50,"color":"red","size":null}]
|
|
46
|
+
-- items_a untouched (the two `src='A'` / `kind='large'` legs were skipped).
|
|
47
|
+
select id from items_a order by id;
|
|
48
|
+
→ [{"id":1},{"id":2}]
|
|
49
|
+
select * from U where id = 5;
|
|
50
|
+
→ [{"id":5,"x":50,"kind":"red","src":"B"}]
|
|
51
|
+
|
|
52
|
+
-- (6,60,'large','A') → only the `items_a where size='large'` leg; σ recovers `size='large'`.
|
|
53
|
+
insert into U (id, x, kind, src) values (6, 60, 'large', 'A');
|
|
54
|
+
select id, color, size from items_a where id = 6;
|
|
55
|
+
→ [{"id":6,"color":null,"size":"large"}]
|
|
56
|
+
select * from U where id = 6;
|
|
57
|
+
→ [{"id":6,"x":60,"kind":"large","src":"A"}]
|
|
58
|
+
|
|
59
|
+
-- ===================================================================
|
|
60
|
+
-- DELETE fan-out: `kind='large'` fans to BOTH `size='large'` legs (items_a + items_b); the
|
|
61
|
+
-- two `kind='red'` legs are provably inconsistent and skipped.
|
|
62
|
+
-- ===================================================================
|
|
63
|
+
delete from U where kind = 'large';
|
|
64
|
+
select id from items_a order by id;
|
|
65
|
+
→ [{"id":1}]
|
|
66
|
+
select id from items_b order by id;
|
|
67
|
+
→ [{"id":3},{"id":5}]
|
|
68
|
+
|
|
69
|
+
-- ===================================================================
|
|
70
|
+
-- data-UPDATE fan-out + discriminator read-only rejection (fresh tables).
|
|
71
|
+
-- ===================================================================
|
|
72
|
+
create table ua (id integer primary key, x integer, color text null, size text null);
|
|
73
|
+
create table ub (id integer primary key, x integer, color text null, size text null);
|
|
74
|
+
insert into ua values (1,10,'red','small'),(2,20,'blue','large');
|
|
75
|
+
insert into ub values (3,30,'red','small'),(4,40,'blue','large');
|
|
76
|
+
create view U4 as
|
|
77
|
+
select id, x, 'red' as kind, 'A' as src from ua where color = 'red'
|
|
78
|
+
union all
|
|
79
|
+
select id, x, 'red' as kind, 'B' as src from ub where color = 'red'
|
|
80
|
+
union all
|
|
81
|
+
select id, x, 'large' as kind, 'A' as src from ua where size = 'large'
|
|
82
|
+
union all
|
|
83
|
+
select id, x, 'large' as kind, 'B' as src from ub where size = 'large';
|
|
84
|
+
|
|
85
|
+
-- `src='A'` fans to the two items_a-based legs (red + large) only.
|
|
86
|
+
update U4 set x = x + 1 where src = 'A';
|
|
87
|
+
select id, x from ua order by id;
|
|
88
|
+
→ [{"id":1,"x":11},{"id":2,"x":21}]
|
|
89
|
+
select id, x from ub order by id;
|
|
90
|
+
→ [{"id":3,"x":30},{"id":4,"x":40}]
|
|
91
|
+
|
|
92
|
+
-- A literal discriminator is read-only (no base inverse) — NOT silently routed.
|
|
93
|
+
update U4 set kind = 'large' where id = 1;
|
|
94
|
+
-- error: read-only
|
|
95
|
+
|
|
96
|
+
update U4 set src = 'B' where id = 1;
|
|
97
|
+
-- error: read-only
|
|
98
|
+
|
|
99
|
+
-- An unknown column is a clean reject.
|
|
100
|
+
update U4 set bogus = 1 where id = 1;
|
|
101
|
+
-- error: not a column
|
|
102
|
+
|
|
103
|
+
-- ===================================================================
|
|
104
|
+
-- Rejected boundaries: SELECT-source insert (v1 VALUES only) + RETURNING.
|
|
105
|
+
-- ===================================================================
|
|
106
|
+
insert into U4 (id, x, kind, src) select id, x, 'red', 'A' from ua;
|
|
107
|
+
-- error: VALUES source
|
|
108
|
+
|
|
109
|
+
delete from U4 where id = 1 returning id;
|
|
110
|
+
-- error: RETURNING
|
|
111
|
+
|
|
112
|
+
-- ===================================================================
|
|
113
|
+
-- σ-consistency oracle: a provably-inconsistent leg is skipped; the checker never emits a
|
|
114
|
+
-- false `unsat`, so a real target leg is never dropped.
|
|
115
|
+
-- ===================================================================
|
|
116
|
+
create table sa (id integer primary key, x integer, color text null);
|
|
117
|
+
create table sb (id integer primary key, x integer, color text null);
|
|
118
|
+
insert into sa values (1,10,'red'),(2,20,'red');
|
|
119
|
+
insert into sb values (3,30,'green'),(4,40,'green');
|
|
120
|
+
create view U6 as
|
|
121
|
+
select id, x, 'lit' as tag from sa where color = 'red'
|
|
122
|
+
union all
|
|
123
|
+
select id, x, 'fn' as tag from sb where length(color) > 2;
|
|
124
|
+
|
|
125
|
+
select * from U6 order by id;
|
|
126
|
+
→ [{"id":1,"x":10,"tag":"lit"},{"id":2,"x":20,"tag":"lit"},{"id":3,"x":30,"tag":"fn"},{"id":4,"x":40,"tag":"fn"}]
|
|
127
|
+
|
|
128
|
+
-- `tag='lit'` is provably inconsistent with the `'fn'` leg → that leg is skipped (sb intact),
|
|
129
|
+
-- even though its σ (`length(color) > 2`) is undecidable to the checker.
|
|
130
|
+
delete from U6 where tag = 'lit';
|
|
131
|
+
select id from sa order by id;
|
|
132
|
+
→ []
|
|
133
|
+
select id from sb order by id;
|
|
134
|
+
→ [{"id":3},{"id":4}]
|
|
135
|
+
|
|
136
|
+
-- Honest fan-out: an UNDECIDABLE predicate (`tag like 'f%'`) is included on BOTH legs (no
|
|
137
|
+
-- false `unsat`), and the member-exists correlation self-restricts the no-op leg — the `fn`
|
|
138
|
+
-- leg's rows go, the `lit` leg's stay (its discriminator never matches the captured `fn` rows).
|
|
139
|
+
create table la (id integer primary key, x integer, color text null);
|
|
140
|
+
create table lb (id integer primary key, x integer, color text null);
|
|
141
|
+
insert into la values (1,10,'red'),(2,20,'red');
|
|
142
|
+
insert into lb values (3,30,'green'),(4,40,'green');
|
|
143
|
+
create view U7 as
|
|
144
|
+
select id, x, 'lit' as tag from la where color = 'red'
|
|
145
|
+
union all
|
|
146
|
+
select id, x, 'fn' as tag from lb where length(color) > 2;
|
|
147
|
+
delete from U7 where tag like 'f%';
|
|
148
|
+
select id from la order by id;
|
|
149
|
+
→ [{"id":1},{"id":2}]
|
|
150
|
+
select id from lb order by id;
|
|
151
|
+
→ []
|
|
152
|
+
|
|
153
|
+
-- ===================================================================
|
|
154
|
+
-- `intersect` semantics: insert fans to EVERY leg (a row must be in all to be visible);
|
|
155
|
+
-- delete fans to every leg by default.
|
|
156
|
+
-- ===================================================================
|
|
157
|
+
create table ia (id integer primary key, v integer);
|
|
158
|
+
create table ib (id integer primary key, v integer);
|
|
159
|
+
insert into ia values (1,10),(2,20);
|
|
160
|
+
insert into ib values (2,20),(3,30);
|
|
161
|
+
create view IV as select id, v from ia intersect select id, v from ib;
|
|
162
|
+
select * from IV order by id;
|
|
163
|
+
→ [{"id":2,"v":20}]
|
|
164
|
+
|
|
165
|
+
insert into IV (id, v) values (5, 50);
|
|
166
|
+
select id from ia order by id;
|
|
167
|
+
→ [{"id":1},{"id":2},{"id":5}]
|
|
168
|
+
select id from ib order by id;
|
|
169
|
+
→ [{"id":2},{"id":3},{"id":5}]
|
|
170
|
+
select * from IV order by id;
|
|
171
|
+
→ [{"id":2,"v":20},{"id":5,"v":50}]
|
|
172
|
+
|
|
173
|
+
delete from IV where id = 2;
|
|
174
|
+
select id from ia order by id;
|
|
175
|
+
→ [{"id":1},{"id":5}]
|
|
176
|
+
select id from ib order by id;
|
|
177
|
+
→ [{"id":3},{"id":5}]
|
|
178
|
+
|
|
179
|
+
-- ===================================================================
|
|
180
|
+
-- `except` (A except B) semantics: insert into the LEFT operand only; delete from the LEFT
|
|
181
|
+
-- operand only (so the row leaves the difference).
|
|
182
|
+
-- ===================================================================
|
|
183
|
+
create table ea (id integer primary key, v integer);
|
|
184
|
+
create table eb (id integer primary key, v integer);
|
|
185
|
+
insert into ea values (1,10),(2,20);
|
|
186
|
+
insert into eb values (2,20);
|
|
187
|
+
create view EV as select id, v from ea except select id, v from eb;
|
|
188
|
+
select * from EV order by id;
|
|
189
|
+
→ [{"id":1,"v":10}]
|
|
190
|
+
|
|
191
|
+
insert into EV (id, v) values (3, 30);
|
|
192
|
+
select id from ea order by id;
|
|
193
|
+
→ [{"id":1},{"id":2},{"id":3}]
|
|
194
|
+
select id from eb order by id;
|
|
195
|
+
→ [{"id":2}]
|
|
196
|
+
select * from EV order by id;
|
|
197
|
+
→ [{"id":1,"v":10},{"id":3,"v":30}]
|
|
198
|
+
|
|
199
|
+
delete from EV where id = 1;
|
|
200
|
+
select id from ea order by id;
|
|
201
|
+
→ [{"id":2},{"id":3}]
|
|
202
|
+
select id from eb order by id;
|
|
203
|
+
→ [{"id":2}]
|
|
204
|
+
|
|
205
|
+
-- ===================================================================
|
|
206
|
+
-- Rejected boundaries: the static surfaces must AGREE with the dynamic reject (no
|
|
207
|
+
-- over-claim). A body the flag-less recognizer rejects falls through to the conservative
|
|
208
|
+
-- all-`NO` row AND the dynamic write rejects.
|
|
209
|
+
-- ===================================================================
|
|
210
|
+
create table rp (id integer primary key, x integer);
|
|
211
|
+
create table rq (id integer primary key, x integer);
|
|
212
|
+
create table rr (id integer primary key, x integer);
|
|
213
|
+
|
|
214
|
+
-- A discriminator-less `union all` is NOT writable (no literal discriminator to route an
|
|
215
|
+
-- insert honestly — the phase-1 reject is preserved): all-`NO` static + dynamic reject.
|
|
216
|
+
create view DLU as select id, x from rp union all select id, x from rq;
|
|
217
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('DLU');
|
|
218
|
+
→ [{"is_insertable_into":"NO","is_updatable":"NO","is_deletable":"NO"}]
|
|
219
|
+
select cid, column_name, is_updatable from column_info('DLU');
|
|
220
|
+
→ [{"cid":0,"column_name":"id","is_updatable":"NO"},{"cid":1,"column_name":"x","is_updatable":"NO"}]
|
|
221
|
+
insert into DLU (id, x) values (1, 10);
|
|
222
|
+
-- error:
|
|
223
|
+
|
|
224
|
+
-- A deep / mixed `intersect` chain is not flattened (binary only) → conservative all-`NO`.
|
|
225
|
+
create view DI as select id, x from rp intersect select id, x from rq intersect select id, x from rr;
|
|
226
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('DI');
|
|
227
|
+
→ [{"is_insertable_into":"NO","is_updatable":"NO","is_deletable":"NO"}]
|
|
228
|
+
|
|
229
|
+
-- ===================================================================
|
|
230
|
+
-- `union` (DISTINCT) of literal-discriminator legs is writable too (the recognizer admits
|
|
231
|
+
-- both `union` and `union all`).
|
|
232
|
+
-- ===================================================================
|
|
233
|
+
create table da (id integer primary key, x integer, color text null);
|
|
234
|
+
create table db (id integer primary key, x integer, color text null);
|
|
235
|
+
insert into da values (1,10,'red'),(2,20,'blue');
|
|
236
|
+
insert into db values (3,30,'red'),(4,40,'blue');
|
|
237
|
+
create view DU as
|
|
238
|
+
select id, x, 'a' as src from da where color = 'red'
|
|
239
|
+
union
|
|
240
|
+
select id, x, 'b' as src from db where color = 'red';
|
|
241
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('DU');
|
|
242
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES"}]
|
|
243
|
+
insert into DU (id, x, src) values (5, 50, 'a');
|
|
244
|
+
select id, color from da order by id;
|
|
245
|
+
→ [{"id":1,"color":"red"},{"id":2,"color":"blue"},{"id":5,"color":"red"}]
|
|
246
|
+
|
|
247
|
+
-- ===================================================================
|
|
248
|
+
-- Partial-discriminator insert ambiguity (the routing-ambiguity caveat): omitting a
|
|
249
|
+
-- discriminator that distinguishes two legs of the SAME base table routes the row to BOTH,
|
|
250
|
+
-- which surfaces as a clean PK conflict (not silent corruption).
|
|
251
|
+
-- ===================================================================
|
|
252
|
+
create table oa (id integer primary key, x integer, color text null, size text null);
|
|
253
|
+
create view OU as
|
|
254
|
+
select id, x, 'red' as kind from oa where color = 'red'
|
|
255
|
+
union all
|
|
256
|
+
select id, x, 'large' as kind from oa where size = 'large';
|
|
257
|
+
insert into OU (id, x) values (7, 70);
|
|
258
|
+
-- error:
|
|
259
|
+
|
|
260
|
+
-- ===================================================================
|
|
261
|
+
-- Multi-source (INNER JOIN) leg compose (`set-op-write-multisource-leg-compose`): a flag-less
|
|
262
|
+
-- set-op body one of whose legs is an INNER join is now writable for UPDATE / DELETE. The
|
|
263
|
+
-- leg recognizer (`isWritableLeafLeg`) admits an inner-join leg, so the static surfaces report
|
|
264
|
+
-- is_updatable / is_deletable = YES (and is_insertable_into = NO — join-leg insert is deferred to
|
|
265
|
+
-- `set-op-write-multisource-leg-insert`). The fan composes the join leg via an inner per-leg
|
|
266
|
+
-- base-PK capture (`__vmupd_keys$N`) chained off the outer set-op capture, so the join leg
|
|
267
|
+
-- decomposes through the multi-source machinery without colliding with the outer capture (the
|
|
268
|
+
-- internal `k.k0_0` error is gone). Both surfaces match the dynamic write exactly.
|
|
269
|
+
-- ===================================================================
|
|
270
|
+
create table jv1 (id integer primary key, x integer, color text null);
|
|
271
|
+
create table jv2 (id integer primary key, y integer null);
|
|
272
|
+
insert into jv1 values (1,10,'red'),(2,20,'blue');
|
|
273
|
+
insert into jv2 values (1,100),(2,200);
|
|
274
|
+
create view JV as
|
|
275
|
+
select jv1.id as id, jv1.x as x, 'a' as src from jv1 join jv2 on jv1.id = jv2.id where jv1.color = 'red'
|
|
276
|
+
union all
|
|
277
|
+
select id, x, 'b' as src from jv1 where color = 'blue';
|
|
278
|
+
|
|
279
|
+
select * from JV order by id;
|
|
280
|
+
→ [{"id":1,"x":10,"src":"a"},{"id":2,"x":20,"src":"b"}]
|
|
281
|
+
|
|
282
|
+
-- static surfaces: update/delete YES (the join leg composes), insert YES (the join leg's shared key
|
|
283
|
+
-- `id` is supplied, so its shared-surrogate envelope accepts the insert). The plain data columns
|
|
284
|
+
-- report is_updatable = YES; the literal discriminator `src` is read-only (NO).
|
|
285
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('JV');
|
|
286
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES"}]
|
|
287
|
+
select cid, column_name, is_updatable, base_table, base_column from column_info('JV');
|
|
288
|
+
→ [{"cid":0,"column_name":"id","is_updatable":"YES","base_table":null,"base_column":null},{"cid":1,"column_name":"x","is_updatable":"YES","base_table":null,"base_column":null},{"cid":2,"column_name":"src","is_updatable":"NO","base_table":null,"base_column":null}]
|
|
289
|
+
|
|
290
|
+
-- data UPDATE through the join leg ('a' leg): `x` maps to jv1.x, so only jv1 is updated; the
|
|
291
|
+
-- discriminator σ routes the fan to the join leg ('b' leg is unsat with src='a' ⇒ skipped).
|
|
292
|
+
update JV set x = x + 1 where src = 'a';
|
|
293
|
+
select id, x from jv1 order by id;
|
|
294
|
+
→ [{"id":1,"x":11},{"id":2,"x":20}]
|
|
295
|
+
select id, y from jv2 order by id;
|
|
296
|
+
→ [{"id":1,"y":100},{"id":2,"y":200}]
|
|
297
|
+
|
|
298
|
+
-- DELETE through the join leg fans to both candidate sides of the inner join (ambiguous-delete
|
|
299
|
+
-- default), so the joined row id=1 leaves both jv1 and jv2.
|
|
300
|
+
delete from JV where src = 'a';
|
|
301
|
+
select id, x from jv1 order by id;
|
|
302
|
+
→ [{"id":2,"x":20}]
|
|
303
|
+
select id, y from jv2 order by id;
|
|
304
|
+
→ [{"id":2,"y":200}]
|
|
305
|
+
|
|
306
|
+
-- INSERT through the join leg is now SHIPPED via the nested shared-surrogate envelope splice
|
|
307
|
+
-- (`set-op-write-multisource-leg-insert`): the discriminator `src='a'` routes to the join leg, whose
|
|
308
|
+
-- shared key `id` is supplied (jv1.id = jv2.id), so jv1 gets (5,50,'red') and jv2 gets (5,null). The
|
|
309
|
+
-- 'b' plain leg is unsat with src='a' ⇒ untouched. The join leg's `where jv1.color='red'` IS now
|
|
310
|
+
-- consulted on insert (multisource-insert-sigma-fd-defaulting): the σ equality constant is lifted as
|
|
311
|
+
-- a per-side insert default, so the omitted, projected-away `color` is supplied 'red' on jv1 and the
|
|
312
|
+
-- inserted row is VISIBLE through JV (matching the single-source σ-defaulting rule).
|
|
313
|
+
insert into JV (id, x, src) values (5, 50, 'a');
|
|
314
|
+
select id, x, color from jv1 order by id;
|
|
315
|
+
→ [{"id":2,"x":20,"color":"blue"},{"id":5,"x":50,"color":"red"}]
|
|
316
|
+
select id, y from jv2 order by id;
|
|
317
|
+
→ [{"id":2,"y":200},{"id":5,"y":null}]
|
|
318
|
+
-- the inserted (5,50,'a') row now appears THROUGH the view (jv1.color defaulted to 'red', so the
|
|
319
|
+
-- 'a' join leg's `where color='red'` admits it).
|
|
320
|
+
select * from JV order by id;
|
|
321
|
+
→ [{"id":2,"x":20,"src":"b"},{"id":5,"x":50,"src":"a"}]
|
|
322
|
+
|
|
323
|
+
-- NOTE on comma-join legs (`from a, b`): the engine rejects multiple FROM sources at build
|
|
324
|
+
-- time ("SELECT with multiple FROM sources (joins) not supported"), so such a body cannot even
|
|
325
|
+
-- be created/read — the `from.length > 1` arm of `isJoinBody` is unreachable for any readable
|
|
326
|
+
-- view. The explicit-`join` form above is the live path.
|
|
327
|
+
|
|
328
|
+
-- ===================================================================
|
|
329
|
+
-- Join leg at chain depth > 1 (`set-op-write-multisource-leg-compose`, every-depth coverage):
|
|
330
|
+
-- a flag-less union-all chain whose THIRD leg is a JOIN. The fan walks every flat leg, so a join
|
|
331
|
+
-- at any depth composes its own inner capture and decomposes correctly.
|
|
332
|
+
-- ===================================================================
|
|
333
|
+
create table dv1 (id integer primary key, x integer, color text null);
|
|
334
|
+
create table dv2 (id integer primary key, y integer null);
|
|
335
|
+
insert into dv1 values (1,10,'red'),(2,20,'blue'),(3,30,'red');
|
|
336
|
+
insert into dv2 values (1,100),(3,300);
|
|
337
|
+
create view DV as
|
|
338
|
+
select id, x, 'a' as src from dv1 where color = 'red'
|
|
339
|
+
union all
|
|
340
|
+
select id, x, 'b' as src from dv1 where color = 'blue'
|
|
341
|
+
union all
|
|
342
|
+
select dv1.id as id, dv1.x as x, 'c' as src from dv1 join dv2 on dv1.id = dv2.id where dv1.color = 'red';
|
|
343
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('DV');
|
|
344
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES"}]
|
|
345
|
+
select id, x, src from DV order by src, id;
|
|
346
|
+
→ [{"id":1,"x":10,"src":"a"},{"id":3,"x":30,"src":"a"},{"id":2,"x":20,"src":"b"},{"id":1,"x":10,"src":"c"},{"id":3,"x":30,"src":"c"}]
|
|
347
|
+
|
|
348
|
+
-- DELETE through the depth-3 join leg ('c'): the σ-router fans only to that leg (the 'a'/'b' legs
|
|
349
|
+
-- are unsat with src='c'), and it deletes the joined rows (id 1,3) from both dv1 and dv2.
|
|
350
|
+
delete from DV where src = 'c';
|
|
351
|
+
select id, x from dv1 order by id;
|
|
352
|
+
→ [{"id":2,"x":20}]
|
|
353
|
+
select id, y from dv2 order by id;
|
|
354
|
+
→ []
|
|
355
|
+
|
|
356
|
+
-- INSERT through the deep (depth-3) join leg is now SHIPPED via the nested envelope splice: `src='c'`
|
|
357
|
+
-- routes to the join leg, shared key `id` supplied, so dv1 gets (9,90,null) and dv2 gets (9,null).
|
|
358
|
+
insert into DV (id, x, src) values (9, 90, 'c');
|
|
359
|
+
select id, x from dv1 order by id;
|
|
360
|
+
→ [{"id":2,"x":20},{"id":9,"x":90}]
|
|
361
|
+
select id, y from dv2 order by id;
|
|
362
|
+
→ [{"id":9,"y":null}]
|
|
363
|
+
|
|
364
|
+
-- ===================================================================
|
|
365
|
+
-- Composite-PK join leg (`set-op-write-multisource-leg-compose`): the inner per-leg capture
|
|
366
|
+
-- projects one column per PK column per side (`k<side>_0, k<side>_1`), so a composite-key join
|
|
367
|
+
-- leg decomposes through the multi-source machinery unchanged.
|
|
368
|
+
-- ===================================================================
|
|
369
|
+
create table cp1 (a integer, b integer, v integer, primary key (a, b));
|
|
370
|
+
create table cp2 (a integer, b integer, w integer, primary key (a, b));
|
|
371
|
+
insert into cp1 values (1,1,10),(1,2,20);
|
|
372
|
+
insert into cp2 values (1,1,100),(1,2,200);
|
|
373
|
+
create view CV as
|
|
374
|
+
select cp1.a as a, cp1.b as b, cp1.v as v, 'j' as src from cp1 join cp2 on cp1.a = cp2.a and cp1.b = cp2.b
|
|
375
|
+
union all
|
|
376
|
+
select a, b, v, 's' as src from cp1 where a = 9;
|
|
377
|
+
select cid, column_name, is_updatable from column_info('CV');
|
|
378
|
+
→ [{"cid":0,"column_name":"a","is_updatable":"YES"},{"cid":1,"column_name":"b","is_updatable":"YES"},{"cid":2,"column_name":"v","is_updatable":"YES"},{"cid":3,"column_name":"src","is_updatable":"NO"}]
|
|
379
|
+
|
|
380
|
+
-- INSERT through the composite-PK join leg stays deferred (the shared-surrogate envelope threads a
|
|
381
|
+
-- SINGLE shared key column): `is_insertable_into` reports NO (the probe sees the composite key), and
|
|
382
|
+
-- the dynamic insert rejects cleanly with `unsupported-decomposition-key`. UPDATE / DELETE compose.
|
|
383
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('CV');
|
|
384
|
+
→ [{"is_insertable_into":"NO","is_updatable":"YES","is_deletable":"YES"}]
|
|
385
|
+
insert into CV (a, b, v, src) values (9, 9, 90, 'j');
|
|
386
|
+
-- error: composite shared key
|
|
387
|
+
|
|
388
|
+
-- data UPDATE through the composite-PK join leg: `v` maps to cp1.v (identified by the composite key).
|
|
389
|
+
update CV set v = v + 1 where a = 1 and b = 1 and src = 'j';
|
|
390
|
+
select a, b, v from cp1 order by a, b;
|
|
391
|
+
→ [{"a":1,"b":1,"v":11},{"a":1,"b":2,"v":20}]
|
|
392
|
+
|
|
393
|
+
-- DELETE through the composite-PK join leg fans to both sides (composite identities captured per side).
|
|
394
|
+
delete from CV where a = 1 and b = 2 and src = 'j';
|
|
395
|
+
select a, b, v from cp1 order by a, b;
|
|
396
|
+
→ [{"a":1,"b":1,"v":11}]
|
|
397
|
+
select a, b, w from cp2 order by a, b;
|
|
398
|
+
→ [{"a":1,"b":1,"w":100}]
|
|
399
|
+
|
|
400
|
+
-- ===================================================================
|
|
401
|
+
-- Self-join leg (`set-op-write-multisource-leg-compose`): one base table under two aliases. The
|
|
402
|
+
-- inner capture routes by alias, so a data UPDATE through the self-join leg lands on the aliased
|
|
403
|
+
-- owning side (`name` ⇒ `e.name`), not the manager alias.
|
|
404
|
+
-- ===================================================================
|
|
405
|
+
create table emp (id integer primary key, name text, mgr integer);
|
|
406
|
+
insert into emp values (1,'a',1),(2,'b',1),(3,'c',2);
|
|
407
|
+
create view SJ as
|
|
408
|
+
select e.id as id, e.name as name, m.name as mgrname, 'pair' as src from emp e join emp m on e.mgr = m.id
|
|
409
|
+
union all
|
|
410
|
+
select id, name, name as mgrname, 'solo' as src from emp where id = 99;
|
|
411
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('SJ');
|
|
412
|
+
→ [{"is_insertable_into":"NO","is_updatable":"YES","is_deletable":"YES"}]
|
|
413
|
+
|
|
414
|
+
-- INSERT through the self-join leg stays deferred: the shared key `e.mgr` is neither supplied nor
|
|
415
|
+
-- declares a DEFAULT, so the shared-surrogate envelope cannot mint it. The probe reports NO (above)
|
|
416
|
+
-- and the dynamic insert rejects cleanly with `no-default`. UPDATE / DELETE compose.
|
|
417
|
+
insert into SJ (id, name, mgrname, src) values (4, 'd', 'e', 'pair');
|
|
418
|
+
-- error: neither supplied nor declares a default
|
|
419
|
+
|
|
420
|
+
update SJ set name = 'X' where id = 2 and src = 'pair';
|
|
421
|
+
select id, name from emp order by id;
|
|
422
|
+
→ [{"id":1,"name":"a"},{"id":2,"name":"X"},{"id":3,"name":"c"}]
|
|
423
|
+
|
|
424
|
+
-- ===================================================================
|
|
425
|
+
-- Mixed body — one JOIN leg + one PLAIN leg both active on an INSERT (`set-op-write-multisource-leg-insert`):
|
|
426
|
+
-- a union-all of a join leg ('j') and a plain leg ('p'). An INSERT that OMITS the discriminator is
|
|
427
|
+
-- consistent with BOTH legs, so it routes to each: the join leg via a nested shared-surrogate envelope
|
|
428
|
+
-- (mxj1+mxj2), the plain leg via the single-source spine (mxp). Both land in fan order.
|
|
429
|
+
-- ===================================================================
|
|
430
|
+
create table mxj1 (id integer primary key, x integer);
|
|
431
|
+
create table mxj2 (id integer primary key);
|
|
432
|
+
create table mxp (id integer primary key, x integer);
|
|
433
|
+
insert into mxj1 values (1,10);
|
|
434
|
+
insert into mxj2 values (1);
|
|
435
|
+
insert into mxp values (1,10);
|
|
436
|
+
create view MXV as
|
|
437
|
+
select mxj1.id as id, mxj1.x as x, 'j' as src from mxj1 join mxj2 on mxj1.id = mxj2.id
|
|
438
|
+
union all
|
|
439
|
+
select id, x, 'p' as src from mxp;
|
|
440
|
+
|
|
441
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('MXV');
|
|
442
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES"}]
|
|
443
|
+
|
|
444
|
+
select id, x, src from MXV order by src, id;
|
|
445
|
+
→ [{"id":1,"x":10,"src":"j"},{"id":1,"x":10,"src":"p"}]
|
|
446
|
+
|
|
447
|
+
-- Omit `src`: the row (5,50) is consistent with BOTH legs, so it lands in the join leg's base tables
|
|
448
|
+
-- (mxj1+mxj2 via one nested envelope) AND the plain leg's base table (mxp).
|
|
449
|
+
insert into MXV (id, x) values (5, 50);
|
|
450
|
+
select id, x from mxj1 order by id;
|
|
451
|
+
→ [{"id":1,"x":10},{"id":5,"x":50}]
|
|
452
|
+
select id from mxj2 order by id;
|
|
453
|
+
→ [{"id":1},{"id":5}]
|
|
454
|
+
select id, x from mxp order by id;
|
|
455
|
+
→ [{"id":1,"x":10},{"id":5,"x":50}]
|
|
456
|
+
|
|
457
|
+
-- `insert or ignore` through the join leg threads `onConflict` into the per-leg envelope insert:
|
|
458
|
+
-- a row colliding on the existing join-leg PK (mxj1 id=5) is ignored (no error, no change), while a
|
|
459
|
+
-- fresh non-colliding row still lands (`set-op-write-multisource-leg-insert`).
|
|
460
|
+
insert or ignore into MXV (id, x, src) values (5, 999, 'j');
|
|
461
|
+
select id, x from mxj1 order by id;
|
|
462
|
+
→ [{"id":1,"x":10},{"id":5,"x":50}]
|
|
463
|
+
insert or ignore into MXV (id, x, src) values (6, 60, 'j');
|
|
464
|
+
select id, x from mxj1 order by id;
|
|
465
|
+
→ [{"id":1,"x":10},{"id":5,"x":50},{"id":6,"x":60}]
|
|
466
|
+
select id from mxj2 order by id;
|
|
467
|
+
→ [{"id":1},{"id":5},{"id":6}]
|
|
468
|
+
|
|
469
|
+
-- ===================================================================
|
|
470
|
+
-- Uncovered NOT NULL non-key column on a join leg ⇒ NOT insertable (the probe-reject reason the
|
|
471
|
+
-- `jv2.y integer null` fixture above sidesteps). `nn2.y` is a bare `integer` (NOT NULL, no default)
|
|
472
|
+
-- not exposed as a view column, so the shared-surrogate envelope cannot supply it: `is_insertable_into`
|
|
473
|
+
-- reports NO and the dynamic insert rejects cleanly. UPDATE / DELETE through the join leg stay YES.
|
|
474
|
+
-- ===================================================================
|
|
475
|
+
create table nn1 (id integer primary key, x integer, color text null);
|
|
476
|
+
create table nn2 (id integer primary key, y integer);
|
|
477
|
+
insert into nn1 values (1,10,'red');
|
|
478
|
+
insert into nn2 values (1,100);
|
|
479
|
+
create view NNV as
|
|
480
|
+
select nn1.id as id, nn1.x as x, 'a' as src from nn1 join nn2 on nn1.id = nn2.id
|
|
481
|
+
union all
|
|
482
|
+
select id, x, 'b' as src from nn1 where 0;
|
|
483
|
+
|
|
484
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('NNV');
|
|
485
|
+
→ [{"is_insertable_into":"NO","is_updatable":"YES","is_deletable":"YES"}]
|
|
486
|
+
insert into NNV (id, x, src) values (5, 50, 'a');
|
|
487
|
+
-- error: is NOT NULL with no default
|
|
488
|
+
|
|
489
|
+
-- ===================================================================
|
|
490
|
+
-- Halloween safety through a join leg (`set-op-write-multisource-leg-compose`): the inner capture
|
|
491
|
+
-- is materialized BEFORE the leg's base ops, so an UPDATE that rewrites a column its own member
|
|
492
|
+
-- filter ranged on still matches every captured row by identity (no row escapes mid-update).
|
|
493
|
+
-- ===================================================================
|
|
494
|
+
create table hk1 (id integer primary key, grp integer, x integer);
|
|
495
|
+
create table hk2 (id integer primary key, y integer);
|
|
496
|
+
insert into hk1 values (1,5,10),(2,5,20);
|
|
497
|
+
insert into hk2 values (1,100),(2,200);
|
|
498
|
+
create view HV as
|
|
499
|
+
select hk1.id as id, hk1.grp as grp, hk1.x as x, 'j' as src from hk1 join hk2 on hk1.id = hk2.id
|
|
500
|
+
union all
|
|
501
|
+
select id, grp, x, 's' as src from hk1 where grp = 9;
|
|
502
|
+
update HV set grp = grp + 1 where grp = 5 and src = 'j';
|
|
503
|
+
select id, grp, x from hk1 order by id;
|
|
504
|
+
→ [{"id":1,"grp":6,"x":10},{"id":2,"grp":6,"x":20}]
|
|
505
|
+
|
|
506
|
+
-- ===================================================================
|
|
507
|
+
-- OUTER (left/right/full) join leg deferral (`set-op-write-multisource-leg-compose` ships INNER
|
|
508
|
+
-- legs only): an outer-join leg drops the whole body out of the flag-less route (its
|
|
509
|
+
-- `isWritableLeafLeg` is false), so the static surfaces report all-`NO` and the dynamic write
|
|
510
|
+
-- falls to the single-source spine's clean reject — never the internal `k.k0_0` capture error.
|
|
511
|
+
-- ===================================================================
|
|
512
|
+
create table oj1 (id integer primary key, x integer, color text);
|
|
513
|
+
create table oj2 (id integer primary key, y integer);
|
|
514
|
+
insert into oj1 values (1,10,'red'),(2,20,'blue');
|
|
515
|
+
insert into oj2 values (1,100);
|
|
516
|
+
create view OJV as
|
|
517
|
+
select oj1.id as id, oj1.x as x, 'a' as src from oj1 left join oj2 on oj1.id = oj2.id where oj1.color = 'red'
|
|
518
|
+
union all
|
|
519
|
+
select id, x, 'b' as src from oj1 where color = 'blue';
|
|
520
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('OJV');
|
|
521
|
+
→ [{"is_insertable_into":"NO","is_updatable":"NO","is_deletable":"NO"}]
|
|
522
|
+
update OJV set x = x + 1 where src = 'a';
|
|
523
|
+
-- error: cannot write through view
|
|
524
|
+
delete from OJV where src = 'a';
|
|
525
|
+
-- error: cannot write through view
|
|
526
|
+
-- base tables untouched.
|
|
527
|
+
select id, x from oj1 order by id;
|
|
528
|
+
→ [{"id":1,"x":10},{"id":2,"x":20}]
|
|
529
|
+
|
|
530
|
+
-- ===================================================================
|
|
531
|
+
-- Missing-discriminator deferral (union-all body, non-equi INNER join leg, no literal
|
|
532
|
+
-- discriminator): the body has no literal discriminator in either leg, so `flaglessShape`
|
|
533
|
+
-- returns null — INSERT routing is ambiguous. The static surfaces report all-`NO` and the
|
|
534
|
+
-- dynamic write rejects with `unsupported-set-op`.
|
|
535
|
+
-- Note: the all-`NO` is NOT because the join is non-equi — `isWritableLeafLeg` admits a
|
|
536
|
+
-- non-equi INNER join identically to an equi-join. The deferral is entirely due to the
|
|
537
|
+
-- missing literal discriminator in a union-all body.
|
|
538
|
+
-- ===================================================================
|
|
539
|
+
create table ne1 (id integer primary key, x integer);
|
|
540
|
+
create table ne2 (id integer primary key, lo integer, hi integer);
|
|
541
|
+
insert into ne1 values (1,10),(2,20);
|
|
542
|
+
insert into ne2 values (1,5,15),(2,5,15);
|
|
543
|
+
create view NEV as
|
|
544
|
+
select ne1.id as id, ne1.x as x from ne1 join ne2 on ne1.x > ne2.lo and ne1.x < ne2.hi
|
|
545
|
+
union all
|
|
546
|
+
select id, x from ne1 where id = 99;
|
|
547
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('NEV');
|
|
548
|
+
→ [{"is_insertable_into":"NO","is_updatable":"NO","is_deletable":"NO"}]
|
|
549
|
+
update NEV set x = x + 1 where id = 1;
|
|
550
|
+
-- error: cannot write through view
|
|
551
|
+
delete from NEV where id = 1;
|
|
552
|
+
-- error: cannot write through view
|
|
553
|
+
-- base tables untouched.
|
|
554
|
+
select id, x from ne1 order by id;
|
|
555
|
+
→ [{"id":1,"x":10},{"id":2,"x":20}]
|
|
556
|
+
|
|
557
|
+
-- ===================================================================
|
|
558
|
+
-- Positive: non-equi INNER join leg WITH a literal discriminator (`set-op-flagless-write`).
|
|
559
|
+
-- `isWritableLeafLeg` admits a non-equi INNER join (keys only on `joinType`), and the literal
|
|
560
|
+
-- `tag` discriminator resolves the insert-routing ambiguity. So this body is writable for
|
|
561
|
+
-- UPDATE / DELETE (is_updatable=YES, is_deletable=YES). INSERT is deferred precisely BECAUSE the
|
|
562
|
+
-- join is non-equi: `setOpJoinLegsInsertable` probes the leg with `analyzeMultiSourceInsert`,
|
|
563
|
+
-- which rejects a non-equi ON (`unsupported-join`) → is_insertable_into=NO. An equi-join leg with
|
|
564
|
+
-- a qualifying shared key WOULD be insertable here (cf. the equi-join MXV body above, which is YES).
|
|
565
|
+
-- Only id=1 is visible through the view (x=10 satisfies 5 < x < 15); id=2 (x=20) is not.
|
|
566
|
+
-- ===================================================================
|
|
567
|
+
create view NEVD as
|
|
568
|
+
select ne1.id as id, ne1.x as x, 'ne' as tag from ne1 join ne2 on ne1.x > ne2.lo and ne1.x < ne2.hi
|
|
569
|
+
union all
|
|
570
|
+
select id, x, 'plain' as tag from ne1 where id = 99;
|
|
571
|
+
select is_insertable_into, is_updatable, is_deletable from view_info('NEVD');
|
|
572
|
+
→ [{"is_insertable_into":"NO","is_updatable":"YES","is_deletable":"YES"}]
|
|
573
|
+
|
|
574
|
+
-- UPDATE through the non-equi join leg: id=1 is visible (x=10 is in range), update x.
|
|
575
|
+
update NEVD set x = 11 where id = 1;
|
|
576
|
+
select id, x from ne1 order by id;
|
|
577
|
+
→ [{"id":1,"x":11},{"id":2,"x":20}]
|
|
578
|
+
|
|
579
|
+
-- DELETE through the non-equi join leg: id=1 is now x=11 (still in range), delete it.
|
|
580
|
+
delete from NEVD where id = 1;
|
|
581
|
+
select id, x from ne1 order by id;
|
|
582
|
+
→ [{"id":2,"x":20}]
|
|
583
|
+
|
|
584
|
+
-- ===================================================================
|
|
585
|
+
-- Range-σ write-branch oracle (`set-op-flagless-range-sigma-oracle`): a RANGE σ
|
|
586
|
+
-- (`where x < 5`) on a *projected* column is now fed to the oracle as a conjunct, so a
|
|
587
|
+
-- supplied value that provably violates it makes the leg `unsat` ⇒ skipped (no phantom base
|
|
588
|
+
-- row). A literal discriminator (`bucket`) keeps the body writable; INSERTs OMIT it so the
|
|
589
|
+
-- range σ — not the discriminator — drives the routing decision. See
|
|
590
|
+
-- docs/vu-setops.md § Set-operation membership writes.
|
|
591
|
+
-- ===================================================================
|
|
592
|
+
create table rlo (id integer primary key, x integer);
|
|
593
|
+
create table rhi (id integer primary key, x integer);
|
|
594
|
+
insert into rlo values (1,1),(2,3);
|
|
595
|
+
insert into rhi values (3,7),(4,9);
|
|
596
|
+
create view RV as
|
|
597
|
+
select id, x, 'lo' as bucket from rlo where x < 5
|
|
598
|
+
union all
|
|
599
|
+
select id, x, 'hi' as bucket from rhi where x >= 5;
|
|
600
|
+
|
|
601
|
+
select * from RV order by id;
|
|
602
|
+
→ [{"id":1,"x":1,"bucket":"lo"},{"id":2,"x":3,"bucket":"lo"},{"id":3,"x":7,"bucket":"hi"},{"id":4,"x":9,"bucket":"hi"}]
|
|
603
|
+
|
|
604
|
+
-- Out-of-range INSERT (the fix): x=100 provably violates the `lo` leg's `x < 5` → that leg
|
|
605
|
+
-- is skipped (rlo gets NO phantom row); it is consistent with the `hi` leg's `x >= 5` → routes
|
|
606
|
+
-- to rhi only. (Without the fix the range σ was invisible to the oracle and rlo got a phantom.)
|
|
607
|
+
insert into RV (id, x) values (10, 100);
|
|
608
|
+
select id, x from rlo order by id;
|
|
609
|
+
→ [{"id":1,"x":1},{"id":2,"x":3}]
|
|
610
|
+
select id, x from rhi order by id;
|
|
611
|
+
→ [{"id":3,"x":7},{"id":4,"x":9},{"id":10,"x":100}]
|
|
612
|
+
select * from RV where id = 10;
|
|
613
|
+
→ [{"id":10,"x":100,"bucket":"hi"}]
|
|
614
|
+
|
|
615
|
+
-- Complementary in-range INSERT: x=2 violates `x >= 5` (hi skipped) and satisfies `x < 5`
|
|
616
|
+
-- (lo included) → routes to rlo only.
|
|
617
|
+
insert into RV (id, x) values (11, 2);
|
|
618
|
+
select id, x from rhi order by id;
|
|
619
|
+
→ [{"id":3,"x":7},{"id":4,"x":9},{"id":10,"x":100}]
|
|
620
|
+
select id, x from rlo order by id;
|
|
621
|
+
→ [{"id":1,"x":1},{"id":2,"x":3},{"id":11,"x":2}]
|
|
622
|
+
select * from RV where id = 11;
|
|
623
|
+
→ [{"id":11,"x":2,"bucket":"lo"}]
|
|
624
|
+
|
|
625
|
+
-- DELETE through a range σ: the WHERE `x < 3` is consistent with the `lo` leg but provably
|
|
626
|
+
-- `unsat` with the `hi` leg (`x < 3 ∧ x >= 5`) → the fan NARROWS to rlo only, and the
|
|
627
|
+
-- member-exists capture restricts the delete to the resident matching rows (rlo id 1 & 11).
|
|
628
|
+
-- rhi is untouched; the observable result is exactly the resident rows.
|
|
629
|
+
delete from RV where x < 3;
|
|
630
|
+
select id, x from rlo order by id;
|
|
631
|
+
→ [{"id":2,"x":3}]
|
|
632
|
+
select id, x from rhi order by id;
|
|
633
|
+
→ [{"id":3,"x":7},{"id":4,"x":9},{"id":10,"x":100}]
|
|
634
|
+
|
|
635
|
+
-- UPDATE through a range σ (the delete-companion the ticket asked for): WHERE `x >= 9` is
|
|
636
|
+
-- provably `unsat` with the `lo` leg (`x >= 9 ∧ x < 5`) → the fan NARROWS to rhi, and the
|
|
637
|
+
-- member-exists capture restricts to the resident matching rows (rhi id 4 & 10). The SET
|
|
638
|
+
-- target (`id`) does NOT touch the range column, so no Halloween reclassification. rlo is
|
|
639
|
+
-- untouched (never even consulted — its leg is skipped at routing time).
|
|
640
|
+
update RV set id = id + 100 where x >= 9;
|
|
641
|
+
select id, x from rlo order by id;
|
|
642
|
+
→ [{"id":2,"x":3}]
|
|
643
|
+
select id, x from rhi order by id;
|
|
644
|
+
→ [{"id":3,"x":7},{"id":104,"x":9},{"id":110,"x":100}]
|
|
645
|
+
|
|
646
|
+
-- ===================================================================
|
|
647
|
+
-- Gap-value INSERT: legs with a GAP (`x < 5` / `x >= 10`) reject a value that falls in the
|
|
648
|
+
-- gap — both legs are provably `unsat`, so `baseOps` is empty and the honest
|
|
649
|
+
-- `consistent with no writable leg` diagnostic fires (NOT a silent no-op). Base tables untouched.
|
|
650
|
+
-- ===================================================================
|
|
651
|
+
create table glo (id integer primary key, x integer);
|
|
652
|
+
create table ghi (id integer primary key, x integer);
|
|
653
|
+
insert into glo values (1,1);
|
|
654
|
+
insert into ghi values (2,20);
|
|
655
|
+
create view GV as
|
|
656
|
+
select id, x, 'lo' as bucket from glo where x < 5
|
|
657
|
+
union all
|
|
658
|
+
select id, x, 'hi' as bucket from ghi where x >= 10;
|
|
659
|
+
|
|
660
|
+
select * from GV order by id;
|
|
661
|
+
→ [{"id":1,"x":1,"bucket":"lo"},{"id":2,"x":20,"bucket":"hi"}]
|
|
662
|
+
|
|
663
|
+
insert into GV (id, x) values (9, 7);
|
|
664
|
+
-- error: consistent with no writable leg
|
|
665
|
+
select id, x from glo order by id;
|
|
666
|
+
→ [{"id":1,"x":1}]
|
|
667
|
+
select id, x from ghi order by id;
|
|
668
|
+
→ [{"id":2,"x":20}]
|
|
669
|
+
|
|
670
|
+
-- ===================================================================
|
|
671
|
+
-- BETWEEN-σ leg: `checkSatisfiability`'s positive-BETWEEN handling absorbs `x between 0 and 4`,
|
|
672
|
+
-- so an out-of-band supplied value (`x = 50`) makes that leg `unsat` ⇒ skipped; an in-band
|
|
673
|
+
-- value (`x = 3`) routes into it.
|
|
674
|
+
-- ===================================================================
|
|
675
|
+
create table btw (id integer primary key, x integer);
|
|
676
|
+
create table oth (id integer primary key, x integer);
|
|
677
|
+
insert into btw values (1,2);
|
|
678
|
+
insert into oth values (2,50);
|
|
679
|
+
create view BV as
|
|
680
|
+
select id, x, 'in' as bucket from btw where x between 0 and 4
|
|
681
|
+
union all
|
|
682
|
+
select id, x, 'out' as bucket from oth where x > 10;
|
|
683
|
+
|
|
684
|
+
select * from BV order by id;
|
|
685
|
+
→ [{"id":1,"x":2,"bucket":"in"},{"id":2,"x":50,"bucket":"out"}]
|
|
686
|
+
|
|
687
|
+
-- x=50 is out of `[0,4]` → `in` leg skipped; consistent with `x > 10` → routes to oth.
|
|
688
|
+
insert into BV (id, x) values (9, 50);
|
|
689
|
+
select id, x from btw order by id;
|
|
690
|
+
→ [{"id":1,"x":2}]
|
|
691
|
+
select id, x from oth order by id;
|
|
692
|
+
→ [{"id":2,"x":50},{"id":9,"x":50}]
|
|
693
|
+
|
|
694
|
+
-- x=3 is within `[0,4]` → routes to btw; provably violates `x > 10` → oth skipped.
|
|
695
|
+
insert into BV (id, x) values (8, 3);
|
|
696
|
+
select id, x from btw order by id;
|
|
697
|
+
→ [{"id":1,"x":2},{"id":8,"x":3}]
|
|
698
|
+
select id, x from oth order by id;
|
|
699
|
+
→ [{"id":2,"x":50},{"id":9,"x":50}]
|
|
700
|
+
|
|
701
|
+
-- ===================================================================
|
|
702
|
+
-- Product-coordinate addressing via projected-constant discriminators
|
|
703
|
+
-- (`set-op-projected-constant-product-coordinate`): a body with MULTIPLE literal
|
|
704
|
+
-- discriminator columns forms a discriminator grid — here `kind ∈ {red,large}` × `src ∈ {A,B}`,
|
|
705
|
+
-- the `{inX,inY} × {inA,inB}` product grid the shelved product-coordinate model targeted. A write
|
|
706
|
+
-- addresses a coordinate by filtering on those READ-ONLY discriminator columns:
|
|
707
|
+
-- • both axes pinned (`kind='red' and src='A'`) → writes the one co-satisfiable leg
|
|
708
|
+
-- • one axis pinned (`kind='red'`) → fans to every consistent leg
|
|
709
|
+
-- • same-axis contradiction (`kind='red' and kind='large'`) / off-grid value → clean no-op
|
|
710
|
+
-- This is the recommended surface for product-coordinate write-through — the product *use case*
|
|
711
|
+
-- is served by the shipped flag-less write path, with no bespoke writable-membership build needed.
|
|
712
|
+
-- (Fresh `pca`/`pcb` fixtures: the same two-axis grid as `U`/`U4`, self-contained so the matrix
|
|
713
|
+
-- assertions do not depend on the mutated state of the views above.) See
|
|
714
|
+
-- docs/vu-setops.md § Set-operation membership writes.
|
|
715
|
+
-- ===================================================================
|
|
716
|
+
create table pca (id integer primary key, x integer, color text null, size text null);
|
|
717
|
+
create table pcb (id integer primary key, x integer, color text null, size text null);
|
|
718
|
+
insert into pca values (1,10,'red','small'),(2,20,'blue','large');
|
|
719
|
+
insert into pcb values (3,30,'red','small'),(4,40,'blue','large');
|
|
720
|
+
create view PC as
|
|
721
|
+
select id, x, 'red' as kind, 'A' as src from pca where color = 'red'
|
|
722
|
+
union all
|
|
723
|
+
select id, x, 'red' as kind, 'B' as src from pcb where color = 'red'
|
|
724
|
+
union all
|
|
725
|
+
select id, x, 'large' as kind, 'A' as src from pca where size = 'large'
|
|
726
|
+
union all
|
|
727
|
+
select id, x, 'large' as kind, 'B' as src from pcb where size = 'large';
|
|
728
|
+
|
|
729
|
+
select * from PC order by id;
|
|
730
|
+
→ [{"id":1,"x":10,"kind":"red","src":"A"},{"id":2,"x":20,"kind":"large","src":"A"},{"id":3,"x":30,"kind":"red","src":"B"},{"id":4,"x":40,"kind":"large","src":"B"}]
|
|
731
|
+
|
|
732
|
+
-- static surface: a view that CAN produce a zero-leg write at runtime is still statically writable
|
|
733
|
+
-- (the no-op is a per-predicate runtime outcome, not a shape rejection). Plain columns YES;
|
|
734
|
+
-- discriminators read-only (NO). (Must agree with U above — same grid shape.)
|
|
735
|
+
select is_insertable_into, is_updatable, is_deletable, effective_targets from view_info('PC');
|
|
736
|
+
→ [{"is_insertable_into":"YES","is_updatable":"YES","is_deletable":"YES","effective_targets":"[\"pca\",\"pcb\"]"}]
|
|
737
|
+
select cid, column_name, is_updatable, base_table, base_column from column_info('PC');
|
|
738
|
+
→ [{"cid":0,"column_name":"id","is_updatable":"YES","base_table":null,"base_column":null},{"cid":1,"column_name":"x","is_updatable":"YES","base_table":null,"base_column":null},{"cid":2,"column_name":"kind","is_updatable":"NO","base_table":null,"base_column":null},{"cid":3,"column_name":"src","is_updatable":"NO","base_table":null,"base_column":null}]
|
|
739
|
+
|
|
740
|
+
-- --- Cross-axis pin: both axes specified → the one co-satisfiable leg (pca red) only ---
|
|
741
|
+
-- `kind='red' and src='A'` is sat ONLY with the `red,A` leg (`from pca where color='red'`); the
|
|
742
|
+
-- other three legs each contradict one axis ⇒ unsat ⇒ skipped. So only pca id 1 (the red row) is
|
|
743
|
+
-- touched; pca id 2 (large) and all of pcb are untouched.
|
|
744
|
+
update PC set x = x + 100 where kind = 'red' and src = 'A';
|
|
745
|
+
select id, x from pca order by id;
|
|
746
|
+
→ [{"id":1,"x":110},{"id":2,"x":20}]
|
|
747
|
+
select id, x from pcb order by id;
|
|
748
|
+
→ [{"id":3,"x":30},{"id":4,"x":40}]
|
|
749
|
+
|
|
750
|
+
-- --- Single-axis fan (UPDATE companion to the existing one-axis DELETE/UPDATE fans): `kind='red'`
|
|
751
|
+
-- fans to BOTH red legs (red+A on pca, red+B on pcb); the two `large` legs are unsat ⇒ skipped. ---
|
|
752
|
+
update PC set x = x + 1 where kind = 'red';
|
|
753
|
+
select id, x from pca order by id;
|
|
754
|
+
→ [{"id":1,"x":111},{"id":2,"x":20}]
|
|
755
|
+
select id, x from pcb order by id;
|
|
756
|
+
→ [{"id":3,"x":31},{"id":4,"x":40}]
|
|
757
|
+
|
|
758
|
+
-- --- Same-axis contradiction → clean no-op (the bug fix: this threw `requires at least one base
|
|
759
|
+
-- operation` before). `kind='red' and kind='large'` is provably unsat for EVERY leg (each leg pins
|
|
760
|
+
-- one kind, the other conjunct contradicts it) ⇒ zero legs ⇒ 0 rows deleted, base tables unchanged. ---
|
|
761
|
+
delete from PC where kind = 'red' and kind = 'large';
|
|
762
|
+
select id, x from pca order by id;
|
|
763
|
+
→ [{"id":1,"x":111},{"id":2,"x":20}]
|
|
764
|
+
select id, x from pcb order by id;
|
|
765
|
+
→ [{"id":3,"x":31},{"id":4,"x":40}]
|
|
766
|
+
|
|
767
|
+
-- the symmetric same-axis contradiction on the other axis (`src='A' and src='B'`) is also a no-op.
|
|
768
|
+
delete from PC where src = 'A' and src = 'B';
|
|
769
|
+
select id, x from pca order by id;
|
|
770
|
+
→ [{"id":1,"x":111},{"id":2,"x":20}]
|
|
771
|
+
select id, x from pcb order by id;
|
|
772
|
+
→ [{"id":3,"x":31},{"id":4,"x":40}]
|
|
773
|
+
|
|
774
|
+
-- --- Off-grid value → clean no-op (also the bug fix). `kind='zzz'` matches no leg's kind
|
|
775
|
+
-- discriminator ⇒ every leg unsat ⇒ zero-leg DELETE is 0 rows; the UPDATE form is the same. ---
|
|
776
|
+
delete from PC where kind = 'zzz';
|
|
777
|
+
select id, x from pca order by id;
|
|
778
|
+
→ [{"id":1,"x":111},{"id":2,"x":20}]
|
|
779
|
+
select id, x from pcb order by id;
|
|
780
|
+
→ [{"id":3,"x":31},{"id":4,"x":40}]
|
|
781
|
+
|
|
782
|
+
update PC set x = x + 1000 where src = 'zzz';
|
|
783
|
+
select id, x from pca order by id;
|
|
784
|
+
→ [{"id":1,"x":111},{"id":2,"x":20}]
|
|
785
|
+
select id, x from pcb order by id;
|
|
786
|
+
→ [{"id":3,"x":31},{"id":4,"x":40}]
|
|
787
|
+
|
|
788
|
+
-- --- Positive write beside the no-ops (the no-op guard must NOT swallow a real write): a single-axis
|
|
789
|
+
-- DELETE `kind='large'` still fans to both large legs and removes pca id 2 + pcb id 4. ---
|
|
790
|
+
delete from PC where kind = 'large';
|
|
791
|
+
select id, x from pca order by id;
|
|
792
|
+
→ [{"id":1,"x":111}]
|
|
793
|
+
select id, x from pcb order by id;
|
|
794
|
+
→ [{"id":3,"x":31}]
|
|
795
|
+
|
|
796
|
+
-- --- INSERT is the deliberate contrast to the no-op DELETE/UPDATE: an insert routing to no leg is a
|
|
797
|
+
-- genuine "this row belongs to no branch" reject (NOT softened to a no-op) — the row would be
|
|
798
|
+
-- invisible through every branch. The off-grid `kind='zzz'` makes every leg unsat. ---
|
|
799
|
+
insert into PC (id, x, kind, src) values (8, 80, 'zzz', 'A');
|
|
800
|
+
-- error: consistent with no writable leg
|
|
801
|
+
-- base tables untouched by the rejected insert.
|
|
802
|
+
select id, x from pca order by id;
|
|
803
|
+
→ [{"id":1,"x":111}]
|
|
804
|
+
select id, x from pcb order by id;
|
|
805
|
+
→ [{"id":3,"x":31}]
|
|
806
|
+
|
|
807
|
+
-- --- RETURNING is rejected up front regardless of leg count: a no-op-predicate DELETE that carries
|
|
808
|
+
-- RETURNING still errors on RETURNING (the reject precedes the zero-leg short-circuit). ---
|
|
809
|
+
delete from PC where kind = 'zzz' returning id;
|
|
810
|
+
-- error: RETURNING
|
|
811
|
+
|
|
812
|
+
-- ===================================================================
|
|
813
|
+
-- Zero-leg no-op reaches the `intersect` / `except` fan paths too — a discriminator-less body has
|
|
814
|
+
-- no literal axis, so the zero-leg case is driven by a contradictory base-column predicate
|
|
815
|
+
-- (`id = 1 and id = 2`, provably unsat). `fanLegsForFanOut` returns [] (intersect: every leg unsat;
|
|
816
|
+
-- except: the LEFT operand inconsistent), and the shared no-op guard covers both — never the
|
|
817
|
+
-- internal `requires at least one base operation` error.
|
|
818
|
+
-- ===================================================================
|
|
819
|
+
create table zia (id integer primary key, v integer);
|
|
820
|
+
create table zib (id integer primary key, v integer);
|
|
821
|
+
insert into zia values (1,10),(2,20);
|
|
822
|
+
insert into zib values (2,20),(3,30);
|
|
823
|
+
create view ZIV as select id, v from zia intersect select id, v from zib;
|
|
824
|
+
select * from ZIV order by id;
|
|
825
|
+
→ [{"id":2,"v":20}]
|
|
826
|
+
|
|
827
|
+
-- contradictory predicate → every intersect leg unsat → zero-leg no-op, base tables unchanged.
|
|
828
|
+
delete from ZIV where id = 1 and id = 2;
|
|
829
|
+
select id from zia order by id;
|
|
830
|
+
→ [{"id":1},{"id":2}]
|
|
831
|
+
select id from zib order by id;
|
|
832
|
+
→ [{"id":2},{"id":3}]
|
|
833
|
+
|
|
834
|
+
-- positive intersect delete beside it: id=2 is resident in both legs, so it leaves both bases.
|
|
835
|
+
delete from ZIV where id = 2;
|
|
836
|
+
select id from zia order by id;
|
|
837
|
+
→ [{"id":1}]
|
|
838
|
+
select id from zib order by id;
|
|
839
|
+
→ [{"id":3}]
|
|
840
|
+
|
|
841
|
+
create table zea (id integer primary key, v integer);
|
|
842
|
+
create table zeb (id integer primary key, v integer);
|
|
843
|
+
insert into zea values (1,10),(2,20);
|
|
844
|
+
insert into zeb values (2,20);
|
|
845
|
+
create view ZEV as select id, v from zea except select id, v from zeb;
|
|
846
|
+
select * from ZEV order by id;
|
|
847
|
+
→ [{"id":1,"v":10}]
|
|
848
|
+
|
|
849
|
+
-- `except` fans the LEFT operand only; the contradictory predicate makes the left leg inconsistent,
|
|
850
|
+
-- so the fan is empty → clean no-op, base tables unchanged.
|
|
851
|
+
delete from ZEV where id = 1 and id = 2;
|
|
852
|
+
select id from zea order by id;
|
|
853
|
+
→ [{"id":1},{"id":2}]
|
|
854
|
+
select id from zeb order by id;
|
|
855
|
+
→ [{"id":2}]
|
|
856
|
+
|
|
857
|
+
-- positive except delete beside it: id=1 is in the left (and not the right), so it leaves the difference.
|
|
858
|
+
delete from ZEV where id = 1;
|
|
859
|
+
select id from zea order by id;
|
|
860
|
+
→ [{"id":2}]
|
|
861
|
+
select id from zeb order by id;
|
|
862
|
+
→ [{"id":2}]
|