@quereus/quereus 4.20.1 → 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/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,267 @@
|
|
|
1
|
+
-- adapted from SQLite test/join.test, joinB.test, joinH.test
|
|
2
|
+
-- USING clause: Quereus parses USING as an equi-join shortcut but does NOT merge
|
|
3
|
+
-- the shared column into a single output projection (SQLite-specific semantics).
|
|
4
|
+
-- Tests qualify all column references; an unqualified shared name is ambiguous.
|
|
5
|
+
|
|
6
|
+
create table u_a (id integer primary key, k integer, va text);
|
|
7
|
+
create table u_b (id integer primary key, k integer, vb text);
|
|
8
|
+
|
|
9
|
+
insert into u_a values (1, 100, 'a1'), (2, 200, 'a2'), (3, 300, 'a3');
|
|
10
|
+
insert into u_b values (10, 100, 'b1'), (20, 200, 'b2'), (40, 999, 'b4');
|
|
11
|
+
|
|
12
|
+
-- Single-column USING: filters on shared key; both tables' columns remain selectable
|
|
13
|
+
select u_a.id as aid, u_a.k as ak, va, u_b.id as bid, u_b.k as bk, vb from u_a join u_b using (k) order by u_a.id;
|
|
14
|
+
→ [{"aid":1,"ak":100,"va":"a1","bid":10,"bk":100,"vb":"b1"},{"aid":2,"ak":200,"va":"a2","bid":20,"bk":200,"vb":"b2"}]
|
|
15
|
+
|
|
16
|
+
-- Unqualified shared-key reference is ambiguous (SQLite would merge USING column;
|
|
17
|
+
-- Quereus does not).
|
|
18
|
+
select k, va, vb from u_a join u_b using (k) order by u_a.id;
|
|
19
|
+
-- error: ambiguous column name: k
|
|
20
|
+
|
|
21
|
+
-- select * over a USING join exposes both sides' columns with `:N` disambiguation
|
|
22
|
+
-- (matches the same shape as `inner join ... on u_a.k = u_b.k`).
|
|
23
|
+
select * from u_a join u_b using (k) order by u_a.id;
|
|
24
|
+
→ [{"id":1,"k":100,"va":"a1","id:1":10,"k:1":100,"vb":"b1"},{"id":2,"k":200,"va":"a2","id:1":20,"k:1":200,"vb":"b2"}]
|
|
25
|
+
|
|
26
|
+
-- LEFT JOIN with USING preserves left rows; right side is null on miss
|
|
27
|
+
select u_a.id as aid, u_a.k as ak, u_b.id as bid, vb from u_a left join u_b using (k) order by u_a.id;
|
|
28
|
+
→ [{"aid":1,"ak":100,"bid":10,"vb":"b1"},{"aid":2,"ak":200,"bid":20,"vb":"b2"},{"aid":3,"ak":300,"bid":null,"vb":null}]
|
|
29
|
+
|
|
30
|
+
-- select * over a LEFT JOIN ... USING also disambiguates and keeps unmatched right side as nulls.
|
|
31
|
+
select * from u_a left join u_b using (k) order by u_a.id;
|
|
32
|
+
→ [{"id":1,"k":100,"va":"a1","id:1":10,"k:1":100,"vb":"b1"},{"id":2,"k":200,"va":"a2","id:1":20,"k:1":200,"vb":"b2"},{"id":3,"k":300,"va":"a3","id:1":null,"k:1":null,"vb":null}]
|
|
33
|
+
|
|
34
|
+
drop table u_a;
|
|
35
|
+
drop table u_b;
|
|
36
|
+
-- run
|
|
37
|
+
|
|
38
|
+
-- Multi-column USING (a, b): both keys must match
|
|
39
|
+
create table mu_l (a integer, b integer, vl text, primary key (a, b));
|
|
40
|
+
create table mu_r (a integer, b integer, vr text, primary key (a, b));
|
|
41
|
+
|
|
42
|
+
insert into mu_l values (1, 10, 'l1'), (2, 20, 'l2'), (3, 10, 'l3');
|
|
43
|
+
insert into mu_r values (1, 10, 'r1'), (2, 30, 'r2'), (3, 10, 'r3');
|
|
44
|
+
|
|
45
|
+
-- Inner USING join on (a, b) with qualified references
|
|
46
|
+
select mu_l.a as la, mu_l.b as lb, vl, vr from mu_l join mu_r using (a, b) order by mu_l.a, mu_l.b;
|
|
47
|
+
→ [{"la":1,"lb":10,"vl":"l1","vr":"r1"},{"la":3,"lb":10,"vl":"l3","vr":"r3"}]
|
|
48
|
+
|
|
49
|
+
-- LEFT JOIN with multi-column USING
|
|
50
|
+
select mu_l.a as la, mu_l.b as lb, vl, vr from mu_l left join mu_r using (a, b) order by mu_l.a, mu_l.b;
|
|
51
|
+
→ [{"la":1,"lb":10,"vl":"l1","vr":"r1"},{"la":2,"lb":20,"vl":"l2","vr":null},{"la":3,"lb":10,"vl":"l3","vr":"r3"}]
|
|
52
|
+
|
|
53
|
+
drop table mu_l;
|
|
54
|
+
drop table mu_r;
|
|
55
|
+
-- run
|
|
56
|
+
|
|
57
|
+
-- USING with TEXT keys uses default binary collation
|
|
58
|
+
create table cu_a (id integer primary key, name text);
|
|
59
|
+
create table cu_b (id integer primary key, name text);
|
|
60
|
+
|
|
61
|
+
insert into cu_a values (1, 'alice'), (2, 'BOB'), (3, 'carol');
|
|
62
|
+
insert into cu_b values (10, 'alice'), (20, 'bob'), (30, 'carol');
|
|
63
|
+
|
|
64
|
+
-- Default binary collation: 'BOB' != 'bob'
|
|
65
|
+
select cu_a.id as aid, cu_a.name as aname, cu_b.id as bid from cu_a join cu_b using (name) order by cu_a.id;
|
|
66
|
+
→ [{"aid":1,"aname":"alice","bid":10},{"aid":3,"aname":"carol","bid":30}]
|
|
67
|
+
|
|
68
|
+
drop table cu_a;
|
|
69
|
+
drop table cu_b;
|
|
70
|
+
|
|
71
|
+
-- USING with COLLATE NOCASE TEXT keys folds case: the join honors the columns'
|
|
72
|
+
-- collation, so 'BOB' matches 'bob'
|
|
73
|
+
-- (unique-collation-name-case-insensitive-normalization).
|
|
74
|
+
create table cn_a (id integer primary key, name text collate nocase);
|
|
75
|
+
create table cn_b (id integer primary key, name text collate nocase);
|
|
76
|
+
|
|
77
|
+
insert into cn_a values (1, 'alice'), (2, 'BOB'), (3, 'carol');
|
|
78
|
+
insert into cn_b values (10, 'alice'), (20, 'bob'), (30, 'carol');
|
|
79
|
+
|
|
80
|
+
select cn_a.id as aid, cn_a.name as aname, cn_b.id as bid from cn_a join cn_b using (name) order by cn_a.id;
|
|
81
|
+
→ [{"aid":1,"aname":"alice","bid":10},{"aid":2,"aname":"BOB","bid":20},{"aid":3,"aname":"carol","bid":30}]
|
|
82
|
+
|
|
83
|
+
drop table cn_a;
|
|
84
|
+
drop table cn_b;
|
|
85
|
+
-- run
|
|
86
|
+
|
|
87
|
+
-- ===== USING collation precedence (both spellings) + conflict =====
|
|
88
|
+
-- (ticket join-key-collation-resolution-alignment) A USING pair resolves its
|
|
89
|
+
-- comparison collation through the shared provenance lattice
|
|
90
|
+
-- (explicit > declared > default > BINARY), SYMMETRICALLY: a declared NOCASE on
|
|
91
|
+
-- EITHER side governs the join regardless of which side spells it, matching the
|
|
92
|
+
-- equivalent `l.name = r.name`. Fixtures are chosen so BINARY yields 0 matches
|
|
93
|
+
-- and NOCASE yields 3 — the row count alone pins which collation is in effect.
|
|
94
|
+
|
|
95
|
+
-- Left declares NOCASE, right defaults to BINARY → NOCASE wins (folds case).
|
|
96
|
+
create table up_l (id integer primary key, name text collate nocase);
|
|
97
|
+
create table up_r (id integer primary key, name text);
|
|
98
|
+
insert into up_l values (1, 'Alice'), (2, 'BOB'), (3, 'carol');
|
|
99
|
+
insert into up_r values (10, 'alice'), (20, 'bob'), (30, 'CAROL');
|
|
100
|
+
select up_l.id as lid, up_r.id as rid from up_l join up_r using (name) order by up_l.id;
|
|
101
|
+
→ [{"lid":1,"rid":10},{"lid":2,"rid":20},{"lid":3,"rid":30}]
|
|
102
|
+
|
|
103
|
+
-- Mirror spelling: left defaults to BINARY, right declares NOCASE → still NOCASE.
|
|
104
|
+
-- The pre-alignment left-only rule (`leftType ?? BINARY`) would have compared
|
|
105
|
+
-- under BINARY here and matched nothing; the symmetric lattice folds case.
|
|
106
|
+
create table um_l (id integer primary key, name text);
|
|
107
|
+
create table um_r (id integer primary key, name text collate nocase);
|
|
108
|
+
insert into um_l values (1, 'Alice'), (2, 'BOB'), (3, 'carol');
|
|
109
|
+
insert into um_r values (10, 'alice'), (20, 'bob'), (30, 'CAROL');
|
|
110
|
+
select um_l.id as lid, um_r.id as rid from um_l join um_r using (name) order by um_l.id;
|
|
111
|
+
→ [{"lid":1,"rid":10},{"lid":2,"rid":20},{"lid":3,"rid":30}]
|
|
112
|
+
|
|
113
|
+
drop table up_l;
|
|
114
|
+
drop table up_r;
|
|
115
|
+
drop table um_l;
|
|
116
|
+
drop table um_r;
|
|
117
|
+
-- run
|
|
118
|
+
|
|
119
|
+
-- Conflicting DECLARED collations on a USING pair → plan-time ambiguous-collation
|
|
120
|
+
-- error, raised at build: `using (name)` desugars to the `l.name = r.name`
|
|
121
|
+
-- BinaryOpNode, whose type generation reports the conflict. The spelled-out ON
|
|
122
|
+
-- form must error IDENTICALLY (uniformity between USING and ON).
|
|
123
|
+
create table cf_a (id integer primary key, name text collate nocase);
|
|
124
|
+
create table cf_b (id integer primary key, name text collate rtrim);
|
|
125
|
+
insert into cf_a values (1, 'bob');
|
|
126
|
+
insert into cf_b values (10, 'BOB');
|
|
127
|
+
-- run
|
|
128
|
+
|
|
129
|
+
select cf_a.id from cf_a join cf_b using (name);
|
|
130
|
+
-- error: ambiguous collation
|
|
131
|
+
|
|
132
|
+
select cf_a.id from cf_a join cf_b on cf_a.name = cf_b.name;
|
|
133
|
+
-- error: ambiguous collation
|
|
134
|
+
|
|
135
|
+
-- An explicit COLLATE on the comparison resolves the ambiguity (rank-3 explicit
|
|
136
|
+
-- outranks both declared collations), so the same join builds and runs. USING has
|
|
137
|
+
-- no per-column COLLATE syntax, so pin the resolution via the ON form: under the
|
|
138
|
+
-- explicit NOCASE, 'bob' matches 'BOB'.
|
|
139
|
+
select cf_a.id as aid, cf_b.id as bid from cf_a join cf_b on cf_a.name = cf_b.name collate nocase order by cf_a.id;
|
|
140
|
+
→ [{"aid":1,"bid":10}]
|
|
141
|
+
|
|
142
|
+
drop table cf_a;
|
|
143
|
+
drop table cf_b;
|
|
144
|
+
-- run
|
|
145
|
+
|
|
146
|
+
-- ===== NULL USING keys never match =====
|
|
147
|
+
-- `using (k)` is `l.k = r.k`, and `null = null` is UNKNOWN, so two NULL keys do
|
|
148
|
+
-- NOT join — on either physical path. The nested-loop emitter compares through an
|
|
149
|
+
-- ORDERING comparator, which ranks NULL/NULL as equal, so it carries its own NULL
|
|
150
|
+
-- guard; the hash path drops NULL keys by construction. The two must agree with
|
|
151
|
+
-- each other and with the spelled-out ON form.
|
|
152
|
+
create table nu_l (id integer primary key, k text null);
|
|
153
|
+
create table nu_r (id integer primary key, k text null);
|
|
154
|
+
insert into nu_l values (1, null), (2, 'a');
|
|
155
|
+
insert into nu_r values (10, null), (20, 'a');
|
|
156
|
+
|
|
157
|
+
select nu_l.id as lid, nu_r.id as rid from nu_l join nu_r using (k) order by nu_l.id;
|
|
158
|
+
→ [{"lid":2,"rid":20}]
|
|
159
|
+
|
|
160
|
+
select nu_l.id as lid, nu_r.id as rid from nu_l join nu_r on nu_l.k = nu_r.k order by nu_l.id;
|
|
161
|
+
→ [{"lid":2,"rid":20}]
|
|
162
|
+
|
|
163
|
+
-- LEFT JOIN: the NULL-keyed left row survives, null-extended, rather than pairing
|
|
164
|
+
-- with the NULL-keyed right row.
|
|
165
|
+
select nu_l.id as lid, nu_r.id as rid from nu_l left join nu_r using (k) order by nu_l.id;
|
|
166
|
+
→ [{"lid":1,"rid":null},{"lid":2,"rid":20}]
|
|
167
|
+
|
|
168
|
+
drop table nu_l;
|
|
169
|
+
drop table nu_r;
|
|
170
|
+
-- run
|
|
171
|
+
|
|
172
|
+
-- ===== Cross-type USING pairs coerce exactly as the ON form does =====
|
|
173
|
+
-- (ticket bug-using-join-skips-cross-type-coercion) `using (k)` desugars to
|
|
174
|
+
-- `l.k = r.k`, so `insertCrossTypeCoercion` reconciles the pair: a JSON column
|
|
175
|
+
-- against a TEXT one compares structurally (key order irrelevant), and an INTEGER
|
|
176
|
+
-- against a TEXT one compares numerically. Before the desugar USING compared raw
|
|
177
|
+
-- storage classes and returned 0 rows where the ON spelling returned the row.
|
|
178
|
+
create table xj_l (id integer primary key, k json);
|
|
179
|
+
create table xj_r (id integer primary key, k text);
|
|
180
|
+
insert into xj_l values (1, '{"a":1,"b":2}');
|
|
181
|
+
insert into xj_r values (10, '{"b":2,"a":1}');
|
|
182
|
+
|
|
183
|
+
select xj_l.id as lid, xj_r.id as rid from xj_l join xj_r using (k);
|
|
184
|
+
→ [{"lid":1,"rid":10}]
|
|
185
|
+
|
|
186
|
+
select xj_l.id as lid, xj_r.id as rid from xj_l join xj_r on xj_l.k = xj_r.k;
|
|
187
|
+
→ [{"lid":1,"rid":10}]
|
|
188
|
+
|
|
189
|
+
create table xn_l (id integer primary key, k integer);
|
|
190
|
+
create table xn_r (id integer primary key, k text);
|
|
191
|
+
insert into xn_l values (1, 5), (2, 6);
|
|
192
|
+
insert into xn_r values (10, '5'), (20, 'six');
|
|
193
|
+
|
|
194
|
+
select xn_l.id as lid, xn_r.id as rid from xn_l join xn_r using (k);
|
|
195
|
+
→ [{"lid":1,"rid":10}]
|
|
196
|
+
|
|
197
|
+
select xn_l.id as lid, xn_r.id as rid from xn_l join xn_r on xn_l.k = xn_r.k;
|
|
198
|
+
→ [{"lid":1,"rid":10}]
|
|
199
|
+
|
|
200
|
+
-- A cross-type USING pair runs as nested loop (the coerced operand is a CastNode, so
|
|
201
|
+
-- no equi-pair is extracted), which is the path an outer join's null-extension takes.
|
|
202
|
+
-- The unmatched left row must still survive, and the ON form must agree.
|
|
203
|
+
select xn_l.id as lid, xn_r.id as rid from xn_l left join xn_r using (k) order by xn_l.id;
|
|
204
|
+
→ [{"lid":1,"rid":10},{"lid":2,"rid":null}]
|
|
205
|
+
|
|
206
|
+
select xn_l.id as lid, xn_r.id as rid from xn_l left join xn_r on xn_l.k = xn_r.k order by xn_l.id;
|
|
207
|
+
→ [{"lid":1,"rid":10},{"lid":2,"rid":null}]
|
|
208
|
+
|
|
209
|
+
drop table xj_l;
|
|
210
|
+
drop table xj_r;
|
|
211
|
+
drop table xn_l;
|
|
212
|
+
drop table xn_r;
|
|
213
|
+
-- run
|
|
214
|
+
|
|
215
|
+
-- Multi-column USING where only ONE column is cross-type: the same-type column still
|
|
216
|
+
-- keys the join and the coerced one is evaluated as a residual predicate. Both must
|
|
217
|
+
-- apply — row (2,'7') differs on the coerced column and must not join.
|
|
218
|
+
create table xm_l (id integer primary key, a integer, b integer);
|
|
219
|
+
create table xm_r (id integer primary key, a integer, b text);
|
|
220
|
+
insert into xm_l values (1, 100, 5), (2, 200, 6);
|
|
221
|
+
insert into xm_r values (10, 100, '5'), (20, 200, '7');
|
|
222
|
+
|
|
223
|
+
select xm_l.id as lid, xm_r.id as rid from xm_l join xm_r using (a, b) order by xm_l.id;
|
|
224
|
+
→ [{"lid":1,"rid":10}]
|
|
225
|
+
|
|
226
|
+
select xm_l.id as lid, xm_r.id as rid from xm_l join xm_r on xm_l.a = xm_r.a and xm_l.b = xm_r.b order by xm_l.id;
|
|
227
|
+
→ [{"lid":1,"rid":10}]
|
|
228
|
+
|
|
229
|
+
drop table xm_l;
|
|
230
|
+
drop table xm_r;
|
|
231
|
+
-- run
|
|
232
|
+
|
|
233
|
+
-- ===== A USING column absent from a side is an error, not a silent empty result =====
|
|
234
|
+
-- Formerly the emitter's USING comparator returned false for an unresolved column
|
|
235
|
+
-- index, so the join produced zero rows and raised nothing.
|
|
236
|
+
create table mi_l (id integer primary key, k integer);
|
|
237
|
+
create table mi_r (id integer primary key, k integer);
|
|
238
|
+
insert into mi_l values (1, 10);
|
|
239
|
+
insert into mi_r values (10, 10);
|
|
240
|
+
-- run
|
|
241
|
+
|
|
242
|
+
select mi_l.id from mi_l join mi_r using (zzz);
|
|
243
|
+
-- error: USING column not found on left side of join: zzz
|
|
244
|
+
|
|
245
|
+
drop table mi_l;
|
|
246
|
+
drop table mi_r;
|
|
247
|
+
-- run
|
|
248
|
+
|
|
249
|
+
-- ===== Three-way USING: first-match-per-side pairing =====
|
|
250
|
+
-- The second join's left side (the a-b join) carries TWO `k` attributes. The pair
|
|
251
|
+
-- is formed from the FIRST match on each side, i.e. `t3a.k = t3c.k` — pinned by
|
|
252
|
+
-- making the first join a LEFT join so a null-extended row has `t3b.k` NULL while
|
|
253
|
+
-- `t3a.k` still holds 20. Pairing on `t3b.k` would drop that row.
|
|
254
|
+
create table t3a (id integer primary key, k integer);
|
|
255
|
+
create table t3b (id integer primary key, k integer);
|
|
256
|
+
create table t3c (id integer primary key, k integer);
|
|
257
|
+
insert into t3a values (1, 10), (2, 20);
|
|
258
|
+
insert into t3b values (11, 10);
|
|
259
|
+
insert into t3c values (111, 10), (222, 20);
|
|
260
|
+
|
|
261
|
+
select t3a.id as aid, t3b.id as bid, t3c.id as cid
|
|
262
|
+
from t3a left join t3b using (k) join t3c using (k) order by t3a.id;
|
|
263
|
+
→ [{"aid":1,"bid":11,"cid":111},{"aid":2,"bid":null,"cid":222}]
|
|
264
|
+
|
|
265
|
+
drop table t3a;
|
|
266
|
+
drop table t3b;
|
|
267
|
+
drop table t3c;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- adapted from SQLite test/join3.test, join.test
|
|
2
|
+
-- Quereus does NOT support SQLite's comma syntax in FROM (implicit cross join).
|
|
3
|
+
-- Use explicit JOIN instead. This file pins the rejection.
|
|
4
|
+
|
|
5
|
+
create table cj_a (id integer primary key, va text);
|
|
6
|
+
create table cj_b (id integer primary key, vb text);
|
|
7
|
+
create table cj_c (id integer primary key, vc text);
|
|
8
|
+
|
|
9
|
+
insert into cj_a values (1, 'a1'), (2, 'a2');
|
|
10
|
+
insert into cj_b values (10, 'b1'), (20, 'b2'), (30, 'b3');
|
|
11
|
+
insert into cj_c values (100, 'c1'), (200, 'c2');
|
|
12
|
+
-- run
|
|
13
|
+
|
|
14
|
+
-- Two-table comma list: not supported
|
|
15
|
+
select count(*) as cnt from cj_a, cj_b;
|
|
16
|
+
-- error: multiple FROM sources
|
|
17
|
+
|
|
18
|
+
-- Two-table comma list with WHERE join condition: not supported
|
|
19
|
+
select cj_a.id, cj_b.id from cj_a, cj_b where cj_a.id = cj_b.id - 9;
|
|
20
|
+
-- error: multiple FROM sources
|
|
21
|
+
|
|
22
|
+
-- Three-table comma list: not supported
|
|
23
|
+
select cj_a.id, cj_b.id, cj_c.id from cj_a, cj_b, cj_c
|
|
24
|
+
where cj_a.id = cj_b.id - 9 and cj_b.id = cj_c.id - 90;
|
|
25
|
+
-- error: multiple FROM sources
|
|
26
|
+
|
|
27
|
+
-- Comma mixed with explicit JOIN: still rejected because of the leading comma list
|
|
28
|
+
select cj_a.id, cj_b.id, cj_c.id
|
|
29
|
+
from cj_a, cj_b join cj_c on cj_b.id = cj_c.id - 90
|
|
30
|
+
where cj_a.id = 2;
|
|
31
|
+
-- error: multiple FROM sources
|
|
32
|
+
|
|
33
|
+
drop table cj_a;
|
|
34
|
+
drop table cj_b;
|
|
35
|
+
drop table cj_c;
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
-- 11.3-index-nested-loop-join.sqllogic
|
|
2
|
+
-- Result-equality coverage for the index-nested-loop join (rule-join-physical-selection
|
|
3
|
+
-- + rules/join/index-nested-loop.ts): a small outer joined to a large indexed inner
|
|
4
|
+
-- replaces the inner walk with one IndexSeek per outer row. Every assertion here is
|
|
5
|
+
-- pure row equality, so it holds whichever physical join the optimizer picks — the
|
|
6
|
+
-- plan-shape assertions (that these shapes DO pick the seek) live in
|
|
7
|
+
-- test/optimizer/index-nested-loop.spec.ts.
|
|
8
|
+
|
|
9
|
+
-- Small outer: one row with a NULL join key, one with a key matching nothing.
|
|
10
|
+
CREATE TABLE inl_s (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
11
|
+
INSERT INTO inl_s VALUES (1, 5), (2, 7), (3, 500), (4, NULL);
|
|
12
|
+
|
|
13
|
+
-- Large inner: v secondary-indexed; one row with a NULL v (must never match).
|
|
14
|
+
CREATE TABLE inl_big (id INTEGER PRIMARY KEY, v INTEGER NULL, w INTEGER);
|
|
15
|
+
CREATE INDEX inl_idx_v ON inl_big(v);
|
|
16
|
+
INSERT INTO inl_big VALUES
|
|
17
|
+
(1, 1, 1), (2, 2, 2), (3, 3, 3), (4, 4, 4), (5, 5, 0), (6, 6, 1), (7, 7, 2), (8, 8, 3),
|
|
18
|
+
(9, 9, 4), (10, 10, 0), (11, 11, 1), (12, 12, 2), (13, 13, 3), (14, 14, 4), (15, 15, 0),
|
|
19
|
+
(16, 16, 1), (17, 17, 2), (18, 18, 3), (19, 19, 4), (20, 20, 0), (21, 21, 1), (22, 22, 2),
|
|
20
|
+
(23, 23, 3), (24, 24, 4), (25, 25, 0), (26, 26, 1), (27, 27, 2), (28, 28, 3), (29, 29, 4),
|
|
21
|
+
(30, 30, 0), (31, 31, 1), (32, 32, 2), (33, 33, 3), (34, 34, 4), (35, 35, 0), (36, 36, 1),
|
|
22
|
+
(37, 37, 2), (38, 38, 3), (39, 39, 4), (40, 40, 0), (41, NULL, 0);
|
|
23
|
+
ANALYZE;
|
|
24
|
+
|
|
25
|
+
-- INNER: only the two matching outer rows survive
|
|
26
|
+
SELECT s.id, b.id AS bid FROM inl_s s JOIN inl_big b ON b.v = s.k ORDER BY s.id;
|
|
27
|
+
→ [{"id":1,"bid":5},{"id":2,"bid":7}]
|
|
28
|
+
|
|
29
|
+
-- LEFT: unmatched (k=500) and NULL-key outer rows null-pad — `NULL = v` is UNKNOWN
|
|
30
|
+
SELECT s.id, b.id AS bid FROM inl_s s LEFT JOIN inl_big b ON b.v = s.k ORDER BY s.id;
|
|
31
|
+
→ [{"id":1,"bid":5},{"id":2,"bid":7},{"id":3,"bid":null},{"id":4,"bid":null}]
|
|
32
|
+
|
|
33
|
+
-- SEMI (EXISTS): first-match short-circuit; NULL key drops
|
|
34
|
+
SELECT s.id FROM inl_s s WHERE EXISTS (SELECT 1 FROM inl_big b WHERE b.v = s.k) ORDER BY s.id;
|
|
35
|
+
→ [{"id":1},{"id":2}]
|
|
36
|
+
|
|
37
|
+
-- ANTI (NOT EXISTS): unmatched AND NULL-key outer rows are kept
|
|
38
|
+
SELECT s.id FROM inl_s s WHERE NOT EXISTS (SELECT 1 FROM inl_big b WHERE b.v = s.k) ORDER BY s.id;
|
|
39
|
+
→ [{"id":3},{"id":4}]
|
|
40
|
+
|
|
41
|
+
-- The inner NULL-v row (id 41) matches nothing: NULL keys miss from BOTH sides
|
|
42
|
+
SELECT count(*) AS n FROM inl_s s JOIN inl_big b ON b.v = s.k;
|
|
43
|
+
→ [{"n":2}]
|
|
44
|
+
|
|
45
|
+
-- `exists … as` flag joins keep the nested-loop driver, so they CAN take the seek
|
|
46
|
+
-- path — the flag bit must survive and report per-row match truthfully
|
|
47
|
+
SELECT s.id, has_b FROM inl_s s LEFT JOIN inl_big b ON b.v = s.k EXISTS RIGHT AS has_b ORDER BY s.id;
|
|
48
|
+
→ [{"id":1,"has_b":true},{"id":2,"has_b":true},{"id":3,"has_b":false},{"id":4,"has_b":false}]
|
|
49
|
+
|
|
50
|
+
-- Self-join: two access sites on the same table must not share a cursor (the
|
|
51
|
+
-- outer walk and the per-row inner seek are distinct scan sites)
|
|
52
|
+
SELECT a.id, b.id AS bid FROM inl_big a JOIN inl_big b ON b.v = a.w WHERE a.id <= 3 ORDER BY a.id;
|
|
53
|
+
→ [{"id":1,"bid":1},{"id":2,"bid":2},{"id":3,"bid":3}]
|
|
54
|
+
|
|
55
|
+
-- Three-way left-deep spine where the seek key comes from the INNER-MOST relation:
|
|
56
|
+
-- at the final join the outer row is the composite [t1, t2] row and the t1
|
|
57
|
+
-- attribute must still resolve inside the seek
|
|
58
|
+
CREATE TABLE inl_t1 (id INTEGER PRIMARY KEY, k INTEGER);
|
|
59
|
+
CREATE TABLE inl_t2 (id INTEGER PRIMARY KEY, t1_id INTEGER);
|
|
60
|
+
INSERT INTO inl_t1 VALUES (1, 5), (2, 7);
|
|
61
|
+
INSERT INTO inl_t2 VALUES (10, 1), (20, 2);
|
|
62
|
+
ANALYZE;
|
|
63
|
+
SELECT t1.id, t2.id AS t2id, b.id AS bid
|
|
64
|
+
FROM inl_t1 t1 JOIN inl_t2 t2 ON t2.t1_id = t1.id JOIN inl_big b ON b.v = t1.k
|
|
65
|
+
ORDER BY t1.id;
|
|
66
|
+
→ [{"id":1,"t2id":10,"bid":5},{"id":2,"t2id":20,"bid":7}]
|
|
67
|
+
|
|
68
|
+
-- ===== Composite and partial-composite keys =====
|
|
69
|
+
|
|
70
|
+
CREATE TABLE inl_c (a INTEGER, b INTEGER, val TEXT, PRIMARY KEY (a, b));
|
|
71
|
+
INSERT INTO inl_c VALUES
|
|
72
|
+
(1, 1, 'c1'), (2, 2, 'c2'), (3, 3, 'c3'), (4, 4, 'c4'), (0, 5, 'c5'),
|
|
73
|
+
(1, 6, 'c6'), (2, 7, 'c7'), (3, 8, 'c8'), (4, 9, 'c9'), (0, 10, 'c10'),
|
|
74
|
+
(1, 11, 'c11'), (2, 12, 'c12'), (3, 13, 'c13'), (4, 14, 'c14'), (0, 15, 'c15'),
|
|
75
|
+
(1, 16, 'c16'), (2, 17, 'c17'), (3, 18, 'c18'), (4, 19, 'c19'), (0, 20, 'c20'),
|
|
76
|
+
(1, 21, 'c21'), (2, 22, 'c22'), (3, 23, 'c23'), (4, 24, 'c24'), (0, 25, 'c25'),
|
|
77
|
+
(1, 26, 'c26'), (2, 27, 'c27'), (3, 28, 'c28'), (4, 29, 'c29'), (0, 30, 'c30');
|
|
78
|
+
CREATE TABLE inl_s2 (id INTEGER PRIMARY KEY, x INTEGER NULL, y INTEGER NULL, name TEXT);
|
|
79
|
+
INSERT INTO inl_s2 VALUES (1, 3, 3, 'c3'), (2, 2, 7, 'c12'), (3, 4, 999, 'zzz'), (4, NULL, 1, 'c1');
|
|
80
|
+
ANALYZE;
|
|
81
|
+
|
|
82
|
+
-- Both equi pairs covered by the composite PK: one composite seek per outer row
|
|
83
|
+
SELECT s2.id, c.val FROM inl_s2 s2 JOIN inl_c c ON c.a = s2.x AND c.b = s2.y ORDER BY s2.id;
|
|
84
|
+
→ [{"id":1,"val":"c3"},{"id":2,"val":"c7"}]
|
|
85
|
+
|
|
86
|
+
-- Partial composite: the index covers only the `a = x` pair; `val = name` stays
|
|
87
|
+
-- enforced by the retained condition (id 2: x=2 candidates are c2,c7,c12,… — only
|
|
88
|
+
-- c12's val matches its name)
|
|
89
|
+
SELECT s2.id, c.b AS cb FROM inl_s2 s2 JOIN inl_c c ON c.a = s2.x AND c.val = s2.name ORDER BY s2.id;
|
|
90
|
+
→ [{"id":1,"cb":3},{"id":2,"cb":12}]
|
|
91
|
+
|
|
92
|
+
-- ===== LATERAL with an ON equality (the sibling-reference guard) =====
|
|
93
|
+
-- Before the guard in rule-join-physical-selection, a LATERAL right side reading
|
|
94
|
+
-- the left side's columns with an equality ON was converted to a hash join,
|
|
95
|
+
-- which drains the right side before any left row exists — "No row context
|
|
96
|
+
-- found" at runtime. The guard keeps the nested-loop driver.
|
|
97
|
+
|
|
98
|
+
CREATE TABLE inl_lt (k INTEGER PRIMARY KEY, v INTEGER);
|
|
99
|
+
CREATE TABLE inl_lu (k INTEGER PRIMARY KEY, v INTEGER);
|
|
100
|
+
INSERT INTO inl_lt VALUES (1, 10), (2, 20);
|
|
101
|
+
INSERT INTO inl_lu VALUES (1, 10), (2, 99);
|
|
102
|
+
|
|
103
|
+
SELECT t.k AS tk, x.k AS xk, x.v AS xv
|
|
104
|
+
FROM inl_lt t JOIN LATERAL (SELECT * FROM inl_lu u WHERE u.k = t.k) x ON x.v = t.v;
|
|
105
|
+
→ [{"tk":1,"xk":1,"xv":10}]
|
|
106
|
+
|
|
107
|
+
SELECT t.k AS tk, x.k AS xk
|
|
108
|
+
FROM inl_lt t LEFT JOIN LATERAL (SELECT * FROM inl_lu u WHERE u.k = t.k) x ON x.v = t.v
|
|
109
|
+
ORDER BY tk;
|
|
110
|
+
→ [{"tk":1,"xk":1},{"tk":2,"xk":null}]
|
|
111
|
+
|
|
112
|
+
-- ===== Collation: both directions of a NOCASE / BINARY join =====
|
|
113
|
+
|
|
114
|
+
-- Inner column declared NOCASE (its index inherits NOCASE); outer defaulted
|
|
115
|
+
-- BINARY. Declared beats defaulted, so the join compares NOCASE and the index
|
|
116
|
+
-- cover matches — the seek path is eligible, and 'n3' must match 'N3'.
|
|
117
|
+
CREATE TABLE inl_cs (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
118
|
+
CREATE INDEX inl_idx_cs ON inl_cs(name);
|
|
119
|
+
INSERT INTO inl_cs VALUES
|
|
120
|
+
(1, 'N1'), (2, 'N2'), (3, 'N3'), (4, 'N4'), (5, 'N5'), (6, 'N6'), (7, 'N7'), (8, 'N8'),
|
|
121
|
+
(9, 'N9'), (10, 'N10'), (11, 'N11'), (12, 'N12'), (13, 'N13'), (14, 'N14'), (15, 'N15'),
|
|
122
|
+
(16, 'N16'), (17, 'N17'), (18, 'N18'), (19, 'N19'), (20, 'N20');
|
|
123
|
+
CREATE TABLE inl_co (id INTEGER PRIMARY KEY, name TEXT);
|
|
124
|
+
INSERT INTO inl_co VALUES (1, 'n3'), (2, 'N5'), (3, 'zzz');
|
|
125
|
+
ANALYZE;
|
|
126
|
+
SELECT o.id, i.id AS iid FROM inl_co o JOIN inl_cs i ON i.name = o.name ORDER BY o.id;
|
|
127
|
+
→ [{"id":1,"iid":3},{"id":2,"iid":5}]
|
|
128
|
+
|
|
129
|
+
-- Reverse direction: outer declared NOCASE, inner index BINARY — a raw seek
|
|
130
|
+
-- would under-fetch, so the rule declines and the surviving hash join must
|
|
131
|
+
-- return BOTH case variants of 'A1'.
|
|
132
|
+
CREATE TABLE inl_bs (id INTEGER PRIMARY KEY, name TEXT);
|
|
133
|
+
CREATE INDEX inl_idx_bs ON inl_bs(name);
|
|
134
|
+
INSERT INTO inl_bs VALUES
|
|
135
|
+
(1, 'B1'), (2, 'B2'), (3, 'B3'), (4, 'B4'), (5, 'B5'), (6, 'B6'), (7, 'B7'), (8, 'B8'),
|
|
136
|
+
(9, 'B9'), (10, 'B10'), (11, 'B11'), (12, 'B12'), (13, 'B13'), (14, 'B14'), (15, 'B15'),
|
|
137
|
+
(16, 'B16'), (17, 'B17'), (18, 'B18'), (19, 'B19'), (20, 'B20'), (21, 'a1'), (22, 'A1');
|
|
138
|
+
CREATE TABLE inl_no (id INTEGER PRIMARY KEY, name TEXT COLLATE NOCASE);
|
|
139
|
+
INSERT INTO inl_no VALUES (1, 'A1');
|
|
140
|
+
ANALYZE;
|
|
141
|
+
SELECT o.id, b.id AS bid FROM inl_no o JOIN inl_bs b ON b.name = o.name ORDER BY bid;
|
|
142
|
+
→ [{"id":1,"bid":21},{"id":1,"bid":22}]
|
|
143
|
+
|
|
144
|
+
-- ===== Seek-side election: the index-nested-loop is offered in BOTH orientations =====
|
|
145
|
+
-- The parent/child rollup from ticket feat-index-nested-loop-seek-side-election: a
|
|
146
|
+
-- filtered parent (`txn`, ≈10 rows per entity) and an FK-indexed child (`entry`).
|
|
147
|
+
-- Whichever table is named first, the optimizer drives from the filtered parent and
|
|
148
|
+
-- seeks the child — the second spelling below is rebuilt as a JoinNode with its
|
|
149
|
+
-- children exchanged. Both must return the rows a plain nested loop returns, which
|
|
150
|
+
-- the third query forces (`+ 0` on the key is not a bare column, so no equi pair is
|
|
151
|
+
-- extracted and no physical join algorithm — hash, merge, or seek — competes).
|
|
152
|
+
|
|
153
|
+
CREATE TABLE inl_txn (id INTEGER PRIMARY KEY, entity_id INTEGER, date TEXT);
|
|
154
|
+
CREATE TABLE inl_entry (id INTEGER PRIMARY KEY, txn_id INTEGER, amount REAL);
|
|
155
|
+
CREATE INDEX inl_txn_entity ON inl_txn (entity_id, date);
|
|
156
|
+
CREATE INDEX inl_entry_txn ON inl_entry (txn_id);
|
|
157
|
+
INSERT INTO inl_txn VALUES
|
|
158
|
+
(1, 1, '2024-01-01'), (2, 2, '2024-01-02'), (3, 3, '2024-01-03'), (4, 1, '2024-01-04'),
|
|
159
|
+
(5, 2, '2024-01-05'), (6, 3, '2024-01-06'), (7, 1, '2024-01-07'), (8, 2, '2024-01-08'),
|
|
160
|
+
(9, 3, '2024-01-09'), (10, 1, '2024-01-10'), (11, 2, '2024-01-11'), (12, 3, '2024-01-12'),
|
|
161
|
+
(13, 1, '2024-01-13'), (14, 2, '2024-01-14'), (15, 3, '2024-01-15'), (16, 1, '2024-01-16'),
|
|
162
|
+
(17, 2, '2024-01-17'), (18, 3, '2024-01-18'), (19, 1, '2024-01-19'), (20, 2, '2024-01-20'),
|
|
163
|
+
(21, 3, '2024-01-21'), (22, 1, '2024-01-22'), (23, 2, '2024-01-23'), (24, 3, '2024-01-24');
|
|
164
|
+
INSERT INTO inl_entry VALUES
|
|
165
|
+
(1, 1, 1.5), (2, 2, 3.0), (3, 3, 4.5), (4, 4, 6.0), (5, 5, 7.5), (6, 6, 9.0), (7, 7, 10.5),
|
|
166
|
+
(8, 8, 12.0), (9, 9, 13.5), (10, 10, 15.0), (11, 11, 16.5), (12, 12, 18.0), (13, 13, 19.5),
|
|
167
|
+
(14, 14, 21.0), (15, 15, 22.5), (16, 16, 24.0), (17, 17, 25.5), (18, 18, 27.0), (19, 19, 28.5),
|
|
168
|
+
(20, 20, 30.0), (21, 21, 31.5), (22, 22, 33.0), (23, 23, 34.5), (24, 24, 36.0),
|
|
169
|
+
(25, 1, 0.5), (26, 4, 0.5), (27, 7, 0.5), (28, 10, 0.5), (29, 13, 0.5), (30, 16, 0.5),
|
|
170
|
+
(31, 19, 0.5), (32, 22, 0.5), (33, 2, 100.0), (34, 999, 1.0);
|
|
171
|
+
ANALYZE;
|
|
172
|
+
|
|
173
|
+
-- Filtered parent named first (the un-mirrored orientation)
|
|
174
|
+
SELECT e.txn_id, sum(e.amount) AS total
|
|
175
|
+
FROM inl_txn t JOIN inl_entry e ON t.id = e.txn_id
|
|
176
|
+
WHERE t.entity_id = 1 GROUP BY e.txn_id ORDER BY e.txn_id;
|
|
177
|
+
→ [{"txn_id":1,"total":2},{"txn_id":4,"total":6.5},{"txn_id":7,"total":11},{"txn_id":10,"total":15.5},{"txn_id":13,"total":20},{"txn_id":16,"total":24.5},{"txn_id":19,"total":29},{"txn_id":22,"total":33.5}]
|
|
178
|
+
|
|
179
|
+
-- Child named first (the mirrored orientation: same plan shape, sides exchanged)
|
|
180
|
+
SELECT e.txn_id, sum(e.amount) AS total
|
|
181
|
+
FROM inl_entry e JOIN inl_txn t ON t.id = e.txn_id
|
|
182
|
+
WHERE t.entity_id = 1 GROUP BY e.txn_id ORDER BY e.txn_id;
|
|
183
|
+
→ [{"txn_id":1,"total":2},{"txn_id":4,"total":6.5},{"txn_id":7,"total":11},{"txn_id":10,"total":15.5},{"txn_id":13,"total":20},{"txn_id":16,"total":24.5},{"txn_id":19,"total":29},{"txn_id":22,"total":33.5}]
|
|
184
|
+
|
|
185
|
+
-- Forced away from every equi-join rewrite: the plain nested-loop answer
|
|
186
|
+
SELECT e.txn_id, sum(e.amount) AS total
|
|
187
|
+
FROM inl_entry e JOIN inl_txn t ON t.id = e.txn_id + 0
|
|
188
|
+
WHERE t.entity_id = 1 GROUP BY e.txn_id ORDER BY e.txn_id;
|
|
189
|
+
→ [{"txn_id":1,"total":2},{"txn_id":4,"total":6.5},{"txn_id":7,"total":11},{"txn_id":10,"total":15.5},{"txn_id":13,"total":20},{"txn_id":16,"total":24.5},{"txn_id":19,"total":29},{"txn_id":22,"total":33.5}]
|
|
190
|
+
|
|
191
|
+
-- Non-aggregated projection through the exchanged sides: values follow the
|
|
192
|
+
-- attribute the projection names, not the slot it used to occupy
|
|
193
|
+
SELECT t.id AS tid, t.date, e.id AS eid, e.amount
|
|
194
|
+
FROM inl_entry e JOIN inl_txn t ON t.id = e.txn_id
|
|
195
|
+
WHERE t.entity_id = 1 ORDER BY eid;
|
|
196
|
+
→ [{"tid":1,"date":"2024-01-01","eid":1,"amount":1.5},{"tid":4,"date":"2024-01-04","eid":4,"amount":6},{"tid":7,"date":"2024-01-07","eid":7,"amount":10.5},{"tid":10,"date":"2024-01-10","eid":10,"amount":15},{"tid":13,"date":"2024-01-13","eid":13,"amount":19.5},{"tid":16,"date":"2024-01-16","eid":16,"amount":24},{"tid":19,"date":"2024-01-19","eid":19,"amount":28.5},{"tid":22,"date":"2024-01-22","eid":22,"amount":33},{"tid":1,"date":"2024-01-01","eid":25,"amount":0.5},{"tid":4,"date":"2024-01-04","eid":26,"amount":0.5},{"tid":7,"date":"2024-01-07","eid":27,"amount":0.5},{"tid":10,"date":"2024-01-10","eid":28,"amount":0.5},{"tid":13,"date":"2024-01-13","eid":29,"amount":0.5},{"tid":16,"date":"2024-01-16","eid":30,"amount":0.5},{"tid":19,"date":"2024-01-19","eid":31,"amount":0.5},{"tid":22,"date":"2024-01-22","eid":32,"amount":0.5}]
|
|
197
|
+
|
|
198
|
+
-- `SELECT *` is the sharpest test of the swap: with no projection naming columns,
|
|
199
|
+
-- the result's COLUMN ORDER is whatever the star expanded to at build time. It must
|
|
200
|
+
-- stay the spelled order — entry's three columns then txn's — even though the first
|
|
201
|
+
-- query below plans with the sides exchanged and the second does not.
|
|
202
|
+
SELECT * FROM inl_entry e JOIN inl_txn t ON t.id = e.txn_id WHERE t.entity_id = 1 ORDER BY e.id;
|
|
203
|
+
→ [{"id":1,"txn_id":1,"amount":1.5,"id:1":1,"entity_id":1,"date":"2024-01-01"},{"id":4,"txn_id":4,"amount":6,"id:1":4,"entity_id":1,"date":"2024-01-04"},{"id":7,"txn_id":7,"amount":10.5,"id:1":7,"entity_id":1,"date":"2024-01-07"},{"id":10,"txn_id":10,"amount":15,"id:1":10,"entity_id":1,"date":"2024-01-10"},{"id":13,"txn_id":13,"amount":19.5,"id:1":13,"entity_id":1,"date":"2024-01-13"},{"id":16,"txn_id":16,"amount":24,"id:1":16,"entity_id":1,"date":"2024-01-16"},{"id":19,"txn_id":19,"amount":28.5,"id:1":19,"entity_id":1,"date":"2024-01-19"},{"id":22,"txn_id":22,"amount":33,"id:1":22,"entity_id":1,"date":"2024-01-22"},{"id":25,"txn_id":1,"amount":0.5,"id:1":1,"entity_id":1,"date":"2024-01-01"},{"id":26,"txn_id":4,"amount":0.5,"id:1":4,"entity_id":1,"date":"2024-01-04"},{"id":27,"txn_id":7,"amount":0.5,"id:1":7,"entity_id":1,"date":"2024-01-07"},{"id":28,"txn_id":10,"amount":0.5,"id:1":10,"entity_id":1,"date":"2024-01-10"},{"id":29,"txn_id":13,"amount":0.5,"id:1":13,"entity_id":1,"date":"2024-01-13"},{"id":30,"txn_id":16,"amount":0.5,"id:1":16,"entity_id":1,"date":"2024-01-16"},{"id":31,"txn_id":19,"amount":0.5,"id:1":19,"entity_id":1,"date":"2024-01-19"},{"id":32,"txn_id":22,"amount":0.5,"id:1":22,"entity_id":1,"date":"2024-01-22"}]
|
|
204
|
+
|
|
205
|
+
SELECT * FROM inl_txn t JOIN inl_entry e ON t.id = e.txn_id WHERE t.entity_id = 1 ORDER BY e.id;
|
|
206
|
+
→ [{"id":1,"entity_id":1,"date":"2024-01-01","id:1":1,"txn_id":1,"amount":1.5},{"id":4,"entity_id":1,"date":"2024-01-04","id:1":4,"txn_id":4,"amount":6},{"id":7,"entity_id":1,"date":"2024-01-07","id:1":7,"txn_id":7,"amount":10.5},{"id":10,"entity_id":1,"date":"2024-01-10","id:1":10,"txn_id":10,"amount":15},{"id":13,"entity_id":1,"date":"2024-01-13","id:1":13,"txn_id":13,"amount":19.5},{"id":16,"entity_id":1,"date":"2024-01-16","id:1":16,"txn_id":16,"amount":24},{"id":19,"entity_id":1,"date":"2024-01-19","id:1":19,"txn_id":19,"amount":28.5},{"id":22,"entity_id":1,"date":"2024-01-22","id:1":22,"txn_id":22,"amount":33},{"id":1,"entity_id":1,"date":"2024-01-01","id:1":25,"txn_id":1,"amount":0.5},{"id":4,"entity_id":1,"date":"2024-01-04","id:1":26,"txn_id":4,"amount":0.5},{"id":7,"entity_id":1,"date":"2024-01-07","id:1":27,"txn_id":7,"amount":0.5},{"id":10,"entity_id":1,"date":"2024-01-10","id:1":28,"txn_id":10,"amount":0.5},{"id":13,"entity_id":1,"date":"2024-01-13","id:1":29,"txn_id":13,"amount":0.5},{"id":16,"entity_id":1,"date":"2024-01-16","id:1":30,"txn_id":16,"amount":0.5},{"id":19,"entity_id":1,"date":"2024-01-19","id:1":31,"txn_id":19,"amount":0.5},{"id":22,"entity_id":1,"date":"2024-01-22","id:1":32,"txn_id":22,"amount":0.5}]
|
|
207
|
+
|
|
208
|
+
-- A third relation above the exchanged pair: the upper join consumes the mirrored
|
|
209
|
+
-- join's attributes in their NEW order, so a stale positional mapping there would
|
|
210
|
+
-- surface as swapped values rather than as an error
|
|
211
|
+
CREATE TABLE inl_ent (id INTEGER PRIMARY KEY, name TEXT);
|
|
212
|
+
INSERT INTO inl_ent VALUES (1, 'alpha'), (2, 'beta'), (3, 'gamma');
|
|
213
|
+
ANALYZE;
|
|
214
|
+
SELECT n.name, t.id AS tid, e.id AS eid
|
|
215
|
+
FROM inl_entry e JOIN inl_txn t ON t.id = e.txn_id JOIN inl_ent n ON n.id = t.entity_id
|
|
216
|
+
WHERE t.entity_id = 1 ORDER BY eid;
|
|
217
|
+
→ [{"name":"alpha","tid":1,"eid":1},{"name":"alpha","tid":4,"eid":4},{"name":"alpha","tid":7,"eid":7},{"name":"alpha","tid":10,"eid":10},{"name":"alpha","tid":13,"eid":13},{"name":"alpha","tid":16,"eid":16},{"name":"alpha","tid":19,"eid":19},{"name":"alpha","tid":22,"eid":22},{"name":"alpha","tid":1,"eid":25},{"name":"alpha","tid":4,"eid":26},{"name":"alpha","tid":7,"eid":27},{"name":"alpha","tid":10,"eid":28},{"name":"alpha","tid":13,"eid":29},{"name":"alpha","tid":16,"eid":30},{"name":"alpha","tid":19,"eid":31},{"name":"alpha","tid":22,"eid":32}]
|
|
218
|
+
|
|
219
|
+
-- Self-join that mirrors: `b` (filtered to 8 rows) drives and `a` is seeked by
|
|
220
|
+
-- primary key, the reverse of how it is spelled. The two sides are distinct scan
|
|
221
|
+
-- instances of one table, so the swap must not make them share a cursor.
|
|
222
|
+
SELECT a.id AS aid, b.id AS bid FROM inl_big a JOIN inl_big b ON a.id = b.v WHERE b.w = 3 ORDER BY aid;
|
|
223
|
+
→ [{"aid":3,"bid":3},{"aid":8,"bid":8},{"aid":13,"bid":13},{"aid":18,"bid":18},{"aid":23,"bid":23},{"aid":28,"bid":28},{"aid":33,"bid":33},{"aid":38,"bid":38}]
|
|
224
|
+
|
|
225
|
+
-- Both sides indexed on the join key, large table named first: the swap drives from
|
|
226
|
+
-- the small side and seeks the large one; rows are what an inner join returns
|
|
227
|
+
CREATE TABLE inl_pa (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
228
|
+
CREATE INDEX inl_idx_pa_k ON inl_pa(k);
|
|
229
|
+
INSERT INTO inl_pa VALUES (1, 3), (2, 7), (3, 40), (4, NULL);
|
|
230
|
+
ANALYZE;
|
|
231
|
+
SELECT b.id AS bid, a.id AS aid FROM inl_big b JOIN inl_pa a ON b.v = a.k ORDER BY aid;
|
|
232
|
+
→ [{"bid":3,"aid":1},{"bid":7,"aid":2},{"bid":40,"aid":3}]
|
|
233
|
+
|
|
234
|
+
-- ===== Join-key seek combined with a predicate the module already claimed =====
|
|
235
|
+
-- `status` is indexed, so `b.status = 'x'` is pushed into the inner leaf as a seek
|
|
236
|
+
-- the module enforces alone — it is nowhere else in the tree. The index-nested-loop
|
|
237
|
+
-- rule re-offers that predicate together with the join key and re-applies whatever
|
|
238
|
+
-- the module declines as a Filter directly above the new seek (or the module takes
|
|
239
|
+
-- both on a composite index). Every outer key below matches an inner row by id, so
|
|
240
|
+
-- the status predicate is what decides each row: k=5 hits id 5 (status 'y') and
|
|
241
|
+
-- must be dropped — a redundant filter would prove nothing.
|
|
242
|
+
CREATE TABLE inl_ps (id INTEGER PRIMARY KEY, k INTEGER NULL);
|
|
243
|
+
INSERT INTO inl_ps VALUES (1, 5), (2, 8), (3, 12), (4, NULL), (5, 500);
|
|
244
|
+
CREATE TABLE inl_pb (id INTEGER PRIMARY KEY, v INTEGER, status TEXT);
|
|
245
|
+
CREATE INDEX inl_idx_pb_v ON inl_pb(v);
|
|
246
|
+
CREATE INDEX inl_idx_pb_status ON inl_pb(status);
|
|
247
|
+
INSERT INTO inl_pb VALUES
|
|
248
|
+
(1, 1, 'y'), (2, 2, 'y'), (3, 3, 'y'), (4, 4, 'x'), (5, 5, 'y'), (6, 6, 'y'), (7, 7, 'y'), (8, 8, 'x'),
|
|
249
|
+
(9, 9, 'y'), (10, 10, 'y'), (11, 11, 'y'), (12, 12, 'x'), (13, 13, 'y'), (14, 14, 'y'), (15, 15, 'y'),
|
|
250
|
+
(16, 16, 'x'), (17, 17, 'y'), (18, 18, 'y'), (19, 19, 'y'), (20, 20, 'x'), (21, 21, 'y'), (22, 22, 'y'),
|
|
251
|
+
(23, 23, 'y'), (24, 24, 'x'), (25, 25, 'y'), (26, 26, 'y'), (27, 27, 'y'), (28, 28, 'x'), (29, 29, 'y'),
|
|
252
|
+
(30, 30, 'y'), (31, 31, 'y'), (32, 32, 'x'), (33, 33, 'y'), (34, 34, 'y'), (35, 35, 'y'), (36, 36, 'x'),
|
|
253
|
+
(37, 37, 'y'), (38, 38, 'y'), (39, 39, 'y'), (40, 40, 'x');
|
|
254
|
+
ANALYZE;
|
|
255
|
+
|
|
256
|
+
-- INNER on the primary key: id 5 is status 'y' → dropped by the re-applied predicate
|
|
257
|
+
SELECT s.id, b.id AS bid FROM inl_ps s JOIN inl_pb b ON b.id = s.k WHERE b.status = 'x' ORDER BY s.id;
|
|
258
|
+
→ [{"id":2,"bid":8},{"id":3,"bid":12}]
|
|
259
|
+
|
|
260
|
+
-- INNER on the secondary index: same answer through idx_v
|
|
261
|
+
SELECT s.id, b.id AS bid FROM inl_ps s JOIN inl_pb b ON b.v = s.k WHERE b.status = 'x' ORDER BY s.id;
|
|
262
|
+
→ [{"id":2,"bid":8},{"id":3,"bid":12}]
|
|
263
|
+
|
|
264
|
+
-- LEFT with the predicate in the ON clause: the inner Filter sits inside the inner
|
|
265
|
+
-- pipeline, so a row it rejects makes the seek look empty → null-pad (k=5), as do
|
|
266
|
+
-- the NULL key and the unmatched key
|
|
267
|
+
SELECT s.id, b.id AS bid FROM inl_ps s LEFT JOIN inl_pb b ON b.id = s.k AND b.status = 'x' ORDER BY s.id;
|
|
268
|
+
→ [{"id":1,"bid":null},{"id":2,"bid":8},{"id":3,"bid":12},{"id":4,"bid":null},{"id":5,"bid":null}]
|
|
269
|
+
|
|
270
|
+
-- ANTI (NOT EXISTS): a seek hit rejected by the Filter counts as "no inner row" → kept
|
|
271
|
+
SELECT s.id FROM inl_ps s WHERE NOT EXISTS (SELECT 1 FROM inl_pb b WHERE b.id = s.k AND b.status = 'x') ORDER BY s.id;
|
|
272
|
+
→ [{"id":1},{"id":4},{"id":5}]
|
|
273
|
+
|
|
274
|
+
-- SEMI (EXISTS): the mirror of the anti answer
|
|
275
|
+
SELECT s.id FROM inl_ps s WHERE EXISTS (SELECT 1 FROM inl_pb b WHERE b.id = s.k AND b.status = 'x') ORDER BY s.id;
|
|
276
|
+
→ [{"id":2},{"id":3}]
|
|
277
|
+
|
|
278
|
+
-- A pushed range on the join-key column itself: `b.id = s.k` wins the column as the
|
|
279
|
+
-- seek and `b.id > 10` comes back as the residual (id 5 and 8 are out)
|
|
280
|
+
SELECT s.id, b.id AS bid FROM inl_ps s JOIN inl_pb b ON b.id = s.k WHERE b.id > 10 ORDER BY s.id;
|
|
281
|
+
→ [{"id":3,"bid":12}]
|
|
282
|
+
|
|
283
|
+
-- A pushed BETWEEN re-applied as its single node: id 12 is outside the range
|
|
284
|
+
SELECT s.id, b.id AS bid FROM inl_ps s JOIN inl_pb b ON b.v = s.k WHERE b.id BETWEEN 2 AND 9 ORDER BY s.id;
|
|
285
|
+
→ [{"id":1,"bid":5},{"id":2,"bid":8}]
|
|
286
|
+
|
|
287
|
+
-- Composite index covering both: the module takes the status equality AND the join
|
|
288
|
+
-- key as one seek, so no residual is needed — rows identical to the single-index form
|
|
289
|
+
CREATE TABLE inl_cb (id INTEGER PRIMARY KEY, v INTEGER, status TEXT);
|
|
290
|
+
CREATE INDEX inl_idx_cb ON inl_cb(status, v);
|
|
291
|
+
CREATE INDEX inl_idx_cb1 ON inl_cb(status);
|
|
292
|
+
INSERT INTO inl_cb SELECT id, v, status FROM inl_pb;
|
|
293
|
+
ANALYZE;
|
|
294
|
+
SELECT s.id, c.id AS cid FROM inl_ps s JOIN inl_cb c ON c.v = s.k WHERE c.status = 'x' ORDER BY s.id;
|
|
295
|
+
→ [{"id":2,"cid":8},{"id":3,"cid":12}]
|
|
296
|
+
|
|
297
|
+
-- The module keeps its own seek (a unique primary-key equality) and refuses the join
|
|
298
|
+
-- key: the plan is left alone and the rows are still right
|
|
299
|
+
SELECT s.id, b.id AS bid FROM inl_ps s JOIN inl_pb b ON b.v = s.k WHERE b.id = 8 ORDER BY s.id;
|
|
300
|
+
→ [{"id":2,"bid":8}]
|
|
301
|
+
|
|
302
|
+
-- The predicate's column is not selected by the derived table: the re-applied Filter
|
|
303
|
+
-- goes below the peeled Project, where `status` is still in scope
|
|
304
|
+
SELECT s.id FROM inl_ps s JOIN (SELECT id, v FROM inl_pb WHERE status = 'x') t ON t.v = s.k ORDER BY s.id;
|
|
305
|
+
→ [{"id":2},{"id":3}]
|