@quereus/quereus 4.20.0 → 4.20.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/src/common/types.d.ts +4 -4
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/schema/catalog-rendering.js +13 -1
- package/dist/src/schema/catalog-rendering.js.map +1 -1
- package/dist/src/schema/catalog.d.ts +35 -2
- package/dist/src/schema/catalog.d.ts.map +1 -1
- package/dist/src/schema/catalog.js +42 -33
- package/dist/src/schema/catalog.js.map +1 -1
- package/dist/src/schema/schema-differ.d.ts +1 -1
- package/dist/src/schema/schema-differ.d.ts.map +1 -1
- package/dist/src/schema/schema-differ.js +195 -33
- package/dist/src/schema/schema-differ.js.map +1 -1
- package/dist/src/schema/unique-enforcement.d.ts +23 -1
- package/dist/src/schema/unique-enforcement.d.ts.map +1 -1
- package/dist/src/schema/unique-enforcement.js +43 -1
- package/dist/src/schema/unique-enforcement.js.map +1 -1
- package/dist/src/vtab/memory/layer/manager.d.ts +15 -12
- package/dist/src/vtab/memory/layer/manager.d.ts.map +1 -1
- package/dist/src/vtab/memory/layer/manager.js +87 -100
- package/dist/src/vtab/memory/layer/manager.js.map +1 -1
- package/package.json +7 -4
- package/test/README.md +173 -0
- package/test/logic/01-basic.sqllogic +39 -0
- package/test/logic/01.1-select-projection-extras.sqllogic +130 -0
- package/test/logic/01.2-strings.sqllogic +43 -0
- package/test/logic/01.5-insert-select.sqllogic +237 -0
- package/test/logic/01.6-update-extras.sqllogic +118 -0
- package/test/logic/01.7-update-from.sqllogic +67 -0
- package/test/logic/01.8-delete-extras.sqllogic +122 -0
- package/test/logic/01.8.1-delete-range-cow.sqllogic +115 -0
- package/test/logic/01.9-query-expr-dml-gates.sqllogic +32 -0
- package/test/logic/01.9-query-expr-dml.sqllogic +171 -0
- package/test/logic/01.9-query-expr-values.sqllogic +111 -0
- package/test/logic/01.9.1-limit-over-dml-subquery.sqllogic +119 -0
- package/test/logic/02-filters.sqllogic +54 -0
- package/test/logic/02-smoke.sqllogic +62 -0
- package/test/logic/02.1-bind-parameters.sqllogic +223 -0
- package/test/logic/03-expressions.sqllogic +190 -0
- package/test/logic/03.1-quoted-identifiers.sqllogic +55 -0
- package/test/logic/03.4-defaults.sqllogic +554 -0
- package/test/logic/03.4.1-default-edge-cases.sqllogic +99 -0
- package/test/logic/03.5-tvf.sqllogic +61 -0
- package/test/logic/03.6-type-system.sqllogic +544 -0
- package/test/logic/03.6.1-numeric-text-comparison-coercion.sqllogic +244 -0
- package/test/logic/03.6.2-value-to-text.sqllogic +224 -0
- package/test/logic/03.7-bigint-mixed-arithmetic.sqllogic +65 -0
- package/test/logic/03.8-not-precedence.sqllogic +255 -0
- package/test/logic/03.9-is-bool-predicate.sqllogic +150 -0
- package/test/logic/03.9.1-is-bool-predicate-partial-index.sqllogic +34 -0
- package/test/logic/04-order-by.sqllogic +38 -0
- package/test/logic/04-transactions.sqllogic +239 -0
- package/test/logic/04a-savepoint-lazy-attach.sqllogic +122 -0
- package/test/logic/05-vtab_memory.sqllogic +50 -0
- package/test/logic/05.0.1-vtab-memory-unique-index-collation.sqllogic +34 -0
- package/test/logic/05.1-composite-pk-range-scan.sqllogic +147 -0
- package/test/logic/05.1.1-secondary-index-range-scan.sqllogic +56 -0
- package/test/logic/05.2-cast-seek-correctness.sqllogic +124 -0
- package/test/logic/06-builtin_functions.sqllogic +149 -0
- package/test/logic/06.1-string-functions.sqllogic +92 -0
- package/test/logic/06.1.3-like-glob-edges.sqllogic +53 -0
- package/test/logic/06.2-math-functions.sqllogic +82 -0
- package/test/logic/06.3-schema.sqllogic +48 -0
- package/test/logic/06.3.1-schema-all-schemas.sqllogic +39 -0
- package/test/logic/06.3.2-schema-foreign-keys.sqllogic +208 -0
- package/test/logic/06.3.3-introspection-tags.sqllogic +202 -0
- package/test/logic/06.3.3.1-introspection-tags-index.sqllogic +74 -0
- package/test/logic/06.3.4-view-info.sqllogic +378 -0
- package/test/logic/06.3.5-column-info.sqllogic +347 -0
- package/test/logic/06.3.6-schema-views.sqllogic +29 -0
- package/test/logic/06.4-schema-search-path.sqllogic +354 -0
- package/test/logic/06.4.1-schema-case-insensitive.sqllogic +83 -0
- package/test/logic/06.4.2-collation-extras.sqllogic +472 -0
- package/test/logic/06.4.2.1-collation-index-access-paths.sqllogic +140 -0
- package/test/logic/06.4.3-write-path-collation.sqllogic +99 -0
- package/test/logic/06.4.4-comparison-collation-precedence.sqllogic +342 -0
- package/test/logic/06.4.5-any-collate-declared-keys.sqllogic +92 -0
- package/test/logic/06.5-polymorphic-types.sqllogic +169 -0
- package/test/logic/06.5.1-conversion-functions.sqllogic +173 -0
- package/test/logic/06.5.2-scalar-minmax.sqllogic +16 -0
- package/test/logic/06.5.3-undeclared-return-type-comparison.sqllogic +244 -0
- package/test/logic/06.5.4-declared-return-type-builtins.sqllogic +288 -0
- package/test/logic/06.6-aggregate-extended.sqllogic +78 -0
- package/test/logic/06.7-json-extended.sqllogic +53 -0
- package/test/logic/06.8-json-path-operators.sqllogic +99 -0
- package/test/logic/06.9-json-canonical-key.sqllogic +65 -0
- package/test/logic/06.9.1-json-coerce-once.sqllogic +417 -0
- package/test/logic/06.9.1.1-json-coerce-once-index.sqllogic +39 -0
- package/test/logic/06.9.2-json-structural-equality.sqllogic +382 -0
- package/test/logic/06.9.2.1-json-structural-equality-index.sqllogic +76 -0
- package/test/logic/06.9.3-json-index-range-seek.sqllogic +205 -0
- package/test/logic/07-aggregates.sqllogic +193 -0
- package/test/logic/07.1-aggregate-filter-clause.sqllogic +12 -0
- package/test/logic/07.2-aggregate-order-by.sqllogic +11 -0
- package/test/logic/07.3-group-by-extras.sqllogic +602 -0
- package/test/logic/07.3.1-group-by-order-by-key.sqllogic +164 -0
- package/test/logic/07.3.2-grouped-select-list-shape.sqllogic +231 -0
- package/test/logic/07.4-group-by-filter-composite-pk.sqllogic +120 -0
- package/test/logic/07.5-sum-mixed-exact-and-float.sqllogic +151 -0
- package/test/logic/07.5-window.sqllogic +1941 -0
- package/test/logic/07.5.1-window-named.sqllogic +13 -0
- package/test/logic/07.5.2-window-nth-value.sqllogic +13 -0
- package/test/logic/07.6-subqueries.sqllogic +78 -0
- package/test/logic/07.6.1-subquery-extras.sqllogic +93 -0
- package/test/logic/07.7-and-or-short-circuit.sqllogic +58 -0
- package/test/logic/07.7-in-subquery-caching.sqllogic +174 -0
- package/test/logic/07.7-scalar-agg-decorrelation.sqllogic +197 -0
- package/test/logic/07.7.1-in-extras.sqllogic +158 -0
- package/test/logic/07.7.1-scalar-agg-decorrelation-filter.sqllogic +105 -0
- package/test/logic/07.7.2-scalar-agg-decorrelation-nested.sqllogic +115 -0
- package/test/logic/07.7.3-exists-in-select-decorrelation.sqllogic +107 -0
- package/test/logic/07.7.4-where-conjunct-ordering.sqllogic +121 -0
- package/test/logic/07.7.5-filter-lost-under-index-order.sqllogic +134 -0
- package/test/logic/07.7.6-correlated-predicate-scope.sqllogic +178 -0
- package/test/logic/07.7.7-join-source-scope-shadowing.sqllogic +137 -0
- package/test/logic/07.7.8-correlated-ref-to-computed-column.sqllogic +165 -0
- package/test/logic/07.7.9-conjunct-monotonicity.sqllogic +200 -0
- package/test/logic/07.8-correlated-subquery-edges.sqllogic +71 -0
- package/test/logic/07.8.1-correlated-in-decorrelation-gates.sqllogic +156 -0
- package/test/logic/07.9-in-value-list.sqllogic +154 -0
- package/test/logic/07.9.1-in-value-list-composite-index.sqllogic +38 -0
- package/test/logic/08-views.sqllogic +64 -0
- package/test/logic/08.1-semi-anti-join.sqllogic +191 -0
- package/test/logic/08.1-view-edge-cases.sqllogic +118 -0
- package/test/logic/08.1.1-uncorrelated-in-semijoin.sqllogic +251 -0
- package/test/logic/08.2-existence-flag-semijoin-recovery.sqllogic +197 -0
- package/test/logic/08.3-existence-flag-inner-recovery.sqllogic +102 -0
- package/test/logic/08.4-key-set-semi-join.sqllogic +447 -0
- package/test/logic/09-set_operations.sqllogic +244 -0
- package/test/logic/09.1-set-op-cross-collation.sqllogic +230 -0
- package/test/logic/10-distinct_datatypes.sqllogic +127 -0
- package/test/logic/10.1-ddl-lifecycle.sqllogic +203 -0
- package/test/logic/10.1.1-create-table-syntax-edges.sqllogic +47 -0
- package/test/logic/10.1.2-ddl-in-transaction.sqllogic +143 -0
- package/test/logic/10.1.2.1-add-constraint-in-transaction.sqllogic +59 -0
- package/test/logic/10.1.3-ddl-drop-in-transaction.sqllogic +187 -0
- package/test/logic/10.1.3.1-drop-constraint-in-transaction.sqllogic +45 -0
- package/test/logic/10.1.4-create-table-duplicate-constraint-names.sqllogic +149 -0
- package/test/logic/10.1.4-ddl-transaction-policy.sqllogic +314 -0
- package/test/logic/10.1.5-alter-add-constraint-minted-name-disambiguation.sqllogic +219 -0
- package/test/logic/10.1.5-create-index-lifecycle.sqllogic +72 -0
- package/test/logic/10.1.6-ddl-transaction-policy-index.sqllogic +143 -0
- package/test/logic/10.1.7-create-table-default-type-check.sqllogic +119 -0
- package/test/logic/10.1.8-alter-add-check-existing-rows.sqllogic +117 -0
- package/test/logic/10.2-column-features.sqllogic +365 -0
- package/test/logic/10.2.1-table-options-rejection.sqllogic +63 -0
- package/test/logic/10.2.2-default-collation-memory.sqllogic +30 -0
- package/test/logic/10.3-function-features.sqllogic +247 -0
- package/test/logic/10.4-schema-scale.sqllogic +168 -0
- package/test/logic/10.4.1-schema-scale-indexes.sqllogic +121 -0
- package/test/logic/10.5-indexes.sqllogic +164 -0
- package/test/logic/10.5.1-partial-indexes.sqllogic +526 -0
- package/test/logic/10.5.2-expression-indexes.sqllogic +34 -0
- package/test/logic/10.5.3-desc-index-ordering.sqllogic +130 -0
- package/test/logic/10.5.4-composite-pk-index-update-phantom.sqllogic +55 -0
- package/test/logic/10.5.5-index-name-uniqueness.sqllogic +76 -0
- package/test/logic/10.5.6-minmax-index-boundary.sqllogic +204 -0
- package/test/logic/10.5.7-implicit-unique-index-lifecycle.sqllogic +1216 -0
- package/test/logic/10.6-distinct-edge-cases.sqllogic +119 -0
- package/test/logic/100-predicate-normalization-edge-cases.sqllogic +238 -0
- package/test/logic/100.1-where-extras.sqllogic +163 -0
- package/test/logic/100.1.1-where-join-on-order-indexed.sqllogic +35 -0
- package/test/logic/101-builtin-mutation-kills.sqllogic +974 -0
- package/test/logic/101-transaction-edge-cases.sqllogic +395 -0
- package/test/logic/102-schema-catalog-edge-cases.sqllogic +252 -0
- package/test/logic/102-unique-constraints.sqllogic +145 -0
- package/test/logic/102.0.1-schema-catalog-indexed-table.sqllogic +32 -0
- package/test/logic/102.1-unique-edge-cases.sqllogic +111 -0
- package/test/logic/102.1.1-unique-index-creation-edge-cases.sqllogic +75 -0
- package/test/logic/102.2-unique-collation.sqllogic +181 -0
- package/test/logic/102.2.1-unique-collation-index-derived.sqllogic +305 -0
- package/test/logic/103-database-options-edge-cases.sqllogic +108 -0
- package/test/logic/103.1-pragma-boolean-values.sqllogic +126 -0
- package/test/logic/104-emit-mutation-kills.sqllogic +236 -0
- package/test/logic/105-vtab-memory-mutation-kills.sqllogic +754 -0
- package/test/logic/105.1-vtab-memory-index-mutation-kills.sqllogic +540 -0
- package/test/logic/106-constraint-extractor-mutation-kills.sqllogic +350 -0
- package/test/logic/107-temporal-arithmetic-mutation-kills.sqllogic +833 -0
- package/test/logic/108-cardinality-estimation.sqllogic +146 -0
- package/test/logic/108.1-analyze-name-resolution.sqllogic +102 -0
- package/test/logic/109-aggregate-physical-selection.sqllogic +103 -0
- package/test/logic/11-joins.sqllogic +86 -0
- package/test/logic/11.1-join-using.sqllogic +267 -0
- package/test/logic/11.2-comma-join.sqllogic +35 -0
- package/test/logic/11.3-index-nested-loop-join.sqllogic +305 -0
- package/test/logic/11.3-mismatched-collation-join.sqllogic +74 -0
- package/test/logic/11.4-hash-join-side-swap.sqllogic +118 -0
- package/test/logic/110-scan-emitter-mutation-kills.sqllogic +121 -0
- package/test/logic/110.1-scan-emitter-index-scan-kills.sqllogic +47 -0
- package/test/logic/12-empty-primary-key.sqllogic +124 -0
- package/test/logic/12-join_padding_order.sqllogic +58 -0
- package/test/logic/12.1-orderby-index-consumption.sqllogic +40 -0
- package/test/logic/13-cte.sqllogic +137 -0
- package/test/logic/13.1-cte-multiple-recursive.sqllogic +104 -0
- package/test/logic/13.10-cte-qualified-name.sqllogic +136 -0
- package/test/logic/13.11-unreferenced-dml-cte.sqllogic +414 -0
- package/test/logic/13.12-nested-dml-cte-rejected.sqllogic +280 -0
- package/test/logic/13.2-cte-bind-params.sqllogic +98 -0
- package/test/logic/13.3-cte-edge-cases.sqllogic +120 -0
- package/test/logic/13.4-cte-extras.sqllogic +98 -0
- package/test/logic/13.5-cte-join-order.sqllogic +166 -0
- package/test/logic/13.6-cte-dml-runs-once.sqllogic +318 -0
- package/test/logic/13.7-cte-sibling-visibility.sqllogic +227 -0
- package/test/logic/13.8-insert-with-clause-visibility.sqllogic +254 -0
- package/test/logic/13.9-schema-authored-cte-isolation.sqllogic +328 -0
- package/test/logic/13.9.1-schema-authored-schema-path-isolation.sqllogic +408 -0
- package/test/logic/14-utilities.sqllogic +168 -0
- package/test/logic/15-timespan.sqllogic +279 -0
- package/test/logic/15.1-semantic-ordering.sqllogic +960 -0
- package/test/logic/15.1.1-json-check-coercion.sqllogic +178 -0
- package/test/logic/15.1.2-semantic-ordering-index.sqllogic +41 -0
- package/test/logic/15.1.3-declared-form-write-contract.sqllogic +519 -0
- package/test/logic/16-epoch.sqllogic +187 -0
- package/test/logic/17-weekday-modifier.sqllogic +78 -0
- package/test/logic/18-json-string-escapes.sqllogic +86 -0
- package/test/logic/18-strftime-extended-formats.sqllogic +68 -0
- package/test/logic/20-empty-single-row.sqllogic +261 -0
- package/test/logic/21-null-edge-cases.sqllogic +227 -0
- package/test/logic/21.1-where-null-comparisons.sqllogic +151 -0
- package/test/logic/21.1.1-where-null-comparisons-secondary-index.sqllogic +35 -0
- package/test/logic/21.2-between-edges.sqllogic +157 -0
- package/test/logic/22-boundary-values.sqllogic +175 -0
- package/test/logic/23-self-joins-duplicates.sqllogic +74 -0
- package/test/logic/24-builtin-branches.sqllogic +611 -0
- package/test/logic/24.1-substr-extras.sqllogic +75 -0
- package/test/logic/24.2-random-extras.sqllogic +39 -0
- package/test/logic/25-aggregate-edge-cases.sqllogic +289 -0
- package/test/logic/25.1-nested-aggregates.sqllogic +74 -0
- package/test/logic/25.2-having-edge-cases.sqllogic +318 -0
- package/test/logic/25.3-aggregate-isnull-empty.sqllogic +16 -0
- package/test/logic/25.4-groupby-fd-constant-empty.sqllogic +55 -0
- package/test/logic/25.5-ungrouped-aggregate-column-free-select-item.sqllogic +196 -0
- package/test/logic/26-join-edge-cases.sqllogic +333 -0
- package/test/logic/26.1-left-join-isnull-on-notnull.sqllogic +26 -0
- package/test/logic/26.2-left-join-on-vs-where.sqllogic +67 -0
- package/test/logic/26.3-join-on-constants.sqllogic +61 -0
- package/test/logic/26.4-join-on-is-null-nullable.sqllogic +44 -0
- package/test/logic/27-window-edge-cases.sqllogic +206 -0
- package/test/logic/27.1-window-groups-frame.sqllogic +13 -0
- package/test/logic/27.2-window-exclude.sqllogic +13 -0
- package/test/logic/27.3-window-json-aggregation.sqllogic +66 -0
- package/test/logic/28-set-ops-sort-edge-cases.sqllogic +444 -0
- package/test/logic/28.1-compound-limit-offset.sqllogic +143 -0
- package/test/logic/28.2-orderby-expression-extras.sqllogic +698 -0
- package/test/logic/28.2-set-op-branch-types.sqllogic +217 -0
- package/test/logic/29-constraint-edge-cases.sqllogic +442 -0
- package/test/logic/29.1-column-level-conflict-clause.sqllogic +171 -0
- package/test/logic/29.2-table-level-pk-conflict-clause.sqllogic +113 -0
- package/test/logic/40-constraints.sqllogic +418 -0
- package/test/logic/40.1-pk-desc-direction.sqllogic +76 -0
- package/test/logic/40.2-check-extras.sqllogic +683 -0
- package/test/logic/41-alter-table.sqllogic +191 -0
- package/test/logic/41-fk-cascade-conflict-and-self-ref.sqllogic +263 -0
- package/test/logic/41-fk-cross-schema.sqllogic +155 -0
- package/test/logic/41-fk-extended-targets.sqllogic +173 -0
- package/test/logic/41-foreign-keys.sqllogic +1001 -0
- package/test/logic/41-generated-column-errors.sqllogic +225 -0
- package/test/logic/41-generated-column-extras.sqllogic +598 -0
- package/test/logic/41-generated-column-scope.sqllogic +329 -0
- package/test/logic/41-generated-columns.sqllogic +361 -0
- package/test/logic/41.1-alter-pk.sqllogic +230 -0
- package/test/logic/41.1-fk-collation-conflict.sqllogic +277 -0
- package/test/logic/41.10-alter-drop-column-foreign-key.sqllogic +274 -0
- package/test/logic/41.10.1-alter-drop-column-unique-index.sqllogic +169 -0
- package/test/logic/41.10.2-alter-drop-column-check-and-assertion.sqllogic +1616 -0
- package/test/logic/41.10.3-alter-drop-column-referencing-fk.sqllogic +292 -0
- package/test/logic/41.10.4-drop-object-expression-dependents.sqllogic +602 -0
- package/test/logic/41.11-deferred-fk-with-rename.sqllogic +232 -0
- package/test/logic/41.12-alter-default-coercion.sqllogic +200 -0
- package/test/logic/41.13-alter-add-column-generated-backfill.sqllogic +566 -0
- package/test/logic/41.14-alter-add-column-subquery-backfill.sqllogic +129 -0
- package/test/logic/41.15-alter-column-default-type-check.sqllogic +178 -0
- package/test/logic/41.16-fk-unenforceable.sqllogic +364 -0
- package/test/logic/41.2-alter-column.sqllogic +382 -0
- package/test/logic/41.2.1-alter-column-retype-deleted-row-memory.sqllogic +32 -0
- package/test/logic/41.2.2-alter-column-retype-index-rebuild.sqllogic +77 -0
- package/test/logic/41.2.3-alter-column-set-not-null-pk-backfill.sqllogic +310 -0
- package/test/logic/41.2.3.1-alter-column-set-not-null-pk-backfill-secondary-index.sqllogic +37 -0
- package/test/logic/41.3-alter-add-column-unique.sqllogic +184 -0
- package/test/logic/41.3-alter-rename-propagation.sqllogic +2352 -0
- package/test/logic/41.3.1-alter-rename-index-propagation.sqllogic +94 -0
- package/test/logic/41.4-alter-add-column-constraints.sqllogic +855 -0
- package/test/logic/41.5-alter-misc.sqllogic +48 -0
- package/test/logic/41.5-cross-schema-foreign-keys.sqllogic +253 -0
- package/test/logic/41.6-alter-drop-rename-constraint.sqllogic +354 -0
- package/test/logic/41.6.1-alter-drop-constraint-index-derived.sqllogic +22 -0
- package/test/logic/41.7-alter-column-collate.sqllogic +105 -0
- package/test/logic/41.7.1-alter-column-collate-unique.sqllogic +45 -0
- package/test/logic/41.7.2-alter-column-collate-unique-store.sqllogic +208 -0
- package/test/logic/41.7.2.1-alter-column-collate-unique-index-store.sqllogic +114 -0
- package/test/logic/41.7.3-alter-column-retype-unique.sqllogic +293 -0
- package/test/logic/41.7.3.1-alter-column-retype-staged-rows-memory.sqllogic +41 -0
- package/test/logic/41.7.3.2-alter-column-retype-unique-index.sqllogic +72 -0
- package/test/logic/41.7.4-alter-column-retype-semantic-memory.sqllogic +445 -0
- package/test/logic/41.7.4.1-alter-column-retype-semantic-index-memory.sqllogic +91 -0
- package/test/logic/41.7.5-alter-column-collate-pk-staged-delete-memory.sqllogic +106 -0
- package/test/logic/41.8-alter-add-constraint-unique-fk.sqllogic +275 -0
- package/test/logic/41.8-alter-savepoint-staged-rows.sqllogic +443 -0
- package/test/logic/41.8.1-alter-rename-savepoint-staged-rows.sqllogic +408 -0
- package/test/logic/41.9-alter-constraint-body-change.sqllogic +180 -0
- package/test/logic/41.9-fk-restrict-batched.sqllogic +290 -0
- package/test/logic/42-committed-snapshot.sqllogic +254 -0
- package/test/logic/42-returning.sqllogic +159 -0
- package/test/logic/42.1-returning-extras.sqllogic +232 -0
- package/test/logic/42.2-returning-stored-row.sqllogic +165 -0
- package/test/logic/43-default-nullability.sqllogic +131 -0
- package/test/logic/43-transition-constraints.sqllogic +341 -0
- package/test/logic/43.1-default-collation.sqllogic +140 -0
- package/test/logic/43.1-notnull-or-conflict.sqllogic +79 -0
- package/test/logic/43.2-deferred-check-new-on-delete.sqllogic +95 -0
- package/test/logic/43.3-nullable-primary-key.sqllogic +587 -0
- package/test/logic/44-determinism-validation.sqllogic +164 -0
- package/test/logic/44-orthogonality.sqllogic +146 -0
- package/test/logic/44.1-nondeterministic-schema.sqllogic +316 -0
- package/test/logic/45-udf-determinism.sqllogic +39 -0
- package/test/logic/46-mutation-context.sqllogic +595 -0
- package/test/logic/47-upsert.sqllogic +255 -0
- package/test/logic/47.1-upsert-conflict-targets.sqllogic +169 -0
- package/test/logic/47.2-replace-and-or-clauses.sqllogic +164 -0
- package/test/logic/47.2.1-pk-replace-secondary-unique.sqllogic +374 -0
- package/test/logic/47.3-engine-or-fail.sqllogic +122 -0
- package/test/logic/47.3-upsert-conflict-target-collation.sqllogic +139 -0
- package/test/logic/47.3.1-upsert-conflict-index-derived-collation.sqllogic +25 -0
- package/test/logic/47.4-upsert-conflict-target-affinity.sqllogic +59 -0
- package/test/logic/47.5-upsert-do-update-constraints.sqllogic +440 -0
- package/test/logic/47.6-upsert-do-update-decomposed.sqllogic +100 -0
- package/test/logic/49-reference-graph.sqllogic +84 -0
- package/test/logic/50-declarative-schema.sqllogic +2550 -0
- package/test/logic/50-metadata-tags.sqllogic +1033 -0
- package/test/logic/50.0.1-metadata-tags-index.sqllogic +141 -0
- package/test/logic/50.1-declare-schema-pk.sqllogic +150 -0
- package/test/logic/50.2-declare-schema-renames.sqllogic +949 -0
- package/test/logic/50.2.1-declare-schema-index-renames.sqllogic +173 -0
- package/test/logic/50.3-declare-schema-duplicate-names.sqllogic +280 -0
- package/test/logic/50.4-declare-schema-apply-restore.sqllogic +192 -0
- package/test/logic/51-lens-foundation.sqllogic +147 -0
- package/test/logic/51-materialized-views.sqllogic +376 -0
- package/test/logic/51.5-materialized-views-coarsened-key.sqllogic +198 -0
- package/test/logic/51.7-maintained-table-attach-detach.sqllogic +468 -0
- package/test/logic/51.8-maintained-table-declared-constraints.sqllogic +440 -0
- package/test/logic/51.85-maintained-table-parent-fk.sqllogic +103 -0
- package/test/logic/51.9-maintained-table-secondary-unique.sqllogic +237 -0
- package/test/logic/51.9.1-maintained-table-partial-unique.sqllogic +36 -0
- package/test/logic/52-lens-overrides.sqllogic +566 -0
- package/test/logic/53-materialized-views-rowtime.sqllogic +1601 -0
- package/test/logic/53-reserved-tags.sqllogic +252 -0
- package/test/logic/53.1-materialized-view-write-through.sqllogic +329 -0
- package/test/logic/53.2-materialized-view-rename-propagation.sqllogic +483 -0
- package/test/logic/53.3-materialized-view-constraint-only-ddl.sqllogic +409 -0
- package/test/logic/53.3.1-materialized-view-index-ddl-irrelevance.sqllogic +29 -0
- package/test/logic/53.4-materialized-view-structural-alter-restore.sqllogic +436 -0
- package/test/logic/54-covering-mv-enforcement.sqllogic +314 -0
- package/test/logic/54.1-covering-mv-partial-enforcement.sqllogic +34 -0
- package/test/logic/55-internal-eviction-reporting.sqllogic +89 -0
- package/test/logic/55-lens-prover.sqllogic +202 -0
- package/test/logic/55.1-eviction-restrict-fk.sqllogic +148 -0
- package/test/logic/55.5-lens-authored-inverse.sqllogic +1468 -0
- package/test/logic/80-grow-retrieve-pass.sqllogic +79 -0
- package/test/logic/81-quickpick.sqllogic +24 -0
- package/test/logic/82-bloom-join.sqllogic +185 -0
- package/test/logic/83-merge-join.sqllogic +280 -0
- package/test/logic/84-asof-scan.sqllogic +238 -0
- package/test/logic/84-key-cardinality.sqllogic +50 -0
- package/test/logic/85-relational-const-folding.sqllogic +51 -0
- package/test/logic/86-scalar-cse.sqllogic +42 -0
- package/test/logic/90-error_paths.sqllogic +114 -0
- package/test/logic/90.1-parse-errors.sqllogic +77 -0
- package/test/logic/90.2-alter-table-errors.sqllogic +53 -0
- package/test/logic/90.2.1-alter-extra-errors.sqllogic +51 -0
- package/test/logic/90.3-expression-errors.sqllogic +43 -0
- package/test/logic/90.4-dml-errors.sqllogic +47 -0
- package/test/logic/90.5-unsupported-join-types.sqllogic +37 -0
- package/test/logic/90.5.1-right-full-join-read.sqllogic +157 -0
- package/test/logic/90.6-select-error-paths.sqllogic +57 -0
- package/test/logic/91-merge-join-edge-cases.sqllogic +159 -0
- package/test/logic/92-hash-aggregate-edge-cases.sqllogic +139 -0
- package/test/logic/93-ddl-view-edge-cases.sqllogic +104 -0
- package/test/logic/93.1-view-error-paths.sqllogic +74 -0
- package/test/logic/93.2-view-mutation-pending.sqllogic +108 -0
- package/test/logic/93.3-view-mutation-or-clause.sqllogic +60 -0
- package/test/logic/93.4-view-mutation.sqllogic +4881 -0
- package/test/logic/93.4.1-view-mutation-partial-unique-cardinality.sqllogic +31 -0
- package/test/logic/93.5-authored-inverse.sqllogic +415 -0
- package/test/logic/93.6-set-op-flagless-write.sqllogic +862 -0
- package/test/logic/94-tvf-edge-cases.sqllogic +58 -0
- package/test/logic/94.1-limit-offset-edge-cases.sqllogic +109 -0
- package/test/logic/94.2-limit-offset-extras.sqllogic +98 -0
- package/test/logic/95-assertions.sqllogic +1417 -0
- package/test/logic/96-subquery-edge-cases.sqllogic +276 -0
- package/test/logic/97-json-function-edge-cases.sqllogic +633 -0
- package/test/logic/97.1-json-blob-and-special-values.sqllogic +92 -0
- package/test/logic/98-temporal-edge-cases.sqllogic +515 -0
- package/test/logic/99-conversion-edge-cases.sqllogic +325 -0
- package/test/logic/99.0.1-schema-index-listing.sqllogic +21 -0
- package/test/logic/99.1-cast-syntax-extras.sqllogic +167 -0
- package/test/logic/drop-unique-index.sqllogic +88 -0
- package/test/logic/pushdown-test.sqllogic +36 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
-- ORDER BY a GROUP BY key in a grouped query whose SELECT list contains no
|
|
2
|
+
-- aggregate function at all.
|
|
3
|
+
--
|
|
4
|
+
-- Regression for bug-order-by-group-key-not-in-select-list: the planner used to
|
|
5
|
+
-- decide the final-projection path from "does the SELECT list contain an
|
|
6
|
+
-- aggregate function" rather than "did a grouping phase run". A `group by` with
|
|
7
|
+
-- no aggregate anywhere therefore took *both* paths and emitted a second,
|
|
8
|
+
-- stale projection whose column references still pointed at pre-aggregate
|
|
9
|
+
-- attributes. A bare-column `order by` over a grouping key inserted a blocking
|
|
10
|
+
-- Sort underneath that stale projection, the source row context was gone by the
|
|
11
|
+
-- time it ran, and the query failed with "No row context found for column v".
|
|
12
|
+
-- Adding any aggregate (`count(*)`) or sorting by the output alias hid the bug.
|
|
13
|
+
--
|
|
14
|
+
-- The rule pinned here: any expression legal in GROUP BY is legal in ORDER BY of
|
|
15
|
+
-- the same query and sorts by the grouped value, regardless of how — or
|
|
16
|
+
-- whether — the SELECT list projects it.
|
|
17
|
+
--
|
|
18
|
+
-- Plan shape (exactly one Project, Sort above it) is asserted separately in
|
|
19
|
+
-- test/plan/grouped-projection-shape.spec.ts; this file pins the results.
|
|
20
|
+
|
|
21
|
+
create table gk (v integer primary key, g text);
|
|
22
|
+
insert into gk values (1,'a'),(2,'b'),(3,'a');
|
|
23
|
+
|
|
24
|
+
-- The original repro: grouping key wrapped in an expression in the SELECT list,
|
|
25
|
+
-- ORDER BY names the bare key.
|
|
26
|
+
select cast(v as text) x from gk group by v order by v;
|
|
27
|
+
→ [{"x":"1"},{"x":"2"},{"x":"3"}]
|
|
28
|
+
|
|
29
|
+
-- DESC behaves the same.
|
|
30
|
+
select cast(v as text) x from gk group by v order by v desc;
|
|
31
|
+
→ [{"x":"3"},{"x":"2"},{"x":"1"}]
|
|
32
|
+
|
|
33
|
+
-- Arithmetic instead of CAST.
|
|
34
|
+
select v+0 x from gk group by v order by v;
|
|
35
|
+
→ [{"x":1},{"x":2},{"x":3}]
|
|
36
|
+
|
|
37
|
+
-- Function call over a text grouping key.
|
|
38
|
+
select upper(g) x from gk group by g order by g;
|
|
39
|
+
→ [{"x":"A"},{"x":"B"}]
|
|
40
|
+
|
|
41
|
+
select upper(g) x from gk group by g order by g desc;
|
|
42
|
+
→ [{"x":"B"},{"x":"A"}]
|
|
43
|
+
|
|
44
|
+
-- Bare grouping key in the SELECT list (no expression wrapper, still no aggregate):
|
|
45
|
+
-- output keeps the SELECT-list name, not the raw group column.
|
|
46
|
+
select g from gk group by g order by g;
|
|
47
|
+
→ [{"g":"a"},{"g":"b"}]
|
|
48
|
+
|
|
49
|
+
-- ORDER BY an *expression over* a grouping key.
|
|
50
|
+
select cast(v as text) x from gk group by v order by -v;
|
|
51
|
+
→ [{"x":"3"},{"x":"2"},{"x":"1"}]
|
|
52
|
+
|
|
53
|
+
-- HAVING over a grouping key combined with the bare-column ORDER BY.
|
|
54
|
+
select cast(v as text) x from gk group by v having v > 1 order by v;
|
|
55
|
+
→ [{"x":"2"},{"x":"3"}]
|
|
56
|
+
|
|
57
|
+
select upper(g) x from gk group by g having g > 'a' order by g;
|
|
58
|
+
→ [{"x":"B"}]
|
|
59
|
+
|
|
60
|
+
-- DISTINCT over the projected expression.
|
|
61
|
+
select distinct upper(g) x from gk group by g order by g;
|
|
62
|
+
→ [{"x":"A"},{"x":"B"}]
|
|
63
|
+
|
|
64
|
+
-- LIMIT / OFFSET above the sort.
|
|
65
|
+
select upper(g) x from gk group by g order by g limit 1;
|
|
66
|
+
→ [{"x":"A"}]
|
|
67
|
+
|
|
68
|
+
select cast(v as text) x from gk group by v order by v limit 1 offset 1;
|
|
69
|
+
→ [{"x":"2"}]
|
|
70
|
+
|
|
71
|
+
-- Multiple grouping keys, ORDER BY both as bare columns.
|
|
72
|
+
select cast(v as text) x from gk group by g, v order by g, v;
|
|
73
|
+
→ [{"x":"1"},{"x":"3"},{"x":"2"}]
|
|
74
|
+
|
|
75
|
+
-- Grouping key projected both bare and wrapped.
|
|
76
|
+
select g, upper(g) x from gk group by g order by g;
|
|
77
|
+
→ [{"g":"a","x":"A"},{"g":"b","x":"B"}]
|
|
78
|
+
|
|
79
|
+
-- Contrast: the same shape *with* an aggregate always worked; it must keep working.
|
|
80
|
+
select cast(v as text) x, count(*) c from gk group by v order by v;
|
|
81
|
+
→ [{"x":"1","c":1},{"x":"2","c":1},{"x":"3","c":1}]
|
|
82
|
+
|
|
83
|
+
-- Contrast: the non-grouped path still applies ORDER BY before the projection
|
|
84
|
+
-- (this is what `shouldApplyOrderByBeforeProjection` exists for) — 'a' appears
|
|
85
|
+
-- twice because there is no grouping here.
|
|
86
|
+
select upper(g) x from gk order by g;
|
|
87
|
+
→ [{"x":"A"},{"x":"A"},{"x":"B"}]
|
|
88
|
+
|
|
89
|
+
-- ---------------------------------------------------------------------------
|
|
90
|
+
-- SELECT * / bare column list over a grouped query.
|
|
91
|
+
-- The forced final projection must emit *source-column* order (v, g), not
|
|
92
|
+
-- GROUP BY order (g, v). Column order itself is asserted in the plan-shape spec
|
|
93
|
+
-- (row objects compare key-order-insensitively here); these cases pin that each
|
|
94
|
+
-- name still carries its own column's value.
|
|
95
|
+
-- ---------------------------------------------------------------------------
|
|
96
|
+
select v, g from gk group by g, v order by v;
|
|
97
|
+
→ [{"v":1,"g":"a"},{"v":2,"g":"b"},{"v":3,"g":"a"}]
|
|
98
|
+
|
|
99
|
+
select * from gk group by g, v order by v;
|
|
100
|
+
→ [{"v":1,"g":"a"},{"v":2,"g":"b"},{"v":3,"g":"a"}]
|
|
101
|
+
|
|
102
|
+
select gk.* from gk group by v, g order by v;
|
|
103
|
+
→ [{"v":1,"g":"a"},{"v":2,"g":"b"},{"v":3,"g":"a"}]
|
|
104
|
+
|
|
105
|
+
-- Two stars in one grouped SELECT list. Each expands once: replaying every
|
|
106
|
+
-- expanded star column for each star emitted the whole set N times over.
|
|
107
|
+
create table gj (k integer primary key, w text);
|
|
108
|
+
insert into gj values (1,'p'),(2,'q'),(3,'r');
|
|
109
|
+
|
|
110
|
+
select gk.*, gj.* from gk join gj on gk.v = gj.k group by gk.v, gk.g, gj.k, gj.w order by v;
|
|
111
|
+
→ [{"v":1,"g":"a","k":1,"w":"p"},{"v":2,"g":"b","k":2,"w":"q"},{"v":3,"g":"a","k":3,"w":"r"}]
|
|
112
|
+
|
|
113
|
+
drop table gj;
|
|
114
|
+
|
|
115
|
+
-- ---------------------------------------------------------------------------
|
|
116
|
+
-- The grouped, aggregate-free select must compose everywhere a relation can go.
|
|
117
|
+
-- ---------------------------------------------------------------------------
|
|
118
|
+
|
|
119
|
+
-- As a derived table, re-sorted by the outer query.
|
|
120
|
+
select x from (select upper(g) x from gk group by g order by g) order by x desc;
|
|
121
|
+
→ [{"x":"B"},{"x":"A"}]
|
|
122
|
+
|
|
123
|
+
-- As a CTE.
|
|
124
|
+
with c as (select upper(g) x from gk group by g order by g) select * from c;
|
|
125
|
+
→ [{"x":"A"},{"x":"B"}]
|
|
126
|
+
|
|
127
|
+
-- As an IN subquery.
|
|
128
|
+
select v from gk where v in (select v from gk group by v order by v) order by v;
|
|
129
|
+
→ [{"v":1},{"v":2},{"v":3}]
|
|
130
|
+
|
|
131
|
+
-- As a scalar subquery.
|
|
132
|
+
select (select upper(g) from gk where v = 1 group by g) s;
|
|
133
|
+
→ [{"s":"A"}]
|
|
134
|
+
|
|
135
|
+
-- As a compound-select branch.
|
|
136
|
+
select upper(g) x from gk group by g union all select 'z';
|
|
137
|
+
→ [{"x":"A"},{"x":"B"},{"x":"z"}]
|
|
138
|
+
|
|
139
|
+
-- As a materialized view body.
|
|
140
|
+
create materialized view mvg as select g from gk group by g;
|
|
141
|
+
select g from mvg order by g;
|
|
142
|
+
→ [{"g":"a"},{"g":"b"}]
|
|
143
|
+
|
|
144
|
+
drop materialized view mvg;
|
|
145
|
+
drop table gk;
|
|
146
|
+
|
|
147
|
+
-- ---------------------------------------------------------------------------
|
|
148
|
+
-- NULL grouping key. NULLs collapse into one group and the bare-column ORDER BY
|
|
149
|
+
-- places them per the explicit NULLS clause (the *default* placement under DESC
|
|
150
|
+
-- is deliberately not asserted — see 10.5.3-desc-index-ordering).
|
|
151
|
+
-- ---------------------------------------------------------------------------
|
|
152
|
+
create table gkn (v integer primary key, g text null);
|
|
153
|
+
insert into gkn values (1,'a'),(2,null),(3,'b'),(4,null);
|
|
154
|
+
|
|
155
|
+
select upper(g) x from gkn group by g order by g nulls first;
|
|
156
|
+
→ [{"x":null},{"x":"A"},{"x":"B"}]
|
|
157
|
+
|
|
158
|
+
select upper(g) x from gkn group by g order by g nulls last;
|
|
159
|
+
→ [{"x":"A"},{"x":"B"},{"x":null}]
|
|
160
|
+
|
|
161
|
+
select upper(g) x from gkn group by g order by g desc nulls last;
|
|
162
|
+
→ [{"x":"B"},{"x":"A"},{"x":null}]
|
|
163
|
+
|
|
164
|
+
drop table gkn;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
-- A grouped query returns exactly the columns in its SELECT list, in SELECT-list
|
|
2
|
+
-- order — nothing more, and not in GROUP BY order.
|
|
3
|
+
--
|
|
4
|
+
-- Regression for bug-grouped-aggregate-only-select-returns-extra-column. An
|
|
5
|
+
-- AggregateNode advertises every grouping key it computes followed by its
|
|
6
|
+
-- aggregate results, so whenever the planner skipped the final projection the
|
|
7
|
+
-- query's declared result shape became the aggregate's shape rather than the
|
|
8
|
+
-- select list's: `select count(*) from t group by g` handed back `g` as well as
|
|
9
|
+
-- the count, and a select list that named its keys in a different order than
|
|
10
|
+
-- GROUP BY got them back in GROUP BY order. The projection is now skipped only
|
|
11
|
+
-- when the two shapes already agree column for column.
|
|
12
|
+
--
|
|
13
|
+
-- Column *names and order* are asserted in test/plan/grouped-projection-shape.spec.ts
|
|
14
|
+
-- (row objects compare key-order-insensitively here); this file pins the values.
|
|
15
|
+
|
|
16
|
+
create table t (v integer primary key, g text null);
|
|
17
|
+
insert into t values (1,'a'),(2,null),(3,'b'),(4,null);
|
|
18
|
+
|
|
19
|
+
-- The original repro: select list is only an aggregate, so the grouping key must
|
|
20
|
+
-- not appear at all.
|
|
21
|
+
select count(*) as n from t group by g;
|
|
22
|
+
→ [{"n":1},{"n":2},{"n":1}]
|
|
23
|
+
|
|
24
|
+
select sum(v) s from t group by g;
|
|
25
|
+
→ [{"s":1},{"s":6},{"s":3}]
|
|
26
|
+
|
|
27
|
+
-- Unaliased aggregate keeps its expression name and stays the only column.
|
|
28
|
+
select count(*) from t group by g;
|
|
29
|
+
→ [{"count(*)":1},{"count(*)":2},{"count(*)":1}]
|
|
30
|
+
|
|
31
|
+
-- HAVING attached does not reintroduce the key.
|
|
32
|
+
select count(*) as n from t group by g having count(*) > 1;
|
|
33
|
+
→ [{"n":2}]
|
|
34
|
+
|
|
35
|
+
-- ORDER BY over the (unselected) grouping key still sorts by it.
|
|
36
|
+
select count(*) as n from t group by g order by g nulls first;
|
|
37
|
+
→ [{"n":2},{"n":1},{"n":1}]
|
|
38
|
+
|
|
39
|
+
-- Contrast: the scalar-aggregate (no GROUP BY) case was always correct.
|
|
40
|
+
select count(*) n from t;
|
|
41
|
+
→ [{"n":4}]
|
|
42
|
+
|
|
43
|
+
-- Contrast: naming the key keeps it, exactly once.
|
|
44
|
+
select g, count(*) c from t group by g order by g nulls first;
|
|
45
|
+
→ [{"g":null,"c":2},{"g":"a","c":1},{"g":"b","c":1}]
|
|
46
|
+
|
|
47
|
+
drop table t;
|
|
48
|
+
|
|
49
|
+
-- ---------------------------------------------------------------------------
|
|
50
|
+
-- SELECT-list order, independent of GROUP BY order. No primary key, so the
|
|
51
|
+
-- functional-dependency GROUP BY reduction cannot rewrite the grouping keys out
|
|
52
|
+
-- from under these cases.
|
|
53
|
+
-- ---------------------------------------------------------------------------
|
|
54
|
+
create table nk (a text, b text);
|
|
55
|
+
insert into nk values ('x','1'),('y','2');
|
|
56
|
+
create table u (z text);
|
|
57
|
+
insert into u values ('p'),('q');
|
|
58
|
+
|
|
59
|
+
-- Aggregate first, grouping key after it.
|
|
60
|
+
select count(*) c, a from nk group by a order by a;
|
|
61
|
+
→ [{"c":1,"a":"x"},{"c":1,"a":"y"}]
|
|
62
|
+
|
|
63
|
+
-- Keys named in the reverse of GROUP BY order.
|
|
64
|
+
select b, a, count(*) c from nk group by a, b order by a;
|
|
65
|
+
→ [{"b":"1","a":"x","c":1},{"b":"2","a":"y","c":1}]
|
|
66
|
+
|
|
67
|
+
-- Aggregate interleaved between two grouping keys.
|
|
68
|
+
select a, count(*) c, b from nk group by a, b order by a;
|
|
69
|
+
→ [{"a":"x","c":1,"b":"1"},{"a":"y","c":1,"b":"2"}]
|
|
70
|
+
|
|
71
|
+
-- Fewer keys named than grouped on.
|
|
72
|
+
select count(*) c from nk group by a, b order by a;
|
|
73
|
+
→ [{"c":1},{"c":1}]
|
|
74
|
+
|
|
75
|
+
-- Qualified reference to a bare grouping key is the same column.
|
|
76
|
+
select nk.a, count(*) c from nk group by a order by a;
|
|
77
|
+
→ [{"a":"x","c":1},{"a":"y","c":1}]
|
|
78
|
+
|
|
79
|
+
-- SELECT * over a grouped query with an aggregate: source-column order, then the
|
|
80
|
+
-- aggregate.
|
|
81
|
+
select *, count(*) c from nk group by a, b order by a;
|
|
82
|
+
→ [{"a":"x","b":"1","c":1},{"a":"y","b":"2","c":1}]
|
|
83
|
+
|
|
84
|
+
-- Grouped/ungrouped agreement: a mixed select list (named columns plus a star,
|
|
85
|
+
-- no aggregate) assembles star position the same way whether or not the query
|
|
86
|
+
-- is grouped — the ungrouped path (buildFinalProjections) and the grouped path
|
|
87
|
+
-- (buildFinalAggregateProjections) must agree. `group by a, b` collapses no
|
|
88
|
+
-- rows here (a, b together are already unique), so the two queries return the
|
|
89
|
+
-- same shape and values.
|
|
90
|
+
select b, a, * from nk order by a;
|
|
91
|
+
→ [{"b":"1","a":"x","a:1":"x","b:1":"1"},{"b":"2","a":"y","a:1":"y","b:1":"2"}]
|
|
92
|
+
|
|
93
|
+
select b, a, * from nk group by a, b order by a;
|
|
94
|
+
→ [{"b":"1","a":"x","a:1":"x","b:1":"1"},{"b":"2","a":"y","a:1":"y","b:1":"2"}]
|
|
95
|
+
|
|
96
|
+
-- Two aggregates and no named key: still no key in the output.
|
|
97
|
+
select count(*) c1, count(distinct b) c2 from nk group by a order by c1;
|
|
98
|
+
→ [{"c1":1,"c2":1},{"c1":1,"c2":1}]
|
|
99
|
+
|
|
100
|
+
-- A grouping key named twice is emitted twice — the select list is one column
|
|
101
|
+
-- shorter than the aggregate output here, the opposite of the leaked-key case.
|
|
102
|
+
select a, a, count(*) c from nk group by a order by a;
|
|
103
|
+
→ [{"a":"x","a:1":"x","c":1},{"a":"y","a:1":"y","c":1}]
|
|
104
|
+
|
|
105
|
+
-- An aggregate inside a scalar subquery aggregates over the *other* table, so it
|
|
106
|
+
-- is a non-aggregate item of this query and must not claim an aggregate slot.
|
|
107
|
+
select (select count(*) from u) su, count(*) c from nk group by a order by a;
|
|
108
|
+
→ [{"su":2,"c":1},{"su":2,"c":1}]
|
|
109
|
+
|
|
110
|
+
-- The aggregate-only grouped select composes as a derived table...
|
|
111
|
+
select n from (select count(*) n from nk group by a) order by n;
|
|
112
|
+
→ [{"n":1},{"n":1}]
|
|
113
|
+
|
|
114
|
+
-- ...as a CTE...
|
|
115
|
+
with c as (select count(*) n from nk group by a) select * from c;
|
|
116
|
+
→ [{"n":1},{"n":1}]
|
|
117
|
+
|
|
118
|
+
-- ...and as a materialized view body, where the reordered projection must still
|
|
119
|
+
-- carry the grouping key through as the view's unique key.
|
|
120
|
+
create materialized view mvn as select count(*) n, a from nk group by a;
|
|
121
|
+
select n, a from mvn order by a;
|
|
122
|
+
→ [{"n":1,"a":"x"},{"n":1,"a":"y"}]
|
|
123
|
+
|
|
124
|
+
drop materialized view mvn;
|
|
125
|
+
|
|
126
|
+
-- Contrast: an aggregate-only body has no grouping key in its output, so it is
|
|
127
|
+
-- genuinely unkeyed and a materialized view rejects it. Before the projection was
|
|
128
|
+
-- built the leaked grouping key made this body look keyed by accident.
|
|
129
|
+
create materialized view mvbad as select count(*) n from nk group by a;
|
|
130
|
+
-- error: materialized view 'main.mvbad' body produces duplicate rows
|
|
131
|
+
|
|
132
|
+
drop table u;
|
|
133
|
+
drop table nk;
|
|
134
|
+
|
|
135
|
+
-- ---------------------------------------------------------------------------
|
|
136
|
+
-- The select list survives the FD-driven GROUP BY simplification.
|
|
137
|
+
--
|
|
138
|
+
-- `rule-groupby-fd-simplification` drops a grouping column that another grouping
|
|
139
|
+
-- column functionally determines and re-emits it as a picker `min(<col>)`. An
|
|
140
|
+
-- aggregate's layout is fixed — grouping keys first, then aggregate results — so
|
|
141
|
+
-- the dropped column leaves the key block for the aggregate block. Each select
|
|
142
|
+
-- list below already agrees with the *pre-rewrite* aggregate shape, so no
|
|
143
|
+
-- projection is built above the aggregate and it is the query root: the shift
|
|
144
|
+
-- used to reach the result (bug-grouped-key-reorder-survives-to-output).
|
|
145
|
+
--
|
|
146
|
+
-- Column order is asserted in test/plan/grouped-projection-shape.spec.ts (row
|
|
147
|
+
-- objects compare key-order-insensitively here); this file pins the values, so a
|
|
148
|
+
-- picker that recovered the wrong value per group would fail here.
|
|
149
|
+
-- ---------------------------------------------------------------------------
|
|
150
|
+
|
|
151
|
+
-- Primary-key-driven: `v` determines `g`, so `g` is dropped and re-picked.
|
|
152
|
+
create table pk (v integer primary key, g text);
|
|
153
|
+
insert into pk values (1,'a'),(2,'b'),(3,'a');
|
|
154
|
+
|
|
155
|
+
select g, v, count(*) c from pk group by g, v order by v;
|
|
156
|
+
→ [{"g":"a","v":1,"c":1},{"g":"b","v":2,"c":1},{"g":"a","v":3,"c":1}]
|
|
157
|
+
|
|
158
|
+
drop table pk;
|
|
159
|
+
|
|
160
|
+
-- Equality-driven: `where a = b` puts the two grouping columns in one equivalence
|
|
161
|
+
-- class, so either one determines the other.
|
|
162
|
+
create table eqk (a text, b text);
|
|
163
|
+
insert into eqk values ('x','x'),('y','z'),('w','w');
|
|
164
|
+
|
|
165
|
+
select a, b, count(*) c from eqk where a = b group by a, b order by a;
|
|
166
|
+
→ [{"a":"w","b":"w","c":1},{"a":"x","b":"x","c":1}]
|
|
167
|
+
|
|
168
|
+
drop table eqk;
|
|
169
|
+
|
|
170
|
+
-- Join-equality-driven, with the dropped column in the middle of the select list.
|
|
171
|
+
create table jl (a text, b text);
|
|
172
|
+
create table jr (a text, c text);
|
|
173
|
+
insert into jl values ('x','1'),('y','2');
|
|
174
|
+
insert into jr values ('x','p'),('y','q');
|
|
175
|
+
|
|
176
|
+
select jl.a, jl.b, jr.a, jr.c, count(*) c from jl join jr on jl.a = jr.a
|
|
177
|
+
group by jl.a, jl.b, jr.a, jr.c order by jl.a;
|
|
178
|
+
→ [{"a":"x","b":"1","a:1":"x","c":"p","c:1":1},{"a":"y","b":"2","a:1":"y","c":"q","c:1":1}]
|
|
179
|
+
|
|
180
|
+
drop table jl;
|
|
181
|
+
drop table jr;
|
|
182
|
+
|
|
183
|
+
-- The same two same-named keys with NO dependency between them (the join is on
|
|
184
|
+
-- the *other* pair of columns), so the FD simplification does not fire and
|
|
185
|
+
-- nothing caps the aggregate: it is the query root itself. Its own output names
|
|
186
|
+
-- must still be disambiguated the way a projection's are — a result row is
|
|
187
|
+
-- keyed by column name, so a repeated `a` drops one of the two values
|
|
188
|
+
-- (bug-duplicate-aggregate-output-names-collapse-row). The memory and store
|
|
189
|
+
-- modules pick different plans here, so this pins both physical aggregates.
|
|
190
|
+
create table dl (a text, b text);
|
|
191
|
+
create table dr (a text, c text);
|
|
192
|
+
insert into dl values ('x','p'),('y','q');
|
|
193
|
+
insert into dr values ('m','p'),('n','q');
|
|
194
|
+
|
|
195
|
+
select dl.a, dr.a, count(*) n from dl join dr on dl.b = dr.c group by dl.a, dr.a order by dl.a;
|
|
196
|
+
→ [{"a":"x","a:1":"m","n":1},{"a":"y","a:1":"n","n":1}]
|
|
197
|
+
|
|
198
|
+
drop table dl;
|
|
199
|
+
drop table dr;
|
|
200
|
+
|
|
201
|
+
-- `insert into … select` binds the select output by POSITION, so it is the
|
|
202
|
+
-- consumer the reorder actually corrupted: with the shift, `g`'s text landed in
|
|
203
|
+
-- `sink.v` and `v`'s integer in `sink.g`.
|
|
204
|
+
create table src (v integer primary key, g text);
|
|
205
|
+
insert into src values (1,'a'),(2,'b');
|
|
206
|
+
create table sink (g text, v integer, c integer);
|
|
207
|
+
|
|
208
|
+
insert into sink select g, v, count(*) from src group by g, v;
|
|
209
|
+
select g, v, c from sink order by v;
|
|
210
|
+
→ [{"g":"a","v":1,"c":1},{"g":"b","v":2,"c":1}]
|
|
211
|
+
|
|
212
|
+
-- A `union` arm is the other positional consumer: arms are matched column by
|
|
213
|
+
-- position, and the union's column names come from the first arm. With the shift,
|
|
214
|
+
-- the grouped arm contributed (v, g, c) — pairing integers against the literal
|
|
215
|
+
-- arm's text and vice versa.
|
|
216
|
+
select g, v, count(*) c from src group by g, v
|
|
217
|
+
union all
|
|
218
|
+
select 'z', 9, 1
|
|
219
|
+
order by v;
|
|
220
|
+
→ [{"g":"a","v":1,"c":1},{"g":"b","v":2,"c":1},{"g":"z","v":9,"c":1}]
|
|
221
|
+
|
|
222
|
+
-- Grouped arm second: the union's column names now come from the literal arm, so
|
|
223
|
+
-- a shifted grouped arm would silently file its values under the wrong names.
|
|
224
|
+
select 'z' g, 9 v, 1 c
|
|
225
|
+
union all
|
|
226
|
+
select g, v, count(*) from src group by g, v
|
|
227
|
+
order by v;
|
|
228
|
+
→ [{"g":"a","v":1,"c":1},{"g":"b","v":2,"c":1},{"g":"z","v":9,"c":1}]
|
|
229
|
+
|
|
230
|
+
drop table sink;
|
|
231
|
+
drop table src;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
-- Regression: streaming aggregate must not leak a yielded group's
|
|
2
|
+
-- representative-row context across the next child pull.
|
|
3
|
+
--
|
|
4
|
+
-- A grouped StreamAggregate yields the just-finished group with that group's
|
|
5
|
+
-- representative SOURCE row installed in the runtime context (for HAVING /
|
|
6
|
+
-- correlated reads / projection). Those descriptors are built from the source's
|
|
7
|
+
-- attribute IDs — the SAME IDs a streaming child Filter publishes for its own
|
|
8
|
+
-- row. If the aggregate leaves them live while it pulls the next input row, the
|
|
9
|
+
-- Filter directly below resolves its predicate columns through the aggregate's
|
|
10
|
+
-- STALE representative row instead of the child's current row, so the WHERE
|
|
11
|
+
-- predicate is evaluated against the wrong values and effectively dropped.
|
|
12
|
+
--
|
|
13
|
+
-- Shape that triggers it (StreamAggregate, no interposed Sort, standalone Filter
|
|
14
|
+
-- directly below):
|
|
15
|
+
-- * composite PK (d, r) so the IndexScan is already ordered for GROUP BY d
|
|
16
|
+
-- => StreamAggregate is chosen with no Sort (a Sort would drain the child),
|
|
17
|
+
-- * filter on a non-leading PK column (r) absent from GROUP BY => cannot seek,
|
|
18
|
+
-- => the predicate stays a standalone Filter node right under the aggregate.
|
|
19
|
+
--
|
|
20
|
+
-- Without the fix, the row pulled immediately AFTER a group boundary is filtered
|
|
21
|
+
-- against the previous group's stale representative row, so a row that should be
|
|
22
|
+
-- dropped is wrongly folded into the new group.
|
|
23
|
+
|
|
24
|
+
create table bt (d integer, r integer, total integer null, primary key (d, r));
|
|
25
|
+
insert into bt values
|
|
26
|
+
(1, 10, 150),
|
|
27
|
+
(1, 20, null),
|
|
28
|
+
(2, 10, 7),
|
|
29
|
+
(2, 20, 3),
|
|
30
|
+
(3, 10, 1),
|
|
31
|
+
(3, 20, 99);
|
|
32
|
+
|
|
33
|
+
-- PRIMARY BUG: equality filter on the non-leading composite-PK column.
|
|
34
|
+
-- d=2 is the tell: only (2,10,7) matches r=10, so sum is 7 (NOT 7+3=10).
|
|
35
|
+
-- d=3 likewise: only (3,10,1) matches, so sum is 1 (NOT 1+99=100).
|
|
36
|
+
-- d=1 reads 150 either way (its dropped row (1,20,null) contributes null to sum).
|
|
37
|
+
select d, sum(total) as s from bt where r = 10 group by d order by d;
|
|
38
|
+
→ [{"d":1,"s":150},{"d":2,"s":7},{"d":3,"s":1}]
|
|
39
|
+
|
|
40
|
+
-- RANGE filter (no constant-pin / attributeDefaults surface): same expected rows,
|
|
41
|
+
-- proving the fix is not specific to the r=const shape.
|
|
42
|
+
select d, sum(total) as s from bt where r > 9 and r < 11 group by d order by d;
|
|
43
|
+
→ [{"d":1,"s":150},{"d":2,"s":7},{"d":3,"s":1}]
|
|
44
|
+
|
|
45
|
+
-- count(*) catches the wrong-keep directly: under the bug d=2/d=3 would each see
|
|
46
|
+
-- a second (wrongly-kept) row. Correct count is 1 per group. count(col) likewise.
|
|
47
|
+
select d, count(*) as c, count(total) as ct from bt where r = 10 group by d order by d;
|
|
48
|
+
→ [{"d":1,"c":1,"ct":1},{"d":2,"c":1,"ct":1},{"d":3,"c":1,"ct":1}]
|
|
49
|
+
|
|
50
|
+
-- DISTINCT aggregate under the same shape — exercises the per-group distinct-tree
|
|
51
|
+
-- reset adjacent to the edited code. A wrongly-kept post-boundary row would inflate
|
|
52
|
+
-- the distinct sum (d=2 -> 10, d=3 -> 100). Correct values match sum(total).
|
|
53
|
+
select d, sum(distinct total) as sd from bt where r = 10 group by d order by d;
|
|
54
|
+
→ [{"d":1,"sd":150},{"d":2,"sd":7},{"d":3,"sd":1}]
|
|
55
|
+
|
|
56
|
+
-- HAVING over the filtered rollup — confirms the representative-row context is still
|
|
57
|
+
-- readable during the yield, and the filtered sums feed HAVING correctly. Only d=1
|
|
58
|
+
-- exceeds 100 once the bug-kept rows are excluded.
|
|
59
|
+
select d, sum(total) as s from bt where r = 10 group by d having sum(total) > 100 order by d;
|
|
60
|
+
→ [{"d":1,"s":150}]
|
|
61
|
+
|
|
62
|
+
-- DROPPED-FIRST-ROW-OF-A-LATER-GROUP: filter r=20 keeps (1,20,null),(2,20,3),(3,20,99).
|
|
63
|
+
-- Under the bug the boundary's stale r=20 wrongly passes (3,10,1) (whose real r=10
|
|
64
|
+
-- should be dropped), inflating d=3 to 100. Correct d=3 is 99. d=1 is null (its only
|
|
65
|
+
-- kept row has total null) — also exercises sum-over-all-null.
|
|
66
|
+
select d, sum(total) as s from bt where r = 20 group by d order by d;
|
|
67
|
+
→ [{"d":1,"s":null},{"d":2,"s":3},{"d":3,"s":99}]
|
|
68
|
+
|
|
69
|
+
-- ORDER BY downstream of the aggregate (desc): consumer reads aggregate output
|
|
70
|
+
-- after the generator resumes; must stay correct and ordered.
|
|
71
|
+
select d, sum(total) as s from bt where r = 10 group by d order by d desc;
|
|
72
|
+
→ [{"d":3,"s":1},{"d":2,"s":7},{"d":1,"s":150}]
|
|
73
|
+
|
|
74
|
+
-- Empty result / zero matching rows.
|
|
75
|
+
select d, sum(total) as s from bt where r = 99 group by d order by d;
|
|
76
|
+
→ []
|
|
77
|
+
|
|
78
|
+
-- CONTROL: scalar aggregate (no GROUP BY) — separate code branch, never interleaved
|
|
79
|
+
-- with a yielded-group context. 150 + 7 + 1 = 158.
|
|
80
|
+
select sum(total) as s from bt where r = 10;
|
|
81
|
+
→ [{"s":158}]
|
|
82
|
+
|
|
83
|
+
-- CONTROL: plain projection (no aggregate at all) — the Filter standing alone must
|
|
84
|
+
-- still reject the r=20 rows. Locks that the filter itself is correct.
|
|
85
|
+
select d, r, total from bt where r = 10 order by d;
|
|
86
|
+
→ [{"d":1,"r":10,"total":150},{"d":2,"r":10,"total":7},{"d":3,"r":10,"total":1}]
|
|
87
|
+
|
|
88
|
+
drop table bt;
|
|
89
|
+
|
|
90
|
+
-- CONTROL: single-column-PK analogue. Filtering on the PK column (k) is a seek, so
|
|
91
|
+
-- there is NO standalone Filter under the aggregate — the interleaving hazard cannot
|
|
92
|
+
-- arise. Must remain correct.
|
|
93
|
+
create table sp (k integer primary key, total integer);
|
|
94
|
+
insert into sp values (1, 10), (2, 20), (3, 30);
|
|
95
|
+
|
|
96
|
+
select k, sum(total) as s from sp where k >= 2 group by k order by k;
|
|
97
|
+
→ [{"k":2,"s":20},{"k":3,"s":30}]
|
|
98
|
+
|
|
99
|
+
drop table sp;
|
|
100
|
+
|
|
101
|
+
-- CONTROL: HashAggregate path. With PK on id, GROUP BY d is unsorted, so the planner
|
|
102
|
+
-- picks HashAggregate (it drains the child fully before yielding any group, so no
|
|
103
|
+
-- interleaving). Same logical query as the primary case — assert it stays correct,
|
|
104
|
+
-- don't assume. ORDER BY d makes the (unordered) hash output deterministic.
|
|
105
|
+
create table ht (id integer primary key, d integer, r integer, total integer null);
|
|
106
|
+
insert into ht values
|
|
107
|
+
(1, 1, 10, 150),
|
|
108
|
+
(2, 1, 20, null),
|
|
109
|
+
(3, 2, 10, 7),
|
|
110
|
+
(4, 2, 20, 3),
|
|
111
|
+
(5, 3, 10, 1),
|
|
112
|
+
(6, 3, 20, 99);
|
|
113
|
+
|
|
114
|
+
select d, sum(total) as s from ht where r = 10 group by d order by d;
|
|
115
|
+
→ [{"d":1,"s":150},{"d":2,"s":7},{"d":3,"s":1}]
|
|
116
|
+
|
|
117
|
+
select d, count(*) as c, sum(total) as s from ht where r = 20 group by d order by d;
|
|
118
|
+
→ [{"d":1,"c":1,"s":null},{"d":2,"c":1,"s":3},{"d":3,"c":1,"s":99}]
|
|
119
|
+
|
|
120
|
+
drop table ht;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
-- 07.5-sum-mixed-exact-and-float.sqllogic
|
|
2
|
+
-- Regression: sum() over a column mixing exact integers with floating-point values.
|
|
3
|
+
--
|
|
4
|
+
-- sum() keeps TWO accumulator slots and never mixes the two number domains until
|
|
5
|
+
-- finalize: a bigint or safe-integer number joins the exact part, everything else
|
|
6
|
+
-- (fractions, whole numbers past the safe-integer boundary, non-finites) joins the
|
|
7
|
+
-- floating-point part. Before that split, one slot decided per addition whether the
|
|
8
|
+
-- running total was exact or float, which
|
|
9
|
+
-- (a) silently dropped values (a fractional side made BigInt() throw and the step
|
|
10
|
+
-- swallowed it),
|
|
11
|
+
-- (b) turned a REAL column's total into an arbitrary-precision integer, and
|
|
12
|
+
-- (c) made the answer depend on the order rows happened to be scanned.
|
|
13
|
+
--
|
|
14
|
+
-- Every fold-order pair below must agree — that equality, not just the literal
|
|
15
|
+
-- expected value, is what this file is guarding.
|
|
16
|
+
--
|
|
17
|
+
-- NOTE: the two fold orders are forced with `order by id` inside a subquery, and
|
|
18
|
+
-- nothing here asserts that the two scans genuinely ran in opposite orders. If the
|
|
19
|
+
-- planner ever becomes free to discard an inner `order by` that no outer clause
|
|
20
|
+
-- observes, these arms weaken to two identical scans and stop guarding anything —
|
|
21
|
+
-- they would still pass. If that optimization lands, pin the orders some other way
|
|
22
|
+
-- (an ordered plan assertion under test/plan/, or scan-order-sensitive data).
|
|
23
|
+
|
|
24
|
+
-- ===========================================================================
|
|
25
|
+
-- 1. Fraction + integer past the safe-integer boundary — both scan orders
|
|
26
|
+
-- ===========================================================================
|
|
27
|
+
|
|
28
|
+
create table sum_mixed (id integer primary key, v numeric);
|
|
29
|
+
insert into sum_mixed values (1, 0.5), (2, 9007199254740993);
|
|
30
|
+
-- run
|
|
31
|
+
|
|
32
|
+
-- Ascending: the fraction arrives first. Used to return 0.5 (the large value vanished).
|
|
33
|
+
select sum(v) as s, typeof(sum(v)) as ty from sum_mixed;
|
|
34
|
+
→ [{"s":9007199254740992,"ty":"integer"}]
|
|
35
|
+
|
|
36
|
+
-- Descending: the large value arrives first. Used to return 9007199254740993.
|
|
37
|
+
select sum(v) as s from (select v from sum_mixed order by id desc);
|
|
38
|
+
→ [{"s":9007199254740992}]
|
|
39
|
+
|
|
40
|
+
-- Fold-order independence stated directly, not inferred from two literals matching.
|
|
41
|
+
select (select sum(v) from (select v from sum_mixed order by id asc))
|
|
42
|
+
= (select sum(v) from (select v from sum_mixed order by id desc)) as same;
|
|
43
|
+
→ [{"same":true}]
|
|
44
|
+
|
|
45
|
+
-- Anchored to binary `+` over the same pair — the engine's rule for one mixed
|
|
46
|
+
-- addition, which sum's routing now mirrors.
|
|
47
|
+
select sum(v) = 9007199254740993 + 0.5 as matches_binary_plus from sum_mixed;
|
|
48
|
+
→ [{"matches_binary_plus":true}]
|
|
49
|
+
|
|
50
|
+
-- ===========================================================================
|
|
51
|
+
-- 2. Three values: two fractions and one large integer
|
|
52
|
+
-- ===========================================================================
|
|
53
|
+
-- Used to return 0.75 — the large value dropped and both fractions survived.
|
|
54
|
+
|
|
55
|
+
create table sum_three (id integer primary key, v numeric);
|
|
56
|
+
insert into sum_three values (1, 0.5), (2, 0.25), (3, 9007199254740993);
|
|
57
|
+
-- run
|
|
58
|
+
|
|
59
|
+
select sum(v) as s from sum_three;
|
|
60
|
+
→ [{"s":9007199254740992}]
|
|
61
|
+
|
|
62
|
+
select sum(v) as s from (select v from sum_three order by id desc);
|
|
63
|
+
→ [{"s":9007199254740992}]
|
|
64
|
+
|
|
65
|
+
-- ===========================================================================
|
|
66
|
+
-- 3. A whole `number` outside the safe-integer range is NOT exact
|
|
67
|
+
-- ===========================================================================
|
|
68
|
+
-- 1e308 is a legal `number` (R1 constrains which values may be bigint, not which
|
|
69
|
+
-- numbers may be whole). Routing it to the exact part is what produced a 309-digit
|
|
70
|
+
-- integer total for a REAL column — a bigint in a REAL result, violating R2.
|
|
71
|
+
|
|
72
|
+
create table sum_real (id integer primary key, v real);
|
|
73
|
+
insert into sum_real values (1, 1e308), (2, 0.5);
|
|
74
|
+
-- run
|
|
75
|
+
|
|
76
|
+
select sum(v) as s, typeof(sum(v)) as ty from sum_real;
|
|
77
|
+
→ [{"s":1e308,"ty":"integer"}]
|
|
78
|
+
|
|
79
|
+
select sum(v) as s from (select v from sum_real order by id desc);
|
|
80
|
+
→ [{"s":1e308}]
|
|
81
|
+
|
|
82
|
+
select sum(v) = 0.5 + 1e308 as matches_binary_plus from sum_real;
|
|
83
|
+
→ [{"matches_binary_plus":true}]
|
|
84
|
+
|
|
85
|
+
-- ===========================================================================
|
|
86
|
+
-- 4. A REAL total that overflows to infinity
|
|
87
|
+
-- ===========================================================================
|
|
88
|
+
-- Used to return a 309-digit exact integer with typeof 'integer'. It now stays in
|
|
89
|
+
-- the float domain and agrees with total(), the other float-summing aggregate.
|
|
90
|
+
-- (Infinity has no JSON spelling, so this section asserts through text and equality.)
|
|
91
|
+
|
|
92
|
+
create table sum_overflow (id integer primary key, v real);
|
|
93
|
+
insert into sum_overflow values (1, 1e308), (2, 1e308);
|
|
94
|
+
-- run
|
|
95
|
+
|
|
96
|
+
select typeof(sum(v)) as ty, cast(sum(v) as text) as txt from sum_overflow;
|
|
97
|
+
→ [{"ty":"real","txt":"Infinity"}]
|
|
98
|
+
|
|
99
|
+
select sum(v) = total(v) as agrees_with_total from sum_overflow;
|
|
100
|
+
→ [{"agrees_with_total":true}]
|
|
101
|
+
|
|
102
|
+
-- ===========================================================================
|
|
103
|
+
-- 5. Unchanged: all-integer and all-float folds
|
|
104
|
+
-- ===========================================================================
|
|
105
|
+
|
|
106
|
+
-- An all-integer fold still accumulates exactly, promoting and narrowing under R1.
|
|
107
|
+
create table sum_ints (id integer primary key, v integer);
|
|
108
|
+
insert into sum_ints values (1, 9007199254740991), (2, 5), (3, -6);
|
|
109
|
+
-- run
|
|
110
|
+
|
|
111
|
+
-- Promotes past 2^53−1 mid-fold, then retracts back inside it.
|
|
112
|
+
select sum(v) as s, typeof(sum(v)) as ty from sum_ints;
|
|
113
|
+
→ [{"s":9007199254740990,"ty":"integer"}]
|
|
114
|
+
|
|
115
|
+
-- A total that stays outside the safe range remains exact.
|
|
116
|
+
delete from sum_ints where id = 3;
|
|
117
|
+
select sum(v) as s from sum_ints;
|
|
118
|
+
→ [{"s":9007199254740996}]
|
|
119
|
+
|
|
120
|
+
-- An all-float fold is bit-identical to plain left-to-right float addition.
|
|
121
|
+
create table sum_floats (id integer primary key, v real);
|
|
122
|
+
insert into sum_floats values (1, 0.1), (2, 0.2);
|
|
123
|
+
-- run
|
|
124
|
+
|
|
125
|
+
select sum(v) = 0.1 + 0.2 as same from sum_floats;
|
|
126
|
+
→ [{"same":true}]
|
|
127
|
+
|
|
128
|
+
-- Empty group and all-NULL group still finalize to NULL.
|
|
129
|
+
create table sum_nulls (id integer primary key, v numeric null);
|
|
130
|
+
insert into sum_nulls values (1, null), (2, null);
|
|
131
|
+
-- run
|
|
132
|
+
|
|
133
|
+
select sum(v) as s from sum_nulls;
|
|
134
|
+
→ [{"s":null}]
|
|
135
|
+
|
|
136
|
+
select sum(v) as s from sum_nulls where id > 99;
|
|
137
|
+
→ [{"s":null}]
|
|
138
|
+
|
|
139
|
+
-- ===========================================================================
|
|
140
|
+
-- 6. GROUP BY keeps the split per group
|
|
141
|
+
-- ===========================================================================
|
|
142
|
+
|
|
143
|
+
create table sum_grouped (id integer primary key, g text, v numeric);
|
|
144
|
+
insert into sum_grouped values
|
|
145
|
+
(1, 'exact', 9007199254740993), (2, 'exact', 5),
|
|
146
|
+
(3, 'mixed', 9007199254740993), (4, 'mixed', 0.5),
|
|
147
|
+
(5, 'float', 0.5), (6, 'float', 0.25);
|
|
148
|
+
-- run
|
|
149
|
+
|
|
150
|
+
select g, sum(v) as s, typeof(sum(v)) as ty from sum_grouped group by g order by g;
|
|
151
|
+
→ [{"g":"exact","s":9007199254740998,"ty":"integer"},{"g":"float","s":0.75,"ty":"real"},{"g":"mixed","s":9007199254740992,"ty":"integer"}]
|