@quereus/quereus 4.20.1 → 4.20.3
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/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +17 -69
- package/dist/src/schema/schema-differ.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 +210 -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,79 @@
|
|
|
1
|
+
-- Test for grow-retrieve rule with the new pass system
|
|
2
|
+
-- This test verifies that the structural pass correctly identifies and grows RetrieveNodes
|
|
3
|
+
|
|
4
|
+
-- Create a simple memory table
|
|
5
|
+
CREATE TABLE test_table (id INTEGER PRIMARY KEY, name TEXT, age INTEGER) USING memory;
|
|
6
|
+
|
|
7
|
+
-- Insert test data
|
|
8
|
+
INSERT INTO test_table VALUES (1, 'Alice', 30), (2, 'Bob', 25), (3, 'Charlie', 35);
|
|
9
|
+
|
|
10
|
+
-- Basic filter test - should work
|
|
11
|
+
SELECT id, name FROM test_table WHERE id = 1;
|
|
12
|
+
→ [{"id": 1, "name": "Alice"}]
|
|
13
|
+
|
|
14
|
+
-- Range filter test
|
|
15
|
+
SELECT name, age FROM test_table WHERE age > 25;
|
|
16
|
+
→ [{"name": "Alice", "age": 30}, {"name": "Charlie", "age": 35}]
|
|
17
|
+
|
|
18
|
+
-- Compound predicate test
|
|
19
|
+
SELECT name FROM test_table WHERE id = 2 AND age > 20;
|
|
20
|
+
→ [{"name": "Bob"}]
|
|
21
|
+
|
|
22
|
+
-- Project with filter test
|
|
23
|
+
SELECT name FROM test_table WHERE age < 35;
|
|
24
|
+
→ [{"name": "Alice"}, {"name": "Bob"}]
|
|
25
|
+
|
|
26
|
+
-- TODO: Once grow-retrieve is fully working with memory tables, these tests should verify
|
|
27
|
+
-- that the Filter nodes are being pushed into the RetrieveNode boundary.
|
|
28
|
+
|
|
29
|
+
-- Plan checks using query_plan()
|
|
30
|
+
|
|
31
|
+
-- Equality predicate on primary key: IndexSeek handles the constraint directly (no FILTER needed)
|
|
32
|
+
SELECT COUNT(*) AS filters FROM query_plan('SELECT id, name FROM test_table WHERE id = 1') WHERE op = 'FILTER';
|
|
33
|
+
→ [{"filters": 0}]
|
|
34
|
+
SELECT COUNT(*) AS accesses FROM query_plan('SELECT id, name FROM test_table WHERE id = 1') WHERE op = 'SEQSCAN' OR op = 'INDEXSCAN' OR op = 'INDEXSEEK';
|
|
35
|
+
→ [{"accesses": 1}]
|
|
36
|
+
|
|
37
|
+
-- Range predicate on non-indexed column should retain a FILTER above access
|
|
38
|
+
SELECT COUNT(*) AS filters FROM query_plan('SELECT name, age FROM test_table WHERE age > 25') WHERE op = 'FILTER';
|
|
39
|
+
→ [{"filters": 1}]
|
|
40
|
+
SELECT COUNT(*) AS accesses FROM query_plan('SELECT name, age FROM test_table WHERE age > 25') WHERE op = 'SEQSCAN' OR op = 'INDEXSCAN' OR op = 'INDEXSEEK';
|
|
41
|
+
→ [{"accesses": 1}]
|
|
42
|
+
|
|
43
|
+
-- Sort placement test: ensure access still chosen and one filter remains (if any)
|
|
44
|
+
SELECT COUNT(*) AS sorts FROM query_plan('SELECT name FROM test_table WHERE age > 20 ORDER BY name') WHERE op = 'SORT';
|
|
45
|
+
→ [{"sorts": 1}]
|
|
46
|
+
SELECT COUNT(*) AS accesses FROM query_plan('SELECT name FROM test_table WHERE age > 20 ORDER BY name') WHERE op = 'SEQSCAN' OR op = 'INDEXSCAN' OR op = 'INDEXSEEK';
|
|
47
|
+
→ [{"accesses": 1}]
|
|
48
|
+
|
|
49
|
+
-- Limit placement test: ensure access still chosen
|
|
50
|
+
SELECT COUNT(*) AS accesses FROM query_plan('SELECT name FROM test_table WHERE age > 20 ORDER BY name LIMIT 2') WHERE op = 'SEQSCAN' OR op = 'INDEXSCAN' OR op = 'INDEXSEEK';
|
|
51
|
+
→ [{"accesses": 1}]
|
|
52
|
+
|
|
53
|
+
-- Query-based push-down smoke (TestQueryModule) when available (non-assertive)
|
|
54
|
+
-- SELECT json_group_array(op) as ops FROM query_plan('SELECT id FROM test_table WHERE id = 2');
|
|
55
|
+
-- → [{"ops":"[\"FILTER\",\"SEQSCAN\"]"}]
|
|
56
|
+
|
|
57
|
+
-- Identity projection optimization: SELECT * should not create a PROJECT node
|
|
58
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT * FROM test_table') WHERE op = 'PROJECT';
|
|
59
|
+
→ [{"projects": 0}]
|
|
60
|
+
|
|
61
|
+
-- Identity projection: selecting all columns in order should also skip PROJECT
|
|
62
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT id, name, age FROM test_table') WHERE op = 'PROJECT';
|
|
63
|
+
→ [{"projects": 0}]
|
|
64
|
+
|
|
65
|
+
-- Non-identity: reordered columns SHOULD have PROJECT
|
|
66
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT age, name, id FROM test_table') WHERE op = 'PROJECT';
|
|
67
|
+
→ [{"projects": 1}]
|
|
68
|
+
|
|
69
|
+
-- Non-identity: partial columns SHOULD have PROJECT
|
|
70
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT id, name FROM test_table') WHERE op = 'PROJECT';
|
|
71
|
+
→ [{"projects": 1}]
|
|
72
|
+
|
|
73
|
+
-- Non-identity: alias SHOULD have PROJECT
|
|
74
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT id AS identifier, name, age FROM test_table') WHERE op = 'PROJECT';
|
|
75
|
+
→ [{"projects": 1}]
|
|
76
|
+
|
|
77
|
+
-- Non-identity: computed expression SHOULD have PROJECT
|
|
78
|
+
SELECT COUNT(*) AS projects FROM query_plan('SELECT id, name, age + 1 FROM test_table') WHERE op = 'PROJECT';
|
|
79
|
+
→ [{"projects": 1}]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- QuickPick Join Enumeration logic tests
|
|
2
|
+
|
|
3
|
+
-- Chain schema: a -> b -> c
|
|
4
|
+
CREATE TABLE a (id INTEGER PRIMARY KEY, v INTEGER) USING memory;
|
|
5
|
+
CREATE TABLE b (id INTEGER PRIMARY KEY, a_id INTEGER) USING memory;
|
|
6
|
+
CREATE TABLE c (id INTEGER PRIMARY KEY, b_id INTEGER) USING memory;
|
|
7
|
+
|
|
8
|
+
INSERT INTO a VALUES (1,10),(2,20),(3,30);
|
|
9
|
+
INSERT INTO b VALUES (10,1),(20,2),(30,3);
|
|
10
|
+
INSERT INTO c VALUES (100,10),(200,20),(300,30);
|
|
11
|
+
|
|
12
|
+
-- Sanity: actual result correctness regardless of order
|
|
13
|
+
select COUNT(*) from a JOIN b ON a.id=b.a_id JOIN c ON b.id=c.b_id;
|
|
14
|
+
→ [{"count(*)":3}]
|
|
15
|
+
|
|
16
|
+
-- Diagnostics presence: ensure quickpick diagnostics appear in properties (exists)
|
|
17
|
+
select 1 AS ok from query_plan('SELECT * FROM a JOIN b ON a.id=b.a_id JOIN c ON b.id=c.b_id')
|
|
18
|
+
WHERE properties LIKE '%quickpick%'
|
|
19
|
+
LIMIT 1;
|
|
20
|
+
→ [{"ok":1}]
|
|
21
|
+
|
|
22
|
+
-- Bushy check (indirect): verify results also correct for re-ordered predicates (same semantics)
|
|
23
|
+
select COUNT(*) from c JOIN b ON b.id=c.b_id JOIN a ON a.id=b.a_id;
|
|
24
|
+
→ [{"count(*)":3}]
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
-- bloom-join.sqllogic - Hash/bloom join algorithm correctness tests
|
|
2
|
+
|
|
3
|
+
-- Setup tables
|
|
4
|
+
CREATE TABLE bj_left (id INTEGER PRIMARY KEY, val INTEGER, label TEXT);
|
|
5
|
+
CREATE TABLE bj_right (id INTEGER PRIMARY KEY, val INTEGER, tag TEXT);
|
|
6
|
+
|
|
7
|
+
INSERT INTO bj_left VALUES (1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (4, 40, 'd'), (5, 10, 'e');
|
|
8
|
+
INSERT INTO bj_right VALUES (100, 10, 'x'), (200, 20, 'y'), (300, 50, 'z'), (400, 10, 'w');
|
|
9
|
+
|
|
10
|
+
-- Basic equi-join (should use bloom join for equi predicate)
|
|
11
|
+
SELECT l.id, r.id FROM bj_left l JOIN bj_right r ON l.val = r.val ORDER BY l.id, r.id;
|
|
12
|
+
→ [{"id":1,"id:1":100},{"id":1,"id:1":400},{"id":2,"id:1":200},{"id":5,"id:1":100},{"id":5,"id:1":400}]
|
|
13
|
+
|
|
14
|
+
-- Verify bloom join appears in plan
|
|
15
|
+
SELECT 1 AS ok FROM query_plan('SELECT * FROM bj_left l JOIN bj_right r ON l.val = r.val')
|
|
16
|
+
WHERE properties LIKE '%bloom%' LIMIT 1;
|
|
17
|
+
→ [{"ok":1}]
|
|
18
|
+
|
|
19
|
+
-- LEFT JOIN with bloom join
|
|
20
|
+
SELECT l.id, l.val, r.tag FROM bj_left l LEFT JOIN bj_right r ON l.val = r.val ORDER BY l.id, r.tag;
|
|
21
|
+
→ [{"id":1,"val":10,"tag":"w"},{"id":1,"val":10,"tag":"x"},{"id":2,"val":20,"tag":"y"},{"id":3,"val":30,"tag":null},{"id":4,"val":40,"tag":null},{"id":5,"val":10,"tag":"w"},{"id":5,"val":10,"tag":"x"}]
|
|
22
|
+
|
|
23
|
+
-- Multi-column equi-join
|
|
24
|
+
CREATE TABLE bj_mc1 (a INTEGER, b INTEGER, v TEXT, PRIMARY KEY (a, b));
|
|
25
|
+
CREATE TABLE bj_mc2 (x INTEGER, y INTEGER, w TEXT, PRIMARY KEY (x, y));
|
|
26
|
+
|
|
27
|
+
INSERT INTO bj_mc1 VALUES (1, 10, 'alpha'), (2, 20, 'beta'), (3, 30, 'gamma');
|
|
28
|
+
INSERT INTO bj_mc2 VALUES (1, 10, 'one'), (2, 20, 'two'), (9, 99, 'nine');
|
|
29
|
+
|
|
30
|
+
SELECT m1.v, m2.w FROM bj_mc1 m1 JOIN bj_mc2 m2 ON m1.a = m2.x AND m1.b = m2.y ORDER BY m1.a;
|
|
31
|
+
→ [{"v":"alpha","w":"one"},{"v":"beta","w":"two"}]
|
|
32
|
+
|
|
33
|
+
-- NULL handling: NULLs should not match
|
|
34
|
+
CREATE TABLE bj_nulls (id INTEGER PRIMARY KEY, val INTEGER NULL);
|
|
35
|
+
INSERT INTO bj_nulls VALUES (1, null), (2, 10), (3, null), (4, 10);
|
|
36
|
+
|
|
37
|
+
SELECT a.id, b.id FROM bj_nulls a JOIN bj_nulls b ON a.val = b.val ORDER BY a.id, b.id;
|
|
38
|
+
→ [{"id":2,"id:1":2},{"id":2,"id:1":4},{"id":4,"id:1":2},{"id":4,"id:1":4}]
|
|
39
|
+
|
|
40
|
+
-- NULL LEFT JOIN: null keys produce unmatched rows
|
|
41
|
+
SELECT a.id, b.id FROM bj_nulls a LEFT JOIN bj_nulls b ON a.val = b.val ORDER BY a.id, b.id;
|
|
42
|
+
→ [{"id":1,"id:1":null},{"id":2,"id:1":2},{"id":2,"id:1":4},{"id":3,"id:1":null},{"id":4,"id:1":2},{"id":4,"id:1":4}]
|
|
43
|
+
|
|
44
|
+
-- Empty right side
|
|
45
|
+
CREATE TABLE bj_empty (id INTEGER PRIMARY KEY, val INTEGER);
|
|
46
|
+
|
|
47
|
+
SELECT l.id FROM bj_left l JOIN bj_empty e ON l.val = e.val;
|
|
48
|
+
→ []
|
|
49
|
+
|
|
50
|
+
SELECT l.id, e.id FROM bj_left l LEFT JOIN bj_empty e ON l.val = e.val ORDER BY l.id;
|
|
51
|
+
→ [{"id":1,"id:1":null},{"id":2,"id:1":null},{"id":3,"id:1":null},{"id":4,"id:1":null},{"id":5,"id:1":null}]
|
|
52
|
+
|
|
53
|
+
-- Empty left side
|
|
54
|
+
SELECT e.id FROM bj_empty e JOIN bj_right r ON e.val = r.val;
|
|
55
|
+
→ []
|
|
56
|
+
|
|
57
|
+
-- Type-mixed join columns: integer = integer is standard; also test with text
|
|
58
|
+
CREATE TABLE bj_text_l (id INTEGER PRIMARY KEY, key TEXT);
|
|
59
|
+
CREATE TABLE bj_text_r (id INTEGER PRIMARY KEY, key TEXT);
|
|
60
|
+
INSERT INTO bj_text_l VALUES (1, 'foo'), (2, 'bar'), (3, 'baz');
|
|
61
|
+
INSERT INTO bj_text_r VALUES (10, 'bar'), (20, 'baz'), (30, 'qux');
|
|
62
|
+
|
|
63
|
+
SELECT l.id, r.id FROM bj_text_l l JOIN bj_text_r r ON l.key = r.key ORDER BY l.id;
|
|
64
|
+
→ [{"id":2,"id:1":10},{"id":3,"id:1":20}]
|
|
65
|
+
|
|
66
|
+
-- USING clause should also use bloom join
|
|
67
|
+
SELECT l.id, r.id FROM bj_text_l l JOIN bj_text_r r USING (key) ORDER BY l.id;
|
|
68
|
+
→ [{"id":2,"id:1":10},{"id":3,"id:1":20}]
|
|
69
|
+
|
|
70
|
+
-- Duplicate key runs: multiple matches on both sides
|
|
71
|
+
CREATE TABLE bj_dup_l (id INTEGER PRIMARY KEY, val INTEGER);
|
|
72
|
+
CREATE TABLE bj_dup_r (id INTEGER PRIMARY KEY, val INTEGER);
|
|
73
|
+
INSERT INTO bj_dup_l VALUES (1, 5), (2, 5), (3, 5);
|
|
74
|
+
INSERT INTO bj_dup_r VALUES (10, 5), (20, 5);
|
|
75
|
+
|
|
76
|
+
SELECT count(*) AS cnt FROM bj_dup_l l JOIN bj_dup_r r ON l.val = r.val;
|
|
77
|
+
→ [{"cnt":6}]
|
|
78
|
+
|
|
79
|
+
-- LEFT JOIN with small left, large right: left must remain probe side
|
|
80
|
+
-- (regression: side-swap must not apply to LEFT JOINs)
|
|
81
|
+
CREATE TABLE bj_small (id INTEGER PRIMARY KEY, val INTEGER);
|
|
82
|
+
CREATE TABLE bj_large (id INTEGER PRIMARY KEY, val INTEGER);
|
|
83
|
+
INSERT INTO bj_small VALUES (1, 100), (2, 200);
|
|
84
|
+
INSERT INTO bj_large VALUES (10, 100), (20, 100), (30, 300), (40, 400), (50, 500), (60, 600), (70, 700);
|
|
85
|
+
|
|
86
|
+
-- All rows from bj_small must appear; unmatched get null right side
|
|
87
|
+
SELECT s.id, s.val, l.id AS lid FROM bj_small s LEFT JOIN bj_large l ON s.val = l.val ORDER BY s.id, l.id;
|
|
88
|
+
→ [{"id":1,"val":100,"lid":10},{"id":1,"val":100,"lid":20},{"id":2,"val":200,"lid":null}]
|
|
89
|
+
|
|
90
|
+
-- NOCASE collation: bloom join must normalize keys for case-insensitive matching
|
|
91
|
+
CREATE TABLE bj_nocase_l (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
92
|
+
CREATE TABLE bj_nocase_r (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
93
|
+
INSERT INTO bj_nocase_l VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
94
|
+
INSERT INTO bj_nocase_r VALUES (10, 'alice'), (20, 'BOB'), (30, 'Dave');
|
|
95
|
+
|
|
96
|
+
SELECT l.id, r.id FROM bj_nocase_l l JOIN bj_nocase_r r ON l.name = r.name ORDER BY l.id;
|
|
97
|
+
→ [{"id":1,"id:1":10},{"id":2,"id:1":20}]
|
|
98
|
+
|
|
99
|
+
-- NOCASE LEFT JOIN: unmatched probe rows still appear
|
|
100
|
+
SELECT l.id, l.name, r.id AS rid FROM bj_nocase_l l LEFT JOIN bj_nocase_r r ON l.name = r.name ORDER BY l.id;
|
|
101
|
+
→ [{"id":1,"name":"Alice","rid":10},{"id":2,"name":"Bob","rid":20},{"id":3,"name":"Charlie","rid":null}]
|
|
102
|
+
|
|
103
|
+
-- Asymmetric collation (ticket join-key-collation-resolution-alignment): left
|
|
104
|
+
-- defaults to BINARY, right declares NOCASE. The matched-collation gate demotes the
|
|
105
|
+
-- asymmetric pair out of the bloom path (kept conservative — the physical ordering
|
|
106
|
+
-- property is collation-blind), so this runs as a nested loop, but the comparison
|
|
107
|
+
-- still resolves to NOCASE via the shared lattice — IDENTICAL result to the
|
|
108
|
+
-- symmetric bloom join above. Mirror spelling: the old left-first key normalizer
|
|
109
|
+
-- would have built/probed under BINARY and missed the case-variant matches.
|
|
110
|
+
CREATE TABLE bj_mix_l (id INTEGER PRIMARY KEY, name TEXT);
|
|
111
|
+
CREATE TABLE bj_mix_r (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
112
|
+
INSERT INTO bj_mix_l VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
113
|
+
INSERT INTO bj_mix_r VALUES (10, 'alice'), (20, 'BOB'), (30, 'Dave');
|
|
114
|
+
|
|
115
|
+
SELECT l.id, r.id FROM bj_mix_l l JOIN bj_mix_r r ON l.name = r.name ORDER BY l.id;
|
|
116
|
+
→ [{"id":1,"id:1":10},{"id":2,"id:1":20}]
|
|
117
|
+
|
|
118
|
+
DROP TABLE bj_mix_l;
|
|
119
|
+
DROP TABLE bj_mix_r;
|
|
120
|
+
|
|
121
|
+
-- Residual condition: an equi conjunct (k) picks the bloom path, and the
|
|
122
|
+
-- cross-side non-equi conjunct (l.s < r.t) stays behind as the residual, which the
|
|
123
|
+
-- probe loop evaluates per candidate pair. Exercises the residual sub-program
|
|
124
|
+
-- param, which is only present when the ON clause has a non-equi conjunct.
|
|
125
|
+
CREATE TABLE bj_res_l (id INTEGER PRIMARY KEY, k INTEGER, s TEXT);
|
|
126
|
+
CREATE TABLE bj_res_r (id INTEGER PRIMARY KEY, k INTEGER, t TEXT);
|
|
127
|
+
INSERT INTO bj_res_l VALUES (1, 1, 'm'), (2, 1, 'z'), (3, 2, 'a'), (4, 3, 'q');
|
|
128
|
+
INSERT INTO bj_res_r VALUES (10, 1, 'p'), (20, 1, 'b'), (30, 2, 'c'), (40, 9, 'x');
|
|
129
|
+
|
|
130
|
+
SELECT 1 AS ok FROM query_plan('SELECT * FROM bj_res_l l JOIN bj_res_r r ON l.k = r.k AND l.s < r.t')
|
|
131
|
+
WHERE properties LIKE '%bloom%' LIMIT 1;
|
|
132
|
+
→ [{"ok":1}]
|
|
133
|
+
|
|
134
|
+
-- Inner: only pairs whose residual holds survive
|
|
135
|
+
SELECT l.id AS lid, r.id AS rid FROM bj_res_l l JOIN bj_res_r r ON l.k = r.k AND l.s < r.t ORDER BY lid, rid;
|
|
136
|
+
→ [{"lid":1,"rid":10},{"lid":3,"rid":30}]
|
|
137
|
+
|
|
138
|
+
-- LEFT: a left row whose every equi candidate fails the residual is unmatched, not dropped
|
|
139
|
+
SELECT 1 AS ok FROM query_plan('SELECT * FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t')
|
|
140
|
+
WHERE properties LIKE '%bloom%' LIMIT 1;
|
|
141
|
+
→ [{"ok":1}]
|
|
142
|
+
|
|
143
|
+
SELECT l.id AS lid, r.id AS rid FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t ORDER BY lid, rid;
|
|
144
|
+
→ [{"lid":1,"rid":10},{"lid":2,"rid":null},{"lid":3,"rid":30},{"lid":4,"rid":null}]
|
|
145
|
+
|
|
146
|
+
-- SEMI: residual gates the match bit. Spelled as an existence flag rather than a
|
|
147
|
+
-- correlated `exists (select ...)`: the subquery form plans as EXISTS over an index
|
|
148
|
+
-- seek and never reaches a join at all, so it would give the residual param no
|
|
149
|
+
-- coverage (see 08.2-existence-flag-semijoin-recovery.sqllogic for the rewrite).
|
|
150
|
+
SELECT 1 AS ok FROM query_plan('SELECT l.id AS lid FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t EXISTS RIGHT AS h WHERE h')
|
|
151
|
+
WHERE properties LIKE '%bloom%' LIMIT 1;
|
|
152
|
+
→ [{"ok":1}]
|
|
153
|
+
|
|
154
|
+
SELECT l.id AS lid FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t EXISTS RIGHT AS h WHERE h ORDER BY lid;
|
|
155
|
+
→ [{"lid":1},{"lid":3}]
|
|
156
|
+
|
|
157
|
+
-- ANTI: complement of the semi result
|
|
158
|
+
SELECT 1 AS ok FROM query_plan('SELECT l.id AS lid FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t EXISTS RIGHT AS h WHERE NOT h')
|
|
159
|
+
WHERE properties LIKE '%bloom%' LIMIT 1;
|
|
160
|
+
→ [{"ok":1}]
|
|
161
|
+
|
|
162
|
+
SELECT l.id AS lid FROM bj_res_l l LEFT JOIN bj_res_r r ON l.k = r.k AND l.s < r.t EXISTS RIGHT AS h WHERE NOT h ORDER BY lid;
|
|
163
|
+
→ [{"lid":2},{"lid":4}]
|
|
164
|
+
|
|
165
|
+
-- NOTE: no residual case here forces build/probe side-swap (the swap only fires for
|
|
166
|
+
-- inner joins whose left side is the larger one, and the cost model decides). If a
|
|
167
|
+
-- swap-related residual bug ever surfaces, add a case that pins the swap.
|
|
168
|
+
DROP TABLE bj_res_l;
|
|
169
|
+
DROP TABLE bj_res_r;
|
|
170
|
+
|
|
171
|
+
-- Cleanup
|
|
172
|
+
DROP TABLE bj_left;
|
|
173
|
+
DROP TABLE bj_right;
|
|
174
|
+
DROP TABLE bj_mc1;
|
|
175
|
+
DROP TABLE bj_mc2;
|
|
176
|
+
DROP TABLE bj_nulls;
|
|
177
|
+
DROP TABLE bj_empty;
|
|
178
|
+
DROP TABLE bj_text_l;
|
|
179
|
+
DROP TABLE bj_text_r;
|
|
180
|
+
DROP TABLE bj_dup_l;
|
|
181
|
+
DROP TABLE bj_dup_r;
|
|
182
|
+
DROP TABLE bj_small;
|
|
183
|
+
DROP TABLE bj_large;
|
|
184
|
+
DROP TABLE bj_nocase_l;
|
|
185
|
+
DROP TABLE bj_nocase_r;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
-- merge-join.sqllogic - Merge join algorithm correctness tests
|
|
2
|
+
|
|
3
|
+
-- Setup tables
|
|
4
|
+
CREATE TABLE mj_left (id INTEGER PRIMARY KEY, val INTEGER, label TEXT);
|
|
5
|
+
CREATE TABLE mj_right (id INTEGER PRIMARY KEY, val INTEGER, tag TEXT);
|
|
6
|
+
|
|
7
|
+
INSERT INTO mj_left VALUES (1, 10, 'a'), (2, 20, 'b'), (3, 30, 'c'), (4, 40, 'd'), (5, 10, 'e');
|
|
8
|
+
INSERT INTO mj_right VALUES (100, 10, 'x'), (200, 20, 'y'), (300, 50, 'z'), (400, 10, 'w');
|
|
9
|
+
|
|
10
|
+
-- Basic equi-join
|
|
11
|
+
SELECT l.id, r.id FROM mj_left l JOIN mj_right r ON l.val = r.val ORDER BY l.id, r.id;
|
|
12
|
+
→ [{"id":1,"id:1":100},{"id":1,"id:1":400},{"id":2,"id:1":200},{"id":5,"id:1":100},{"id":5,"id:1":400}]
|
|
13
|
+
|
|
14
|
+
-- LEFT JOIN with merge join
|
|
15
|
+
SELECT l.id, l.val, r.tag FROM mj_left l LEFT JOIN mj_right r ON l.val = r.val ORDER BY l.id, r.tag;
|
|
16
|
+
→ [{"id":1,"val":10,"tag":"w"},{"id":1,"val":10,"tag":"x"},{"id":2,"val":20,"tag":"y"},{"id":3,"val":30,"tag":null},{"id":4,"val":40,"tag":null},{"id":5,"val":10,"tag":"w"},{"id":5,"val":10,"tag":"x"}]
|
|
17
|
+
|
|
18
|
+
-- Multi-column equi-join
|
|
19
|
+
CREATE TABLE mj_mc1 (a INTEGER, b INTEGER, v TEXT, PRIMARY KEY (a, b));
|
|
20
|
+
CREATE TABLE mj_mc2 (x INTEGER, y INTEGER, w TEXT, PRIMARY KEY (x, y));
|
|
21
|
+
|
|
22
|
+
INSERT INTO mj_mc1 VALUES (1, 10, 'alpha'), (2, 20, 'beta'), (3, 30, 'gamma');
|
|
23
|
+
INSERT INTO mj_mc2 VALUES (1, 10, 'one'), (2, 20, 'two'), (9, 99, 'nine');
|
|
24
|
+
|
|
25
|
+
SELECT m1.v, m2.w FROM mj_mc1 m1 JOIN mj_mc2 m2 ON m1.a = m2.x AND m1.b = m2.y ORDER BY m1.a;
|
|
26
|
+
→ [{"v":"alpha","w":"one"},{"v":"beta","w":"two"}]
|
|
27
|
+
|
|
28
|
+
-- NULL handling: NULLs should not match
|
|
29
|
+
CREATE TABLE mj_nulls (id INTEGER PRIMARY KEY, val INTEGER NULL);
|
|
30
|
+
INSERT INTO mj_nulls VALUES (1, null), (2, 10), (3, null), (4, 10);
|
|
31
|
+
|
|
32
|
+
SELECT a.id, b.id FROM mj_nulls a JOIN mj_nulls b ON a.val = b.val ORDER BY a.id, b.id;
|
|
33
|
+
→ [{"id":2,"id:1":2},{"id":2,"id:1":4},{"id":4,"id:1":2},{"id":4,"id:1":4}]
|
|
34
|
+
|
|
35
|
+
-- NULL LEFT JOIN: null keys produce unmatched rows
|
|
36
|
+
SELECT a.id, b.id FROM mj_nulls a LEFT JOIN mj_nulls b ON a.val = b.val ORDER BY a.id, b.id;
|
|
37
|
+
→ [{"id":1,"id:1":null},{"id":2,"id:1":2},{"id":2,"id:1":4},{"id":3,"id:1":null},{"id":4,"id:1":2},{"id":4,"id:1":4}]
|
|
38
|
+
|
|
39
|
+
-- Empty right side
|
|
40
|
+
CREATE TABLE mj_empty (id INTEGER PRIMARY KEY, val INTEGER);
|
|
41
|
+
|
|
42
|
+
SELECT l.id FROM mj_left l JOIN mj_empty e ON l.val = e.val;
|
|
43
|
+
→ []
|
|
44
|
+
|
|
45
|
+
SELECT l.id, e.id FROM mj_left l LEFT JOIN mj_empty e ON l.val = e.val ORDER BY l.id;
|
|
46
|
+
→ [{"id":1,"id:1":null},{"id":2,"id:1":null},{"id":3,"id:1":null},{"id":4,"id:1":null},{"id":5,"id:1":null}]
|
|
47
|
+
|
|
48
|
+
-- Empty left side
|
|
49
|
+
SELECT e.id FROM mj_empty e JOIN mj_right r ON e.val = r.val;
|
|
50
|
+
→ []
|
|
51
|
+
|
|
52
|
+
-- Text column join
|
|
53
|
+
CREATE TABLE mj_text_l (id INTEGER PRIMARY KEY, key TEXT);
|
|
54
|
+
CREATE TABLE mj_text_r (id INTEGER PRIMARY KEY, key TEXT);
|
|
55
|
+
INSERT INTO mj_text_l VALUES (1, 'foo'), (2, 'bar'), (3, 'baz');
|
|
56
|
+
INSERT INTO mj_text_r VALUES (10, 'bar'), (20, 'baz'), (30, 'qux');
|
|
57
|
+
|
|
58
|
+
SELECT l.id, r.id FROM mj_text_l l JOIN mj_text_r r ON l.key = r.key ORDER BY l.id;
|
|
59
|
+
→ [{"id":2,"id:1":10},{"id":3,"id:1":20}]
|
|
60
|
+
|
|
61
|
+
-- USING clause
|
|
62
|
+
SELECT l.id, r.id FROM mj_text_l l JOIN mj_text_r r USING (key) ORDER BY l.id;
|
|
63
|
+
→ [{"id":2,"id:1":10},{"id":3,"id:1":20}]
|
|
64
|
+
|
|
65
|
+
-- Duplicate key runs: multiple matches on both sides
|
|
66
|
+
CREATE TABLE mj_dup_l (id INTEGER PRIMARY KEY, val INTEGER);
|
|
67
|
+
CREATE TABLE mj_dup_r (id INTEGER PRIMARY KEY, val INTEGER);
|
|
68
|
+
INSERT INTO mj_dup_l VALUES (1, 5), (2, 5), (3, 5);
|
|
69
|
+
INSERT INTO mj_dup_r VALUES (10, 5), (20, 5);
|
|
70
|
+
|
|
71
|
+
SELECT count(*) AS cnt FROM mj_dup_l l JOIN mj_dup_r r ON l.val = r.val;
|
|
72
|
+
→ [{"cnt":6}]
|
|
73
|
+
|
|
74
|
+
-- LEFT JOIN with small left, large right
|
|
75
|
+
CREATE TABLE mj_small (id INTEGER PRIMARY KEY, val INTEGER);
|
|
76
|
+
CREATE TABLE mj_large (id INTEGER PRIMARY KEY, val INTEGER);
|
|
77
|
+
INSERT INTO mj_small VALUES (1, 100), (2, 200);
|
|
78
|
+
INSERT INTO mj_large VALUES (10, 100), (20, 100), (30, 300), (40, 400), (50, 500), (60, 600), (70, 700);
|
|
79
|
+
|
|
80
|
+
SELECT s.id, s.val, l.id AS lid FROM mj_small s LEFT JOIN mj_large l ON s.val = l.val ORDER BY s.id, l.id;
|
|
81
|
+
→ [{"id":1,"val":100,"lid":10},{"id":1,"val":100,"lid":20},{"id":2,"val":200,"lid":null}]
|
|
82
|
+
|
|
83
|
+
-- NOCASE collation: merge join must handle case-insensitive matching
|
|
84
|
+
CREATE TABLE mj_nocase_l (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
85
|
+
CREATE TABLE mj_nocase_r (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
86
|
+
INSERT INTO mj_nocase_l VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
87
|
+
INSERT INTO mj_nocase_r VALUES (10, 'alice'), (20, 'BOB'), (30, 'Dave');
|
|
88
|
+
|
|
89
|
+
SELECT l.id, r.id FROM mj_nocase_l l JOIN mj_nocase_r r ON l.name = r.name ORDER BY l.id;
|
|
90
|
+
→ [{"id":1,"id:1":10},{"id":2,"id:1":20}]
|
|
91
|
+
|
|
92
|
+
-- NOCASE LEFT JOIN
|
|
93
|
+
SELECT l.id, l.name, r.id AS rid FROM mj_nocase_l l LEFT JOIN mj_nocase_r r ON l.name = r.name ORDER BY l.id;
|
|
94
|
+
→ [{"id":1,"name":"Alice","rid":10},{"id":2,"name":"Bob","rid":20},{"id":3,"name":"Charlie","rid":null}]
|
|
95
|
+
|
|
96
|
+
-- Asymmetric collation (ticket join-key-collation-resolution-alignment): left
|
|
97
|
+
-- defaults to BINARY, right declares NOCASE. equi-pair-extractor's matched-collation
|
|
98
|
+
-- gate (deliberately kept conservative because the physical ordering property is
|
|
99
|
+
-- collation-blind) demotes this asymmetric pair to the residual, so the join runs
|
|
100
|
+
-- as a nested loop — but the comparison still resolves to NOCASE via the shared
|
|
101
|
+
-- lattice (declared outranks defaulted BINARY), so the result is IDENTICAL to the
|
|
102
|
+
-- symmetric NOCASE join above. The mirror spelling is the regression-prone case
|
|
103
|
+
-- the old left-first emitter rules would have compared under BINARY.
|
|
104
|
+
CREATE TABLE mj_mix_l (id INTEGER PRIMARY KEY, name TEXT);
|
|
105
|
+
CREATE TABLE mj_mix_r (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
106
|
+
INSERT INTO mj_mix_l VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
107
|
+
INSERT INTO mj_mix_r VALUES (10, 'alice'), (20, 'BOB'), (30, 'Dave');
|
|
108
|
+
|
|
109
|
+
SELECT l.id, r.id FROM mj_mix_l l JOIN mj_mix_r r ON l.name = r.name ORDER BY l.id;
|
|
110
|
+
→ [{"id":1,"id:1":10},{"id":2,"id:1":20}]
|
|
111
|
+
|
|
112
|
+
DROP TABLE mj_mix_l;
|
|
113
|
+
DROP TABLE mj_mix_r;
|
|
114
|
+
|
|
115
|
+
-- Real MergeJoin over NOCASE text PK keys: both sides are PK-ordered on a NOCASE
|
|
116
|
+
-- column, so this is an actual MergeJoin (the mj_nocase ON-name join above is a
|
|
117
|
+
-- hash join — name is not ordered there). Exercises the merge emitter's resolved
|
|
118
|
+
-- comparator: the inputs are sorted under NOCASE and the key comparison resolves
|
|
119
|
+
-- to NOCASE through the same lattice, so case-variant keys match in lockstep.
|
|
120
|
+
CREATE TABLE mj_ci_pk_l (name TEXT COLLATE NOCASE PRIMARY KEY, v INTEGER);
|
|
121
|
+
CREATE TABLE mj_ci_pk_r (name TEXT COLLATE NOCASE PRIMARY KEY, w INTEGER);
|
|
122
|
+
INSERT INTO mj_ci_pk_l VALUES ('Alice', 1), ('Bob', 2), ('Carol', 3);
|
|
123
|
+
INSERT INTO mj_ci_pk_r VALUES ('alice', 10), ('BOB', 20), ('dave', 30);
|
|
124
|
+
|
|
125
|
+
SELECT node_type FROM query_plan('SELECT * FROM mj_ci_pk_l l JOIN mj_ci_pk_r r ON l.name = r.name') WHERE node_type LIKE '%Join%';
|
|
126
|
+
→ [{"node_type":"MergeJoin"}]
|
|
127
|
+
|
|
128
|
+
SELECT l.v, r.w FROM mj_ci_pk_l l JOIN mj_ci_pk_r r ON l.name = r.name ORDER BY l.v;
|
|
129
|
+
→ [{"v":1,"w":10},{"v":2,"w":20}]
|
|
130
|
+
|
|
131
|
+
DROP TABLE mj_ci_pk_l;
|
|
132
|
+
DROP TABLE mj_ci_pk_r;
|
|
133
|
+
|
|
134
|
+
-- ===== PK-equi-join tests (trigger MergeJoin via inherent PK ordering) =====
|
|
135
|
+
|
|
136
|
+
-- Setup: single-column PK tables
|
|
137
|
+
CREATE TABLE mj_pk_a (id INTEGER PRIMARY KEY, x TEXT);
|
|
138
|
+
CREATE TABLE mj_pk_b (id INTEGER PRIMARY KEY, y TEXT);
|
|
139
|
+
INSERT INTO mj_pk_a VALUES (1,'a'),(2,'b'),(3,'c');
|
|
140
|
+
INSERT INTO mj_pk_b VALUES (1,'x'),(2,'y'),(3,'z');
|
|
141
|
+
|
|
142
|
+
-- Plan shape: PK-equi-join must use MergeJoin (cost 0.6N beats hash 1.2N)
|
|
143
|
+
SELECT node_type FROM query_plan('SELECT * FROM mj_pk_a JOIN mj_pk_b ON mj_pk_a.id = mj_pk_b.id') WHERE node_type LIKE '%Join%';
|
|
144
|
+
→ [{"node_type":"MergeJoin"}]
|
|
145
|
+
|
|
146
|
+
-- INNER JOIN on PK
|
|
147
|
+
SELECT a.id, a.x, b.y FROM mj_pk_a a JOIN mj_pk_b b ON a.id = b.id ORDER BY a.id;
|
|
148
|
+
→ [{"id":1,"x":"a","y":"x"},{"id":2,"x":"b","y":"y"},{"id":3,"x":"c","y":"z"}]
|
|
149
|
+
|
|
150
|
+
-- LEFT JOIN on PK
|
|
151
|
+
SELECT node_type FROM query_plan('SELECT * FROM mj_pk_a LEFT JOIN mj_pk_b ON mj_pk_a.id = mj_pk_b.id') WHERE node_type LIKE '%Join%';
|
|
152
|
+
→ [{"node_type":"MergeJoin"}]
|
|
153
|
+
|
|
154
|
+
INSERT INTO mj_pk_a VALUES (4,'d');
|
|
155
|
+
|
|
156
|
+
SELECT a.id, a.x, b.y FROM mj_pk_a a LEFT JOIN mj_pk_b b ON a.id = b.id ORDER BY a.id;
|
|
157
|
+
→ [{"id":1,"x":"a","y":"x"},{"id":2,"x":"b","y":"y"},{"id":3,"x":"c","y":"z"},{"id":4,"x":"d","y":null}]
|
|
158
|
+
|
|
159
|
+
DELETE FROM mj_pk_a WHERE id = 4;
|
|
160
|
+
|
|
161
|
+
-- SEMI JOIN (via EXISTS) on PK
|
|
162
|
+
SELECT a.id, a.x FROM mj_pk_a a WHERE EXISTS (SELECT 1 FROM mj_pk_b b WHERE b.id = a.id) ORDER BY a.id;
|
|
163
|
+
→ [{"id":1,"x":"a"},{"id":2,"x":"b"},{"id":3,"x":"c"}]
|
|
164
|
+
|
|
165
|
+
-- ANTI JOIN (via NOT EXISTS) on PK
|
|
166
|
+
INSERT INTO mj_pk_a VALUES (99,'no-match');
|
|
167
|
+
|
|
168
|
+
SELECT a.id, a.x FROM mj_pk_a a WHERE NOT EXISTS (SELECT 1 FROM mj_pk_b b WHERE b.id = a.id) ORDER BY a.id;
|
|
169
|
+
→ [{"id":99,"x":"no-match"}]
|
|
170
|
+
|
|
171
|
+
DELETE FROM mj_pk_a WHERE id = 99;
|
|
172
|
+
|
|
173
|
+
-- Partial overlap: some PKs don't match
|
|
174
|
+
DELETE FROM mj_pk_b WHERE id = 2;
|
|
175
|
+
INSERT INTO mj_pk_b VALUES (5,'v');
|
|
176
|
+
|
|
177
|
+
SELECT a.id, b.id AS bid FROM mj_pk_a a JOIN mj_pk_b b ON a.id = b.id ORDER BY a.id;
|
|
178
|
+
→ [{"id":1,"bid":1},{"id":3,"bid":3}]
|
|
179
|
+
|
|
180
|
+
SELECT a.id, b.id AS bid FROM mj_pk_a a LEFT JOIN mj_pk_b b ON a.id = b.id ORDER BY a.id;
|
|
181
|
+
→ [{"id":1,"bid":1},{"id":2,"bid":null},{"id":3,"bid":3}]
|
|
182
|
+
|
|
183
|
+
-- Multi-column PK merge join
|
|
184
|
+
CREATE TABLE mj_mc_pk1 (k1 INTEGER, k2 INTEGER, v TEXT, PRIMARY KEY (k1, k2));
|
|
185
|
+
CREATE TABLE mj_mc_pk2 (k1 INTEGER, k2 INTEGER, w TEXT, PRIMARY KEY (k1, k2));
|
|
186
|
+
INSERT INTO mj_mc_pk1 VALUES (1,10,'alpha'),(1,20,'beta'),(2,10,'gamma'),(3,30,'delta');
|
|
187
|
+
INSERT INTO mj_mc_pk2 VALUES (1,10,'one'),(1,20,'two'),(2,10,'three'),(9,99,'nine');
|
|
188
|
+
|
|
189
|
+
-- Plan shape: multi-column PK join should also use MergeJoin
|
|
190
|
+
SELECT node_type FROM query_plan('SELECT * FROM mj_mc_pk1 JOIN mj_mc_pk2 ON mj_mc_pk1.k1 = mj_mc_pk2.k1 AND mj_mc_pk1.k2 = mj_mc_pk2.k2') WHERE node_type LIKE '%Join%';
|
|
191
|
+
→ [{"node_type":"MergeJoin"}]
|
|
192
|
+
|
|
193
|
+
SELECT m1.v, m2.w FROM mj_mc_pk1 m1 JOIN mj_mc_pk2 m2 ON m1.k1 = m2.k1 AND m1.k2 = m2.k2 ORDER BY m1.k1, m1.k2;
|
|
194
|
+
→ [{"v":"alpha","w":"one"},{"v":"beta","w":"two"},{"v":"gamma","w":"three"}]
|
|
195
|
+
|
|
196
|
+
SELECT m1.v, m2.w FROM mj_mc_pk1 m1 LEFT JOIN mj_mc_pk2 m2 ON m1.k1 = m2.k1 AND m1.k2 = m2.k2 ORDER BY m1.k1, m1.k2;
|
|
197
|
+
→ [{"v":"alpha","w":"one"},{"v":"beta","w":"two"},{"v":"gamma","w":"three"},{"v":"delta","w":null}]
|
|
198
|
+
|
|
199
|
+
-- Empty table edge case with PK merge join
|
|
200
|
+
CREATE TABLE mj_pk_empty (id INTEGER PRIMARY KEY, z TEXT);
|
|
201
|
+
|
|
202
|
+
SELECT a.id FROM mj_pk_a a JOIN mj_pk_empty e ON a.id = e.id;
|
|
203
|
+
→ []
|
|
204
|
+
|
|
205
|
+
SELECT a.id, e.z FROM mj_pk_a a LEFT JOIN mj_pk_empty e ON a.id = e.id ORDER BY a.id;
|
|
206
|
+
→ [{"id":1,"z":null},{"id":2,"z":null},{"id":3,"z":null}]
|
|
207
|
+
|
|
208
|
+
-- Cleanup PK tables
|
|
209
|
+
DROP TABLE mj_pk_a;
|
|
210
|
+
DROP TABLE mj_pk_b;
|
|
211
|
+
DROP TABLE mj_mc_pk1;
|
|
212
|
+
DROP TABLE mj_mc_pk2;
|
|
213
|
+
DROP TABLE mj_pk_empty;
|
|
214
|
+
|
|
215
|
+
-- ===== Monotonic-merge recognition tests =====
|
|
216
|
+
-- These cover the broader "MonotonicOn on equi-pair" recognition (rule:
|
|
217
|
+
-- monotonic-merge-join). The headline case is a three-way join where the
|
|
218
|
+
-- middle MergeJoin's output advertises MonotonicOn on both equi-pair
|
|
219
|
+
-- attributes (left and right ids), but `physical.ordering` only reflects
|
|
220
|
+
-- the left side; the parent join on the right side's id therefore depends
|
|
221
|
+
-- on the MonotonicOn-aware rule rather than ordering-prefix matching.
|
|
222
|
+
|
|
223
|
+
CREATE TABLE mm_t1 (id INTEGER PRIMARY KEY, x TEXT);
|
|
224
|
+
CREATE TABLE mm_t2 (id INTEGER PRIMARY KEY, y TEXT);
|
|
225
|
+
CREATE TABLE mm_t3 (id INTEGER PRIMARY KEY, z TEXT);
|
|
226
|
+
INSERT INTO mm_t1 VALUES (1,'a'),(2,'b'),(3,'c'),(4,'d');
|
|
227
|
+
INSERT INTO mm_t2 VALUES (1,'x'),(2,'y'),(3,'z');
|
|
228
|
+
INSERT INTO mm_t3 VALUES (1,'p'),(3,'r'),(4,'s');
|
|
229
|
+
|
|
230
|
+
-- Plan shape: three-way join must use MergeJoin at both join levels.
|
|
231
|
+
SELECT count(*) AS cnt FROM query_plan('SELECT * FROM mm_t1 JOIN mm_t2 ON mm_t1.id = mm_t2.id JOIN mm_t3 ON mm_t2.id = mm_t3.id') WHERE node_type = 'MergeJoin';
|
|
232
|
+
→ [{"cnt":2}]
|
|
233
|
+
|
|
234
|
+
-- Three-way INNER JOIN correctness
|
|
235
|
+
SELECT mm_t1.id, x, y, z FROM mm_t1 JOIN mm_t2 ON mm_t1.id = mm_t2.id JOIN mm_t3 ON mm_t2.id = mm_t3.id ORDER BY mm_t1.id;
|
|
236
|
+
→ [{"id":1,"x":"a","y":"x","z":"p"},{"id":3,"x":"c","y":"z","z":"r"}]
|
|
237
|
+
|
|
238
|
+
-- Multi-conjunct ON where only id is monotonic on both sides; the second
|
|
239
|
+
-- conjunct (l.code = r.code) is evaluated as residual without breaking
|
|
240
|
+
-- correctness.
|
|
241
|
+
CREATE TABLE mm_a (id INTEGER PRIMARY KEY, code INTEGER, label TEXT);
|
|
242
|
+
CREATE TABLE mm_b (id INTEGER PRIMARY KEY, code INTEGER, info TEXT);
|
|
243
|
+
INSERT INTO mm_a VALUES (1,100,'a'),(2,200,'b'),(3,200,'c');
|
|
244
|
+
INSERT INTO mm_b VALUES (1,100,'x'),(2,250,'y'),(3,200,'z');
|
|
245
|
+
|
|
246
|
+
SELECT a.id, label, info FROM mm_a a JOIN mm_b b ON a.id = b.id AND a.code = b.code ORDER BY a.id;
|
|
247
|
+
→ [{"id":1,"label":"a","info":"x"},{"id":3,"label":"c","info":"z"}]
|
|
248
|
+
|
|
249
|
+
-- LEFT JOIN with the monotonic-merge rule firing
|
|
250
|
+
SELECT mm_t1.id, x, y FROM mm_t1 LEFT JOIN mm_t2 ON mm_t1.id = mm_t2.id ORDER BY mm_t1.id;
|
|
251
|
+
→ [{"id":1,"x":"a","y":"x"},{"id":2,"x":"b","y":"y"},{"id":3,"x":"c","y":"z"},{"id":4,"x":"d","y":null}]
|
|
252
|
+
|
|
253
|
+
-- SEMI / ANTI join via EXISTS / NOT EXISTS over monotonic equi-pair
|
|
254
|
+
SELECT mm_t1.id FROM mm_t1 WHERE EXISTS (SELECT 1 FROM mm_t2 WHERE mm_t2.id = mm_t1.id) ORDER BY mm_t1.id;
|
|
255
|
+
→ [{"id":1},{"id":2},{"id":3}]
|
|
256
|
+
|
|
257
|
+
SELECT mm_t1.id FROM mm_t1 WHERE NOT EXISTS (SELECT 1 FROM mm_t2 WHERE mm_t2.id = mm_t1.id) ORDER BY mm_t1.id;
|
|
258
|
+
→ [{"id":4}]
|
|
259
|
+
|
|
260
|
+
DROP TABLE mm_t1;
|
|
261
|
+
DROP TABLE mm_t2;
|
|
262
|
+
DROP TABLE mm_t3;
|
|
263
|
+
DROP TABLE mm_a;
|
|
264
|
+
DROP TABLE mm_b;
|
|
265
|
+
|
|
266
|
+
-- Cleanup
|
|
267
|
+
DROP TABLE mj_left;
|
|
268
|
+
DROP TABLE mj_right;
|
|
269
|
+
DROP TABLE mj_mc1;
|
|
270
|
+
DROP TABLE mj_mc2;
|
|
271
|
+
DROP TABLE mj_nulls;
|
|
272
|
+
DROP TABLE mj_empty;
|
|
273
|
+
DROP TABLE mj_text_l;
|
|
274
|
+
DROP TABLE mj_text_r;
|
|
275
|
+
DROP TABLE mj_dup_l;
|
|
276
|
+
DROP TABLE mj_dup_r;
|
|
277
|
+
DROP TABLE mj_small;
|
|
278
|
+
DROP TABLE mj_large;
|
|
279
|
+
DROP TABLE mj_nocase_l;
|
|
280
|
+
DROP TABLE mj_nocase_r;
|