@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,447 @@
|
|
|
1
|
+
-- 08.4-key-set-semi-join.sqllogic — behaviour of the key-set semi join rewrite
|
|
2
|
+
--
|
|
3
|
+
-- requires-capability: standalone-index-ddl
|
|
4
|
+
--
|
|
5
|
+
-- Whole-file directive rather than a split: the rewrite under test only plans over a
|
|
6
|
+
-- SECONDARY-INDEXED column, so the index in the setup is load-bearing for every scenario
|
|
7
|
+
-- here — there is no index-free remainder to carve out.
|
|
8
|
+
--
|
|
9
|
+
-- `where col in (select …)` over a secondary-indexed column plans as a
|
|
10
|
+
-- KeySetSemiJoin under the memory vtab: the inner set is materialized once and,
|
|
11
|
+
-- when small enough, handed to the module as a plan=5 multi-seek. These cases
|
|
12
|
+
-- pin the OBSERVABLE behaviour only (rows, counts) — the same answers must come
|
|
13
|
+
-- back whether the runtime seeks or scans. Plan-shape and seek-vs-scan
|
|
14
|
+
-- assertions live in test/optimizer/key-set-seek.spec.ts and
|
|
15
|
+
-- test/vtab/key-set-semi-join-runtime.spec.ts.
|
|
16
|
+
|
|
17
|
+
-- Setup: target with a secondary index on v, key source, and data.
|
|
18
|
+
CREATE TABLE big (pk INTEGER PRIMARY KEY, v INTEGER NULL, w INTEGER NULL);
|
|
19
|
+
CREATE INDEX idx_big_v ON big(v);
|
|
20
|
+
CREATE TABLE small (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
21
|
+
|
|
22
|
+
INSERT INTO big VALUES (1, 10, 1), (2, 20, 2), (3, 30, 3), (4, 40, 4), (5, NULL, 5);
|
|
23
|
+
INSERT INTO small VALUES (1, 10), (2, 30), (3, 999);
|
|
24
|
+
|
|
25
|
+
-- ===== Key set contents =====
|
|
26
|
+
|
|
27
|
+
-- Basic membership: only matching rows return, each exactly once.
|
|
28
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small) ORDER BY pk;
|
|
29
|
+
→ [{"pk":1},{"pk":3}]
|
|
30
|
+
|
|
31
|
+
-- Duplicates in the inner must not fan out the target.
|
|
32
|
+
INSERT INTO small VALUES (4, 10), (5, 10);
|
|
33
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small) ORDER BY pk;
|
|
34
|
+
→ [{"pk":1},{"pk":3}]
|
|
35
|
+
|
|
36
|
+
-- Inner NULLs are skipped; non-NULL members still match.
|
|
37
|
+
INSERT INTO small VALUES (6, NULL);
|
|
38
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small) ORDER BY pk;
|
|
39
|
+
→ [{"pk":1},{"pk":3}]
|
|
40
|
+
|
|
41
|
+
-- A NULL target value never matches anything.
|
|
42
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small) AND pk = 5;
|
|
43
|
+
→ []
|
|
44
|
+
|
|
45
|
+
-- Empty inner ⇒ no rows.
|
|
46
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small WHERE k > 10000);
|
|
47
|
+
→ []
|
|
48
|
+
|
|
49
|
+
-- All-NULL inner ⇒ no rows.
|
|
50
|
+
SELECT pk FROM big WHERE v IN (SELECT k FROM small WHERE k IS NULL);
|
|
51
|
+
→ []
|
|
52
|
+
|
|
53
|
+
-- ORDER BY above the semi join stays correct whichever access path runs.
|
|
54
|
+
SELECT pk, v FROM big WHERE v IN (SELECT k FROM small) ORDER BY v DESC;
|
|
55
|
+
→ [{"pk":3,"v":30},{"pk":1,"v":10}]
|
|
56
|
+
|
|
57
|
+
-- ===== Types and collation =====
|
|
58
|
+
|
|
59
|
+
-- NOCASE target column and index: case variants of a member match.
|
|
60
|
+
CREATE TABLE ci (pk INTEGER PRIMARY KEY, s TEXT COLLATE NOCASE);
|
|
61
|
+
CREATE INDEX idx_ci_s ON ci(s);
|
|
62
|
+
CREATE TABLE ssrc (id INTEGER PRIMARY KEY, s TEXT);
|
|
63
|
+
INSERT INTO ci VALUES (1, 'Alpha'), (2, 'beta'), (3, 'GAMMA');
|
|
64
|
+
INSERT INTO ssrc VALUES (1, 'alpha'), (2, 'gamma');
|
|
65
|
+
SELECT pk FROM ci WHERE s IN (SELECT s FROM ssrc) ORDER BY pk;
|
|
66
|
+
→ [{"pk":1},{"pk":3}]
|
|
67
|
+
|
|
68
|
+
-- BINARY target column and index, BINARY keys: exact-case only.
|
|
69
|
+
CREATE TABLE bin (pk INTEGER PRIMARY KEY, s TEXT);
|
|
70
|
+
CREATE INDEX idx_bin_s ON bin(s);
|
|
71
|
+
INSERT INTO bin VALUES (1, 'Alpha'), (2, 'alpha');
|
|
72
|
+
SELECT pk FROM bin WHERE s IN (SELECT s FROM ssrc) ORDER BY pk;
|
|
73
|
+
→ [{"pk":2}]
|
|
74
|
+
|
|
75
|
+
-- TIMESPAN keys decline the seek but keep semantic equality:
|
|
76
|
+
-- 'PT1H' still matches 'PT60M' through the surviving hash semi join.
|
|
77
|
+
CREATE TABLE spans (pk INTEGER PRIMARY KEY, d TIMESPAN);
|
|
78
|
+
CREATE INDEX idx_spans_d ON spans(d);
|
|
79
|
+
CREATE TABLE spansrc (id INTEGER PRIMARY KEY, d TIMESPAN);
|
|
80
|
+
INSERT INTO spans VALUES (1, timespan('PT1H')), (2, timespan('P1D'));
|
|
81
|
+
INSERT INTO spansrc VALUES (1, timespan('PT60M'));
|
|
82
|
+
SELECT pk FROM spans WHERE d IN (SELECT d FROM spansrc);
|
|
83
|
+
→ [{"pk":1}]
|
|
84
|
+
|
|
85
|
+
-- ===== DML =====
|
|
86
|
+
|
|
87
|
+
-- DELETE with an IN-subquery source.
|
|
88
|
+
DELETE FROM big WHERE v IN (SELECT k FROM small);
|
|
89
|
+
SELECT pk FROM big ORDER BY pk;
|
|
90
|
+
→ [{"pk":2},{"pk":4},{"pk":5}]
|
|
91
|
+
|
|
92
|
+
-- UPDATE with an IN-subquery source.
|
|
93
|
+
INSERT INTO small VALUES (7, 20);
|
|
94
|
+
UPDATE big SET w = -1 WHERE v IN (SELECT k FROM small);
|
|
95
|
+
SELECT pk, w FROM big ORDER BY pk;
|
|
96
|
+
→ [{"pk":2,"w":-1},{"pk":4,"w":4},{"pk":5,"w":5}]
|
|
97
|
+
|
|
98
|
+
-- Self-referencing DELETE: the key set drains before the target opens, so the
|
|
99
|
+
-- pre-statement snapshot decides membership.
|
|
100
|
+
CREATE TABLE selfref (pk INTEGER PRIMARY KEY, x INTEGER NULL);
|
|
101
|
+
CREATE INDEX idx_selfref_x ON selfref(x);
|
|
102
|
+
INSERT INTO selfref VALUES (1, 10), (2, 20), (3, 30), (4, 40);
|
|
103
|
+
DELETE FROM selfref WHERE x IN (SELECT x FROM selfref WHERE x <= 20);
|
|
104
|
+
SELECT pk FROM selfref ORDER BY pk;
|
|
105
|
+
→ [{"pk":3},{"pk":4}]
|
|
106
|
+
|
|
107
|
+
-- ===== Shapes that leave the runtime free to seek =====
|
|
108
|
+
--
|
|
109
|
+
-- Every case above pins its rows with `ORDER BY <a column the target's own walk
|
|
110
|
+
-- already provides>`. Both backends absorb that sort into the target leaf, which
|
|
111
|
+
-- marks the leaf's emission order load-bearing; since the seek index (idx on v)
|
|
112
|
+
-- is NOT the walk index in those cases, the rewrite declines — they exercise the
|
|
113
|
+
-- surviving hash semi join, not the seek. (When the two indexes coincide the
|
|
114
|
+
-- rewrite now fires despite the absorbed sort — see the primary-key section at
|
|
115
|
+
-- the end of this file.) The aggregate forms below need no ORDER BY, so the
|
|
116
|
+
-- runtime may push the multi-seek. Same answers either way, which is exactly the
|
|
117
|
+
-- property being pinned.
|
|
118
|
+
|
|
119
|
+
CREATE TABLE seekt (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
120
|
+
CREATE INDEX idx_seekt_v ON seekt(v);
|
|
121
|
+
CREATE TABLE seekk (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
122
|
+
INSERT INTO seekt VALUES (1, 10), (2, 20), (3, 30), (4, 40), (5, NULL);
|
|
123
|
+
INSERT INTO seekk VALUES (1, 10), (2, 30), (3, 30), (4, 777);
|
|
124
|
+
|
|
125
|
+
SELECT count(*) AS c, sum(v) AS s FROM seekt WHERE v IN (SELECT k FROM seekk);
|
|
126
|
+
→ [{"c":2,"s":40}]
|
|
127
|
+
|
|
128
|
+
-- Empty key set through the same shape.
|
|
129
|
+
SELECT count(*) AS c FROM seekt WHERE v IN (SELECT k FROM seekk WHERE k > 10000);
|
|
130
|
+
→ [{"c":0}]
|
|
131
|
+
|
|
132
|
+
-- Read-your-own-writes: rows staged in an open transaction must be visible through
|
|
133
|
+
-- whichever access path the runtime chose — a seek reads each window through the
|
|
134
|
+
-- same effective-row merge a full scan does.
|
|
135
|
+
BEGIN;
|
|
136
|
+
INSERT INTO seekt VALUES (6, 777);
|
|
137
|
+
SELECT count(*) AS c FROM seekt WHERE v IN (SELECT k FROM seekk);
|
|
138
|
+
→ [{"c":3}]
|
|
139
|
+
UPDATE seekt SET v = 30 WHERE pk = 2;
|
|
140
|
+
SELECT count(*) AS c FROM seekt WHERE v IN (SELECT k FROM seekk);
|
|
141
|
+
→ [{"c":4}]
|
|
142
|
+
DELETE FROM seekt WHERE pk = 1;
|
|
143
|
+
SELECT count(*) AS c FROM seekt WHERE v IN (SELECT k FROM seekk);
|
|
144
|
+
→ [{"c":3}]
|
|
145
|
+
COMMIT;
|
|
146
|
+
SELECT count(*) AS c FROM seekt WHERE v IN (SELECT k FROM seekk);
|
|
147
|
+
→ [{"c":3}]
|
|
148
|
+
|
|
149
|
+
-- Multi-row DELETE inside an explicit transaction: each seek window is a
|
|
150
|
+
-- distinct key; deleting rows mid-statement must not disturb later windows.
|
|
151
|
+
CREATE TABLE txt (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
152
|
+
CREATE INDEX idx_txt_v ON txt(v);
|
|
153
|
+
CREATE TABLE txkeys (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
154
|
+
INSERT INTO txt VALUES (1, 100), (2, 200), (3, 300), (4, 400), (5, 500);
|
|
155
|
+
INSERT INTO txkeys VALUES (1, 200), (2, 400), (3, 500);
|
|
156
|
+
BEGIN;
|
|
157
|
+
DELETE FROM txt WHERE v IN (SELECT k FROM txkeys);
|
|
158
|
+
SELECT pk FROM txt ORDER BY pk;
|
|
159
|
+
→ [{"pk":1},{"pk":3}]
|
|
160
|
+
COMMIT;
|
|
161
|
+
SELECT pk FROM txt ORDER BY pk;
|
|
162
|
+
→ [{"pk":1},{"pk":3}]
|
|
163
|
+
|
|
164
|
+
-- ===== Cross-type numeric keys =====
|
|
165
|
+
--
|
|
166
|
+
-- INTEGER, REAL and NUMERIC share ONE seek key space: a numeric key's identity is its
|
|
167
|
+
-- VALUE, not the JavaScript representation (number vs bigint) that happens to hold it.
|
|
168
|
+
-- The membership check (`canonicalNumeric`), the in-memory index comparators and the
|
|
169
|
+
-- persistent store's key encoding all agree on that, so the rewrite admits these pairs
|
|
170
|
+
-- and the seek is exact in either direction. This file runs under BOTH backends, which
|
|
171
|
+
-- is what makes it the home for the row-level contract.
|
|
172
|
+
--
|
|
173
|
+
-- Every query below is an aggregate with no ORDER BY, so the runtime is free to push the
|
|
174
|
+
-- multi-seek (see the note above on why an ORDER BY the target's own walk provides makes
|
|
175
|
+
-- the rewrite decline). The answers must be identical whether it seeks or scans.
|
|
176
|
+
|
|
177
|
+
CREATE TABLE xt (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
178
|
+
CREATE INDEX idx_xt_v ON xt(v);
|
|
179
|
+
CREATE TABLE xr (id INTEGER PRIMARY KEY, r REAL NULL);
|
|
180
|
+
CREATE TABLE xik (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
181
|
+
INSERT INTO xt VALUES (1, 10), (2, 20), (3, 30);
|
|
182
|
+
INSERT INTO xr VALUES (1, 10.0), (2, 30.0), (3, 99.5);
|
|
183
|
+
INSERT INTO xik VALUES (1, 10), (2, 30), (3, 77);
|
|
184
|
+
|
|
185
|
+
-- INTEGER target, REAL keys: the whole-number reals match by value; 99.5 matches nothing.
|
|
186
|
+
SELECT count(*) AS c, sum(v) AS s FROM xt WHERE v IN (SELECT r FROM xr);
|
|
187
|
+
→ [{"c":2,"s":40}]
|
|
188
|
+
|
|
189
|
+
-- A NON-INTEGRAL key against a whole-number column matches NOTHING. This is the case a
|
|
190
|
+
-- coercion-based implementation would get wrong: truncating 10.5 to 10 would return pk 1.
|
|
191
|
+
INSERT INTO xr VALUES (4, 10.5);
|
|
192
|
+
SELECT count(*) AS c FROM xt WHERE v IN (SELECT r FROM xr WHERE r = 10.5);
|
|
193
|
+
→ [{"c":0}]
|
|
194
|
+
|
|
195
|
+
-- REAL target, INTEGER keys — the reverse direction.
|
|
196
|
+
CREATE TABLE xrt (pk INTEGER PRIMARY KEY, r REAL NULL);
|
|
197
|
+
CREATE INDEX idx_xrt_r ON xrt(r);
|
|
198
|
+
INSERT INTO xrt VALUES (1, 10.0), (2, 20.5), (3, 30.0);
|
|
199
|
+
SELECT count(*) AS c, min(pk) AS lo, max(pk) AS hi FROM xrt WHERE r IN (SELECT k FROM xik);
|
|
200
|
+
→ [{"c":2,"lo":1,"hi":3}]
|
|
201
|
+
|
|
202
|
+
-- NUMERIC target, INTEGER keys.
|
|
203
|
+
CREATE TABLE xnt (pk INTEGER PRIMARY KEY, n NUMERIC NULL);
|
|
204
|
+
CREATE INDEX idx_xnt_n ON xnt(n);
|
|
205
|
+
INSERT INTO xnt VALUES (1, 10), (2, 20.5), (3, 30);
|
|
206
|
+
SELECT count(*) AS c, min(pk) AS lo, max(pk) AS hi FROM xnt WHERE n IN (SELECT k FROM xik);
|
|
207
|
+
→ [{"c":2,"lo":1,"hi":3}]
|
|
208
|
+
|
|
209
|
+
-- NUMERIC key column, reached the ordinary way: set-operation column-type merging turns
|
|
210
|
+
-- a REAL branch and an INTEGER branch into one NUMERIC column.
|
|
211
|
+
SELECT count(*) AS c, sum(v) AS s FROM xt WHERE v IN (SELECT r FROM xr UNION ALL SELECT k FROM xik);
|
|
212
|
+
→ [{"c":2,"s":40}]
|
|
213
|
+
|
|
214
|
+
-- Past 2^53: a whole-number REAL key must match the integer of EQUAL magnitude and must
|
|
215
|
+
-- NOT reach its neighbour one above, which no longer shares its nearest double.
|
|
216
|
+
CREATE TABLE xbt (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
217
|
+
CREATE INDEX idx_xbt_v ON xbt(v);
|
|
218
|
+
CREATE TABLE xbr (id INTEGER PRIMARY KEY, r REAL NULL);
|
|
219
|
+
INSERT INTO xbt VALUES (1, 9007199254740992), (2, 9007199254740993);
|
|
220
|
+
INSERT INTO xbr VALUES (1, 9007199254740992.0);
|
|
221
|
+
SELECT count(*) AS c, min(pk) AS lo FROM xbt WHERE v IN (SELECT r FROM xbr);
|
|
222
|
+
→ [{"c":1,"lo":1}]
|
|
223
|
+
|
|
224
|
+
-- Signed zero: -0.0 and 0 are one value everywhere (the key serializer folds both to
|
|
225
|
+
-- `n:0`, the store encoder normalizes -0 to +0), so the stored 0 row comes back.
|
|
226
|
+
CREATE TABLE xzt (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
227
|
+
CREATE INDEX idx_xzt_v ON xzt(v);
|
|
228
|
+
CREATE TABLE xzk (id INTEGER PRIMARY KEY, r REAL NULL);
|
|
229
|
+
INSERT INTO xzt VALUES (1, 0), (2, 5);
|
|
230
|
+
INSERT INTO xzk VALUES (1, -0.0);
|
|
231
|
+
SELECT count(*) AS c, min(pk) AS lo FROM xzt WHERE v IN (SELECT r FROM xzk);
|
|
232
|
+
→ [{"c":1,"lo":1}]
|
|
233
|
+
|
|
234
|
+
-- NULL keys are unchanged by any of this: still dropped before the seek.
|
|
235
|
+
INSERT INTO xr VALUES (5, NULL);
|
|
236
|
+
SELECT count(*) AS c, sum(v) AS s FROM xt WHERE v IN (SELECT r FROM xr);
|
|
237
|
+
→ [{"c":2,"s":40}]
|
|
238
|
+
|
|
239
|
+
-- ===== Primary-key IN-subquery (the merge semi join arm) =====
|
|
240
|
+
--
|
|
241
|
+
-- `pk in (select <its own pk> from …)` walks BOTH sides in primary-key order, so on
|
|
242
|
+
-- the memory backend it plans as a merge semi join and reaches the KeySetSemiJoin
|
|
243
|
+
-- through the `key-set-seek-merge` anchor — including under an ORDER BY the walk
|
|
244
|
+
-- absorbed, because the seek index IS the walk index and the node claims the walk's
|
|
245
|
+
-- own order. The persistent store declines a runtime-set IN on its primary key
|
|
246
|
+
-- (backlog/feat-store-pk-in-list-multiseek) and keeps the merge join there. The two
|
|
247
|
+
-- backends therefore run DIFFERENT plans for every case below, which is exactly why
|
|
248
|
+
-- these assert ROWS only — the answers must be byte-identical either way.
|
|
249
|
+
|
|
250
|
+
CREATE TABLE pkt (pk INTEGER PRIMARY KEY, v TEXT);
|
|
251
|
+
CREATE TABLE pksrc (id INTEGER PRIMARY KEY);
|
|
252
|
+
CREATE TABLE pkkeys (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
253
|
+
INSERT INTO pkt VALUES (1, 'one'), (2, 'two'), (3, 'three'), (4, 'four'), (5, 'five');
|
|
254
|
+
INSERT INTO pksrc VALUES (4), (2), (999);
|
|
255
|
+
INSERT INTO pkkeys VALUES (1, 4), (2, 2), (3, 4), (4, NULL), (5, 999);
|
|
256
|
+
|
|
257
|
+
-- The headline shape, with the ORDER BY the walk absorbs (memory seeks anyway now).
|
|
258
|
+
SELECT pk, v FROM pkt WHERE pk IN (SELECT id FROM pksrc) ORDER BY pk;
|
|
259
|
+
→ [{"pk":2,"v":"two"},{"pk":4,"v":"four"}]
|
|
260
|
+
|
|
261
|
+
-- The same membership without ORDER BY, pinned through order-free aggregates.
|
|
262
|
+
SELECT count(*) AS c, min(pk) AS lo, max(pk) AS hi FROM pkt WHERE pk IN (SELECT id FROM pksrc);
|
|
263
|
+
→ [{"c":2,"lo":2,"hi":4}]
|
|
264
|
+
|
|
265
|
+
-- Duplicate and NULL keys against the primary key: dupes must not fan out the
|
|
266
|
+
-- target, NULLs are dropped from the set. (`k` is not the source's own key, so
|
|
267
|
+
-- backends are free to serve this by whichever arm plans — the rows may not care.)
|
|
268
|
+
SELECT pk FROM pkt WHERE pk IN (SELECT k FROM pkkeys) ORDER BY pk;
|
|
269
|
+
→ [{"pk":2},{"pk":4}]
|
|
270
|
+
|
|
271
|
+
-- Empty key set on the primary-key shape.
|
|
272
|
+
SELECT pk FROM pkt WHERE pk IN (SELECT id FROM pksrc WHERE id > 10000);
|
|
273
|
+
→ []
|
|
274
|
+
|
|
275
|
+
-- DELETE through the primary-key shape: exactly the matching rows go.
|
|
276
|
+
DELETE FROM pkt WHERE pk IN (SELECT id FROM pksrc);
|
|
277
|
+
SELECT pk, v FROM pkt ORDER BY pk;
|
|
278
|
+
→ [{"pk":1,"v":"one"},{"pk":3,"v":"three"},{"pk":5,"v":"five"}]
|
|
279
|
+
|
|
280
|
+
-- UPDATE through the same shape.
|
|
281
|
+
INSERT INTO pksrc VALUES (5);
|
|
282
|
+
UPDATE pkt SET v = 'touched' WHERE pk IN (SELECT id FROM pksrc);
|
|
283
|
+
SELECT pk, v FROM pkt ORDER BY pk;
|
|
284
|
+
→ [{"pk":1,"v":"one"},{"pk":3,"v":"three"},{"pk":5,"v":"touched"}]
|
|
285
|
+
|
|
286
|
+
-- Descending primary key: the walk and the seek both descend (the memory backend
|
|
287
|
+
-- advertises the true direction), and an absorbed ORDER BY pk DESC stays served.
|
|
288
|
+
CREATE TABLE pkd (pk INTEGER, v TEXT, PRIMARY KEY (pk DESC));
|
|
289
|
+
INSERT INTO pkd VALUES (1, 'a'), (2, 'b'), (3, 'c'), (4, 'd');
|
|
290
|
+
SELECT pk, v FROM pkd WHERE pk IN (SELECT id FROM pksrc) ORDER BY pk DESC;
|
|
291
|
+
→ [{"pk":4,"v":"d"},{"pk":2,"v":"b"}]
|
|
292
|
+
SELECT count(*) AS c, min(pk) AS lo FROM pkd WHERE pk IN (SELECT id FROM pksrc);
|
|
293
|
+
→ [{"c":2,"lo":2}]
|
|
294
|
+
|
|
295
|
+
-- Read-your-own-writes through the primary-key shape: staged rows must be visible
|
|
296
|
+
-- and ordered wherever the runtime seeks.
|
|
297
|
+
BEGIN;
|
|
298
|
+
INSERT INTO pkt VALUES (2, 'reborn');
|
|
299
|
+
SELECT pk, v FROM pkt WHERE pk IN (SELECT id FROM pksrc) ORDER BY pk;
|
|
300
|
+
→ [{"pk":2,"v":"reborn"},{"pk":5,"v":"touched"}]
|
|
301
|
+
DELETE FROM pkt WHERE pk = 5;
|
|
302
|
+
SELECT pk, v FROM pkt WHERE pk IN (SELECT id FROM pksrc) ORDER BY pk;
|
|
303
|
+
→ [{"pk":2,"v":"reborn"}]
|
|
304
|
+
ROLLBACK;
|
|
305
|
+
SELECT pk, v FROM pkt WHERE pk IN (SELECT id FROM pksrc) ORDER BY pk;
|
|
306
|
+
→ [{"pk":5,"v":"touched"}]
|
|
307
|
+
|
|
308
|
+
-- ===== A second indexed column also filtered (pushed-constraint target) =====
|
|
309
|
+
--
|
|
310
|
+
-- `<indexed col> = <value> and <key col> in (select …)`: the extra predicate is
|
|
311
|
+
-- pushed into the target leaf as its own index seek, and the key-set rewrite —
|
|
312
|
+
-- when a backend's costs admit it — keeps that seek as the target and re-applies
|
|
313
|
+
-- the predicate above the semi join. Seeded so one row matches the key set but
|
|
314
|
+
-- fails the extra filter (the worst failure mode: without the re-applied
|
|
315
|
+
-- predicate a seek would return — or a DML would touch — it) and one row matches
|
|
316
|
+
-- the extra filter but is not in the key set. Both must stay excluded whichever
|
|
317
|
+
-- access path the runtime picks, on either backend.
|
|
318
|
+
|
|
319
|
+
CREATE TABLE pc (pk INTEGER PRIMARY KEY, v INTEGER NULL, s TEXT NULL, w INTEGER NULL);
|
|
320
|
+
CREATE INDEX idx_pc_v ON pc(v);
|
|
321
|
+
CREATE INDEX idx_pc_s ON pc(s);
|
|
322
|
+
CREATE TABLE pckeys (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
323
|
+
-- pk 2: in the key set (v=20) but s='y'. pk 4: s='x' but v=40 is not in the set.
|
|
324
|
+
INSERT INTO pc VALUES (1, 10, 'x', 0), (2, 20, 'y', 0), (3, 30, 'x', 0), (4, 40, 'x', 0), (5, NULL, 'x', 0);
|
|
325
|
+
INSERT INTO pckeys VALUES (1, 10), (2, 20), (3, 30), (4, 999);
|
|
326
|
+
|
|
327
|
+
SELECT pk FROM pc WHERE s = 'x' AND v IN (SELECT k FROM pckeys) ORDER BY pk;
|
|
328
|
+
→ [{"pk":1},{"pk":3}]
|
|
329
|
+
|
|
330
|
+
-- DELETE: only the doubly-matching rows go. Deleting pk 2 (key-set member,
|
|
331
|
+
-- wrong s) would be the dropped-predicate bug this section guards against.
|
|
332
|
+
DELETE FROM pc WHERE s = 'x' AND v IN (SELECT k FROM pckeys);
|
|
333
|
+
SELECT pk FROM pc ORDER BY pk;
|
|
334
|
+
→ [{"pk":2},{"pk":4},{"pk":5}]
|
|
335
|
+
|
|
336
|
+
-- UPDATE through the same shape: pk 2 and pk 4 must stay untouched.
|
|
337
|
+
INSERT INTO pc VALUES (6, 30, 'x', 0);
|
|
338
|
+
UPDATE pc SET w = -1 WHERE s = 'x' AND v IN (SELECT k FROM pckeys);
|
|
339
|
+
SELECT pk, w FROM pc ORDER BY pk;
|
|
340
|
+
→ [{"pk":2,"w":0},{"pk":4,"w":0},{"pk":5,"w":0},{"pk":6,"w":-1}]
|
|
341
|
+
|
|
342
|
+
-- Range form of the pushed predicate (a primary-key range seek as the target).
|
|
343
|
+
CREATE TABLE pcr (pk INTEGER PRIMARY KEY, v INTEGER NULL);
|
|
344
|
+
CREATE INDEX idx_pcr_v ON pcr(v);
|
|
345
|
+
INSERT INTO pcr VALUES (1, 10), (2, 20), (3, 30), (4, 40);
|
|
346
|
+
SELECT pk FROM pcr WHERE pk > 2 AND v IN (SELECT k FROM pckeys) ORDER BY pk;
|
|
347
|
+
→ [{"pk":3}]
|
|
348
|
+
|
|
349
|
+
-- ===== Compound shape: the filtered table is ALSO joined to another table =====
|
|
350
|
+
--
|
|
351
|
+
-- `… from entry e join txn t on t.id = e.txn_id where e.txn_id in (select …)`
|
|
352
|
+
-- puts a join on the probe side of the semi join. `rule-semi-join-pushdown`
|
|
353
|
+
-- reassociates the semi join below the inner join so the probe side peels to a
|
|
354
|
+
-- bare leaf and the key-set rewrite above can fire on it. These cases pin the
|
|
355
|
+
-- rows only — the same answers must come back whether the semi join is pushed,
|
|
356
|
+
-- and whether the runtime then seeks or scans. Plan-shape assertions live in
|
|
357
|
+
-- test/optimizer/semi-join-pushdown.spec.ts and test/optimizer/key-set-seek.spec.ts.
|
|
358
|
+
|
|
359
|
+
CREATE TABLE cj_txn (id INTEGER PRIMARY KEY, date TEXT NULL);
|
|
360
|
+
CREATE TABLE cj_entry (id INTEGER PRIMARY KEY, txn_id INTEGER NULL, account_id INTEGER NULL, amount INTEGER NULL);
|
|
361
|
+
CREATE INDEX idx_cj_entry_account ON cj_entry(account_id);
|
|
362
|
+
CREATE INDEX idx_cj_entry_txn ON cj_entry(txn_id);
|
|
363
|
+
CREATE TABLE cj_tag (id INTEGER PRIMARY KEY, txn_id INTEGER NULL, tag TEXT NULL);
|
|
364
|
+
|
|
365
|
+
INSERT INTO cj_txn VALUES (1, 'd1'), (2, 'd2'), (3, 'd3');
|
|
366
|
+
-- id 1 and 2 share txn 1 (duplicate matches on one key), id 5 has a NULL join
|
|
367
|
+
-- key, id 6 names a txn that does not exist (dropped by the inner join, kept by
|
|
368
|
+
-- a left join), id 4 is not in the key set at all.
|
|
369
|
+
INSERT INTO cj_entry VALUES
|
|
370
|
+
(1, 1, 7, 100), (2, 1, 8, 200), (3, 2, 7, 300),
|
|
371
|
+
(4, 3, 9, 400), (5, NULL, 7, 500), (6, 99, 7, 600);
|
|
372
|
+
INSERT INTO cj_tag VALUES (1, 1, 'a'), (2, 1, 'b'), (3, 2, 'c');
|
|
373
|
+
|
|
374
|
+
-- The repro shape. Key set from account 7 is {1, 2, NULL, 99}; the NULL member
|
|
375
|
+
-- matches nothing, and entry 6's txn 99 has no txn row so the join drops it.
|
|
376
|
+
SELECT e.id, e.amount, t.date FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
377
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
378
|
+
ORDER BY e.id;
|
|
379
|
+
→ [{"id":1,"amount":100,"date":"d1"},{"id":2,"amount":200,"date":"d1"},{"id":3,"amount":300,"date":"d2"}]
|
|
380
|
+
|
|
381
|
+
-- A NULL target value never matches, however the semi join is placed.
|
|
382
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
383
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7) AND e.id = 5;
|
|
384
|
+
→ []
|
|
385
|
+
|
|
386
|
+
-- Fan-out on the UNTOUCHED side: the semi join is pushed onto `entry`, and the
|
|
387
|
+
-- tag join then multiplies the surviving rows. Pushing must not collapse or
|
|
388
|
+
-- duplicate that fan-out.
|
|
389
|
+
SELECT e.id, g.tag FROM cj_entry e JOIN cj_tag g ON g.txn_id = e.txn_id
|
|
390
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
391
|
+
ORDER BY e.id, g.tag;
|
|
392
|
+
→ [{"id":1,"tag":"a"},{"id":1,"tag":"b"},{"id":2,"tag":"a"},{"id":2,"tag":"b"},{"id":3,"tag":"c"}]
|
|
393
|
+
|
|
394
|
+
-- Right-arm: the semi condition reads only the OTHER branch, so the semi join is
|
|
395
|
+
-- pushed onto `txn` instead. Account 8 contributes txn 1 alone.
|
|
396
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
397
|
+
WHERE t.id IN (SELECT txn_id FROM cj_entry WHERE account_id = 8)
|
|
398
|
+
ORDER BY e.id;
|
|
399
|
+
→ [{"id":1},{"id":2}]
|
|
400
|
+
|
|
401
|
+
-- Correlated EXISTS reaches the same shape after decorrelation.
|
|
402
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
403
|
+
WHERE EXISTS (SELECT 1 FROM cj_entry m WHERE m.account_id = 7 AND m.txn_id = e.txn_id)
|
|
404
|
+
ORDER BY e.id;
|
|
405
|
+
→ [{"id":1},{"id":2},{"id":3}]
|
|
406
|
+
|
|
407
|
+
-- NOT IN over the compound shape: the pushdown declines on anti shapes, and the
|
|
408
|
+
-- NULL member of the key set makes the whole predicate UNKNOWN for every row.
|
|
409
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
410
|
+
WHERE e.txn_id NOT IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
411
|
+
ORDER BY e.id;
|
|
412
|
+
→ []
|
|
413
|
+
|
|
414
|
+
-- NOT EXISTS (an anti join above the inner join — the pushdown declines by
|
|
415
|
+
-- design) still answers: only entry 4, whose txn 3 no account-7 entry names.
|
|
416
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
417
|
+
WHERE NOT EXISTS (SELECT 1 FROM cj_entry m WHERE m.account_id = 7 AND m.txn_id = e.txn_id)
|
|
418
|
+
ORDER BY e.id;
|
|
419
|
+
→ [{"id":4}]
|
|
420
|
+
|
|
421
|
+
-- LEFT join on the probe side: out of scope for the pushdown, so the semi join
|
|
422
|
+
-- stays above the join. Entry 6 survives with a NULL-extended txn.
|
|
423
|
+
SELECT e.id, t.date FROM cj_entry e LEFT JOIN cj_txn t ON t.id = e.txn_id
|
|
424
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
425
|
+
ORDER BY e.id;
|
|
426
|
+
→ [{"id":1,"date":"d1"},{"id":2,"date":"d1"},{"id":3,"date":"d2"},{"id":6,"date":null}]
|
|
427
|
+
|
|
428
|
+
-- The semi condition spanning BOTH branches must decline and still answer.
|
|
429
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
430
|
+
WHERE EXISTS (SELECT 1 FROM cj_entry m
|
|
431
|
+
WHERE m.account_id = 7 AND m.txn_id = e.txn_id AND m.id = t.id)
|
|
432
|
+
ORDER BY e.id;
|
|
433
|
+
→ [{"id":1},{"id":2}]
|
|
434
|
+
|
|
435
|
+
-- Read-your-own-writes through the compound shape: a staged row must join and
|
|
436
|
+
-- filter like a committed one.
|
|
437
|
+
BEGIN;
|
|
438
|
+
INSERT INTO cj_entry VALUES (7, 3, 7, 700);
|
|
439
|
+
SELECT e.id, t.date FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
440
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
441
|
+
ORDER BY e.id;
|
|
442
|
+
→ [{"id":1,"date":"d1"},{"id":2,"date":"d1"},{"id":3,"date":"d2"},{"id":4,"date":"d3"},{"id":7,"date":"d3"}]
|
|
443
|
+
ROLLBACK;
|
|
444
|
+
SELECT e.id FROM cj_entry e JOIN cj_txn t ON t.id = e.txn_id
|
|
445
|
+
WHERE e.txn_id IN (SELECT txn_id FROM cj_entry WHERE account_id = 7)
|
|
446
|
+
ORDER BY e.id;
|
|
447
|
+
→ [{"id":1},{"id":2},{"id":3}]
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
-- set_operations.sqllogic - Testing UNION, INTERSECT, EXCEPT operations
|
|
2
|
+
|
|
3
|
+
-- Setup test tables
|
|
4
|
+
CREATE TABLE set_a (id INTEGER PRIMARY KEY, value TEXT);
|
|
5
|
+
CREATE TABLE set_b (id INTEGER PRIMARY KEY, value TEXT);
|
|
6
|
+
|
|
7
|
+
INSERT INTO set_a VALUES (1, 'apple'), (2, 'banana'), (3, 'cherry');
|
|
8
|
+
INSERT INTO set_b VALUES (2, 'banana'), (3, 'cherry'), (4, 'date');
|
|
9
|
+
|
|
10
|
+
-- UNION (combines and deduplicates)
|
|
11
|
+
SELECT value FROM set_a
|
|
12
|
+
UNION
|
|
13
|
+
SELECT value FROM set_b
|
|
14
|
+
ORDER BY value;
|
|
15
|
+
→ [{"value":"apple"},{"value":"banana"},{"value":"cherry"},{"value":"date"}]
|
|
16
|
+
|
|
17
|
+
-- UNION ALL (combines without deduplication)
|
|
18
|
+
SELECT value FROM set_a
|
|
19
|
+
UNION ALL
|
|
20
|
+
SELECT value FROM set_b
|
|
21
|
+
ORDER BY value;
|
|
22
|
+
→ [{"value":"apple"},{"value":"banana"},{"value":"banana"},{"value":"cherry"},{"value":"cherry"},{"value":"date"}]
|
|
23
|
+
|
|
24
|
+
-- INTERSECT (common values)
|
|
25
|
+
SELECT value FROM set_a
|
|
26
|
+
INTERSECT
|
|
27
|
+
SELECT value FROM set_b
|
|
28
|
+
ORDER BY value;
|
|
29
|
+
→ [{"value":"banana"},{"value":"cherry"}]
|
|
30
|
+
|
|
31
|
+
-- EXCEPT/MINUS (values in first set but not second)
|
|
32
|
+
SELECT value FROM set_a
|
|
33
|
+
EXCEPT
|
|
34
|
+
SELECT value FROM set_b
|
|
35
|
+
ORDER BY value;
|
|
36
|
+
→ [{"value":"apple"}]
|
|
37
|
+
|
|
38
|
+
-- DIFF (symmetric difference)
|
|
39
|
+
SELECT value FROM set_a
|
|
40
|
+
DIFF
|
|
41
|
+
SELECT value FROM set_b
|
|
42
|
+
ORDER BY value;
|
|
43
|
+
→ [{"value":"apple"},{"value":"date"}]
|
|
44
|
+
|
|
45
|
+
-- DIFF commutativity: A DIFF B = B DIFF A
|
|
46
|
+
SELECT value FROM set_b
|
|
47
|
+
DIFF
|
|
48
|
+
SELECT value FROM set_a
|
|
49
|
+
ORDER BY value;
|
|
50
|
+
→ [{"value":"apple"},{"value":"date"}]
|
|
51
|
+
|
|
52
|
+
-- DIFF zero on identical sets
|
|
53
|
+
WITH s AS (
|
|
54
|
+
SELECT value FROM set_a
|
|
55
|
+
UNION
|
|
56
|
+
SELECT value FROM set_b
|
|
57
|
+
)
|
|
58
|
+
SELECT COUNT(*) AS c FROM (
|
|
59
|
+
SELECT * FROM s
|
|
60
|
+
DIFF
|
|
61
|
+
SELECT * FROM s
|
|
62
|
+
);
|
|
63
|
+
→ [{"c":0}]
|
|
64
|
+
|
|
65
|
+
-- Table equality via NOT EXISTS(A DIFF B)
|
|
66
|
+
WITH a AS (SELECT 1 AS v UNION SELECT 2),
|
|
67
|
+
b AS (SELECT 2 AS v UNION SELECT 1)
|
|
68
|
+
SELECT NOT EXISTS(
|
|
69
|
+
SELECT * FROM a DIFF SELECT * FROM b
|
|
70
|
+
) AS eq;
|
|
71
|
+
→ [{"eq":true}]
|
|
72
|
+
|
|
73
|
+
-- Complex set operations with WHERE clauses
|
|
74
|
+
SELECT value FROM set_a WHERE id > 1
|
|
75
|
+
UNION
|
|
76
|
+
SELECT value FROM set_b WHERE id < 4
|
|
77
|
+
ORDER BY value;
|
|
78
|
+
→ [{"value":"banana"},{"value":"cherry"}]
|
|
79
|
+
|
|
80
|
+
-- Set operations with literals
|
|
81
|
+
SELECT 'literal1' as value
|
|
82
|
+
UNION
|
|
83
|
+
SELECT value FROM set_a WHERE id = 1
|
|
84
|
+
ORDER BY value;
|
|
85
|
+
→ [{"value":"apple"},{"value":"literal1"}]
|
|
86
|
+
|
|
87
|
+
-- Nested set operations
|
|
88
|
+
SELECT value FROM set_a
|
|
89
|
+
UNION
|
|
90
|
+
(SELECT value FROM set_b EXCEPT SELECT 'banana')
|
|
91
|
+
ORDER BY value;
|
|
92
|
+
→ [{"value":"apple"},{"value":"banana"},{"value":"cherry"},{"value":"date"}]
|
|
93
|
+
|
|
94
|
+
-- Set operations with different column types (should handle type coercion)
|
|
95
|
+
CREATE TABLE set_numbers (id INTEGER PRIMARY KEY, value INTEGER);
|
|
96
|
+
INSERT INTO set_numbers VALUES (1, 123), (2, 456);
|
|
97
|
+
SELECT CAST(value AS TEXT) as value FROM set_numbers
|
|
98
|
+
UNION
|
|
99
|
+
SELECT value FROM set_a WHERE id = 1;
|
|
100
|
+
→ [{"value":"123"},{"value":"456"},{"value":"apple"}]
|
|
101
|
+
DROP TABLE set_numbers;
|
|
102
|
+
|
|
103
|
+
DROP TABLE set_a;
|
|
104
|
+
DROP TABLE set_b;
|
|
105
|
+
|
|
106
|
+
-- Test set operation mathematical properties
|
|
107
|
+
-- Test UNION commutativity: A UNION B = B UNION A
|
|
108
|
+
SELECT value FROM (VALUES (1), (2), (3)) AS t1(value)
|
|
109
|
+
UNION
|
|
110
|
+
SELECT value FROM (VALUES (2), (4), (5)) AS t2(value)
|
|
111
|
+
ORDER BY value;
|
|
112
|
+
→ [{"value":1},{"value":2},{"value":3},{"value":4},{"value":5}]
|
|
113
|
+
|
|
114
|
+
SELECT value FROM (VALUES (2), (4), (5)) AS t1(value)
|
|
115
|
+
UNION
|
|
116
|
+
SELECT value FROM (VALUES (1), (2), (3)) AS t2(value)
|
|
117
|
+
ORDER BY value;
|
|
118
|
+
→ [{"value":1},{"value":2},{"value":3},{"value":4},{"value":5}]
|
|
119
|
+
|
|
120
|
+
-- Test UNION idempotency: A UNION A = A
|
|
121
|
+
SELECT value FROM (VALUES (1), (2), (3)) AS t1(value)
|
|
122
|
+
UNION
|
|
123
|
+
SELECT value FROM (VALUES (1), (2), (3)) AS t2(value)
|
|
124
|
+
ORDER BY value;
|
|
125
|
+
→ [{"value":1},{"value":2},{"value":3}]
|
|
126
|
+
|
|
127
|
+
SELECT DISTINCT value FROM (VALUES (1), (2), (3)) AS t1(value)
|
|
128
|
+
ORDER BY value;
|
|
129
|
+
→ [{"value":1},{"value":2},{"value":3}]
|
|
130
|
+
|
|
131
|
+
-- Test INTERSECT commutativity
|
|
132
|
+
SELECT value FROM (VALUES (1), (2), (3), (4)) AS t1(value)
|
|
133
|
+
INTERSECT
|
|
134
|
+
SELECT value FROM (VALUES (2), (3), (4), (5)) AS t2(value)
|
|
135
|
+
ORDER BY value;
|
|
136
|
+
→ [{"value":2},{"value":3},{"value":4}]
|
|
137
|
+
|
|
138
|
+
SELECT value FROM (VALUES (2), (3), (4), (5)) AS t1(value)
|
|
139
|
+
INTERSECT
|
|
140
|
+
SELECT value FROM (VALUES (1), (2), (3), (4)) AS t2(value)
|
|
141
|
+
ORDER BY value;
|
|
142
|
+
→ [{"value":2},{"value":3},{"value":4}]
|
|
143
|
+
|
|
144
|
+
-- Test type coercion in set operations (regression test for JSON.stringify bug)
|
|
145
|
+
-- These tests ensure proper SQL comparison semantics, not JSON.stringify comparison
|
|
146
|
+
-- Bug: JSON.stringify(1) !== JSON.stringify(true), but SQL treats them as equal (both NUMERIC storage class)
|
|
147
|
+
|
|
148
|
+
-- Test with boolean values (true equals 1, false equals 0 in SQL)
|
|
149
|
+
-- This would FAIL with JSON.stringify because JSON.stringify(1) !== JSON.stringify(true)
|
|
150
|
+
-- UNION preserves left-side types when values are equal
|
|
151
|
+
SELECT value FROM (VALUES (0), (1), (2)) AS t1(value)
|
|
152
|
+
UNION
|
|
153
|
+
SELECT value FROM (VALUES (false), (true)) AS t2(value)
|
|
154
|
+
ORDER BY value;
|
|
155
|
+
→ [{"value":0},{"value":1},{"value":2}]
|
|
156
|
+
|
|
157
|
+
-- INTERSECT with boolean/numeric (regression test for JSON.stringify bug)
|
|
158
|
+
-- true should match 1, false should match 0
|
|
159
|
+
-- INTERSECT returns values from the left side that match the right side
|
|
160
|
+
SELECT value FROM (VALUES (0), (1), (2)) AS t1(value)
|
|
161
|
+
INTERSECT
|
|
162
|
+
SELECT value FROM (VALUES (true), (false)) AS t2(value)
|
|
163
|
+
ORDER BY value;
|
|
164
|
+
→ [{"value":0},{"value":1}]
|
|
165
|
+
|
|
166
|
+
-- INTERSECT reversed - should return boolean types from left side
|
|
167
|
+
SELECT value FROM (VALUES (true), (false)) AS t1(value)
|
|
168
|
+
INTERSECT
|
|
169
|
+
SELECT value FROM (VALUES (0), (1), (2)) AS t2(value)
|
|
170
|
+
ORDER BY value;
|
|
171
|
+
→ [{"value":false},{"value":true}]
|
|
172
|
+
|
|
173
|
+
-- EXCEPT with boolean/numeric
|
|
174
|
+
SELECT value FROM (VALUES (0), (1), (2)) AS t1(value)
|
|
175
|
+
EXCEPT
|
|
176
|
+
SELECT value FROM (VALUES (true)) AS t2(value)
|
|
177
|
+
ORDER BY value;
|
|
178
|
+
→ [{"value":0},{"value":2}]
|
|
179
|
+
|
|
180
|
+
-- Test that numeric and string values are kept distinct (different storage classes)
|
|
181
|
+
-- In SQL, 1 (NUMERIC) and '1' (TEXT) are different values
|
|
182
|
+
SELECT value FROM (VALUES (1), (2), (3)) AS t1(value)
|
|
183
|
+
UNION
|
|
184
|
+
SELECT value FROM (VALUES ('2'), ('3'), ('4')) AS t2(value)
|
|
185
|
+
ORDER BY value;
|
|
186
|
+
→ [{"value":1},{"value":2},{"value":3},{"value":"2"},{"value":"3"},{"value":"4"}]
|
|
187
|
+
|
|
188
|
+
-- Test NULL handling in set operations
|
|
189
|
+
-- NULLs should be treated as equal in DISTINCT operations (for deduplication)
|
|
190
|
+
SELECT value FROM (VALUES (1), (2), (NULL)) AS t1(value)
|
|
191
|
+
UNION
|
|
192
|
+
SELECT value FROM (VALUES (2), (3), (NULL)) AS t2(value)
|
|
193
|
+
ORDER BY value;
|
|
194
|
+
→ [{"value":null},{"value":1},{"value":2},{"value":3}]
|
|
195
|
+
|
|
196
|
+
-- INTERSECT with NULLs (NULL should match NULL in set operations)
|
|
197
|
+
SELECT value FROM (VALUES (1), (2), (NULL)) AS t1(value)
|
|
198
|
+
INTERSECT
|
|
199
|
+
SELECT value FROM (VALUES (2), (NULL), (3)) AS t2(value)
|
|
200
|
+
ORDER BY value;
|
|
201
|
+
→ [{"value":null},{"value":2}]
|
|
202
|
+
|
|
203
|
+
-- EXCEPT with NULLs
|
|
204
|
+
SELECT value FROM (VALUES (1), (2), (NULL)) AS t1(value)
|
|
205
|
+
EXCEPT
|
|
206
|
+
SELECT value FROM (VALUES (2), (NULL)) AS t2(value)
|
|
207
|
+
ORDER BY value;
|
|
208
|
+
→ [{"value":1}]
|
|
209
|
+
|
|
210
|
+
-- Test with real numbers and integers (1 and 1.0 should be equal)
|
|
211
|
+
SELECT value FROM (VALUES (1), (1.0), (1.5)) AS t1(value)
|
|
212
|
+
UNION
|
|
213
|
+
SELECT value FROM (VALUES (1), (2.0)) AS t2(value)
|
|
214
|
+
ORDER BY value;
|
|
215
|
+
→ [{"value":1},{"value":1.5},{"value":2}]
|
|
216
|
+
|
|
217
|
+
-- Test multi-column set operations with boolean/numeric equivalence
|
|
218
|
+
-- (1, 'x') should equal (true, 'x') because 1 == true in SQL
|
|
219
|
+
SELECT a, b FROM (VALUES (1, 'x'), (0, 'y'), (2, 'z')) AS t1(a, b)
|
|
220
|
+
UNION
|
|
221
|
+
SELECT a, b FROM (VALUES (true, 'x'), (false, 'y')) AS t2(a, b)
|
|
222
|
+
ORDER BY a, b;
|
|
223
|
+
→ [{"a":0,"b":"y"},{"a":1,"b":"x"},{"a":2,"b":"z"}]
|
|
224
|
+
|
|
225
|
+
-- INTERSECT multi-column with boolean/numeric
|
|
226
|
+
SELECT a, b FROM (VALUES (1, 'x'), (0, 'y'), (2, 'z')) AS t1(a, b)
|
|
227
|
+
INTERSECT
|
|
228
|
+
SELECT a, b FROM (VALUES (true, 'x'), (false, 'y')) AS t2(a, b)
|
|
229
|
+
ORDER BY a, b;
|
|
230
|
+
→ [{"a":0,"b":"y"},{"a":1,"b":"x"}]
|
|
231
|
+
|
|
232
|
+
-- EXCEPT with duplicate left-side rows expects deduplication
|
|
233
|
+
SELECT value FROM (VALUES (1), (1), (2)) AS t1(value)
|
|
234
|
+
EXCEPT
|
|
235
|
+
SELECT value FROM (VALUES (2)) AS t2(value)
|
|
236
|
+
ORDER BY value;
|
|
237
|
+
→ [{"value":1}]
|
|
238
|
+
|
|
239
|
+
-- EXCEPT with multiple duplicates
|
|
240
|
+
SELECT value FROM (VALUES ('a'), ('a'), ('a'), ('b'), ('c')) AS t1(value)
|
|
241
|
+
EXCEPT
|
|
242
|
+
SELECT value FROM (VALUES ('b')) AS t2(value)
|
|
243
|
+
ORDER BY value;
|
|
244
|
+
→ [{"value":"a"},{"value":"c"}]
|