diff --git a/benchmarks/duckshop/README.md b/benchmarks/duckshop/README.md index 298e8b8f62..04cc4d03a0 100644 --- a/benchmarks/duckshop/README.md +++ b/benchmarks/duckshop/README.md @@ -117,23 +117,29 @@ project and does not change it: - unit tests in the matching `tests/unit//edge_cases/` folders and scenarios in `tests/scenarios/edge_cases/`; - raw `edge__*` sources in `sources/edge_cases.yml`, loaded by `python/loaders/edge_cases.py` from - fixed rows that `data_scale` does not change, so core data and core expectations stay the same. + fixed rows that `data_scale` does not change, so core data and core expectations stay the same; +- Python functions in `functions/python/edge_*.py`, tagged `edge_case`, and their tests in + `tests/unit/functions/python/test_edge_*.sql`. -It has 39 models (7 staging, 22 intermediate, 10 marts), 63 unit tests and 4 scenarios, in four -groups: +It has 67 models (12 staging, 45 intermediate, 10 marts), 5 Python functions, 92 unit tests and 4 +scenarios, in eight groups: | Group | Covers | |-------|--------| -| Semi-structured | JSON null against SQL NULL and missing keys; document text with sorted, case-differing and escaped keys; JSON number fidelity and casts out of `VARIANT`; path-like keys (`''`, `*`, `$`-prefixed, dotted) with `GET` and `GET_IGNORE_CASE`; `OBJECT_CONSTRUCT_KEEP_NULL`, `OBJECT_INSERT`, `OBJECT_DELETE`, `OBJECT_PICK`; array builders, `ARRAY_EXCEPT` and number spellings in `ARRAY_POSITION`; `FLATTEN` outer; lenient and malformed `TRY_PARSE_JSON`; typed lambdas | -| Dates and times | `TRY_TO_TIMESTAMP_NTZ` and its siblings over free text; epoch milliseconds as numbers and text; daylight-saving gaps and overlaps; fractional offsets; nanosecond truncation and epoch parts after 2242; pre-1970 text with sub-microsecond digits; date-part normalisation; month ends and leap days; week numbering across a year; sentinel timestamps | -| Ordering, numbers and text | NULL ordering under `DESC`; ties in ordered aggregates, `MAX_BY`, `MODE` and `ANY_VALUE`; default window frames; `UNION ALL BY NAME`; `ROUND` half-way values, decimal division, `MOD` and 38-digit amounts; integers past 2^53 against `FLOAT`; regular expressions and backslash escapes; `LIKE ... ESCAPE` and `ILIKE ANY`; `SPLIT_PART` and `SUBSTR` before the start; `NOT IN` with a NULL; names in many scripts | +| Semi-structured | JSON null against SQL NULL and missing keys; document text with sorted, case-differing and escaped keys; JSON number fidelity and casts out of `VARIANT`; path-like keys (`''`, `*`, `$`-prefixed, dotted) with `GET` and `GET_IGNORE_CASE`; `OBJECT_CONSTRUCT_KEEP_NULL`, `OBJECT_INSERT`, `OBJECT_DELETE`, `OBJECT_PICK`; array builders, `ARRAY_EXCEPT` and number spellings in `ARRAY_POSITION`; `FLATTEN` outer; lenient and malformed `TRY_PARSE_JSON`; typed lambdas; whole numbers written with zero fractions, numbers with two decimal points and numbers past 38 digits; `FLATTEN` `RECURSIVE` paths for keys holding `$`, `-`, `@`, `[`, spaces and non-ASCII letters | +| Ordering through arrays | an unordered `ARRAY_AGG` ranked under `LIMIT` by position, by name, through `IFF` and over `UNION ALL`, with fixtures whose pick does not depend on element order; order-free reads (`ARRAY_SIZE`, `ARRAY_CONTAINS`, `ARRAYS_OVERLAP`, `ARRAY_DISTINCT` of `ARRAY_CAT`, `FLATTEN` values through a CTE); `SELECT DISTINCT` over arrays repeated in different orders | +| VARIANT comparisons | `ARRAY_MAX`, `ARRAY_MIN` and `ARRAY_REVERSE` over arrays mixing numbers, strings, booleans, JSON nulls and integers past 2^53; `MAX` across kinds and non-ASCII strings; `=` and `EQUAL_NULL` for `1`, `"1"` and `1.0`, reordered keys, JSON null and DATE or TIMESTAMP values held in a `VARIANT` against their text; `MIN_BY` and `MAX_BY` of `VARIANT` values; `PARTITION BY`, `SELECT DISTINCT` and `ARRAY_AGG(DISTINCT)` over `VARIANT`; JSON nulls in `ARRAY_REMOVE`, `ARRAY_DISTINCT` and `ARRAYS_OVERLAP`; decimals with trailing zeros in `ARRAY_CONTAINS` and `ARRAY_POSITION`; `OBJECT_CONSTRUCT(* EXCLUDE ("note"))` | +| Dates and times | `TRY_TO_TIMESTAMP_NTZ` and its siblings over free text; epoch milliseconds as numbers and text; daylight-saving gaps and overlaps; fractional offsets; nanosecond truncation and epoch parts after 2242; pre-1970 text with sub-microsecond digits; date-part normalisation; month ends and leap days; week numbering across a year; sentinel timestamps; `TRY_TO_DATE` with numeric and month-name formats over unpadded fields, two-digit years, ISO `T` and `Z`, trailing letters, a long s, mixed separators and runs of spaces; text read as `TIMESTAMP_TZ` and `TIMESTAMP_LTZ` with `T`, `Z`, `+00:00`, `+0000`, `+09:00` and `-05:30`, back as the UTC instant, date, year and day start, and compared with text, `TIMESTAMP_NTZ` and `DATE`; `FIRST_VALUE` of a zoned value as a `DATE`; `DATEADD` and `TIMEADD` on `TIME(3)` and the type they return; `DATEDIFF` across units; `TIME(9)`, `FF3` and bare `FF` formats truncating | +| Ordering, numbers and text | NULL ordering under `DESC`; ties in ordered aggregates, `MAX_BY`, `MODE` and `ANY_VALUE`; default window frames; `UNION ALL BY NAME`; `ROUND` half-way values, decimal division, `MOD` and 38-digit amounts; integers past 2^53 against `FLOAT`; regular expressions and backslash escapes; `LIKE ... ESCAPE` and `ILIKE ANY`; `SPLIT_PART` and `SUBSTR` before the start; `NOT IN` with a NULL; names in many scripts; `JOIN ... USING` between `NUMBER(18,0)`, `NUMBER(38,0)` and text keys, with the types of the merged and qualified keys; NULL arithmetic and the types of NULL, decimal and exponent literals, `COUNT`, `ROW_NUMBER`, `%` and `NULLIF`; `MEDIAN` at scales 0 to 15; `DIV0` by literal zeros; `MODE`, `MEDIAN` and `COUNT_IF` over no rows; aggregates inside window functions and `QUALIFY`; `ROLLUP` totals; `TRUNC` of `FLOAT`; infinity words in other scripts; `CASE` without `ELSE` and `DECODE` over text; regular expressions with a `?` after a lazy quantifier, classes opening with `]`, empty matches and `.` against a carriage return; case-insensitive matches outside ASCII; `ILIKE` with a long s; `JAROWINKLER_SIMILARITY` of accented names in both cases; Base64 text with line feeds | +| Lineage and analysis | clauses whose columns decide rows without being output (`WHERE`, `QUALIFY`, `GROUP BY` by position and `ALL`, `ORDER BY` under `LIMIT`, `EXISTS`, a `FLATTEN` input, `ROLLUP` by position, `EXCEPT` under `LIMIT`); `RIGHT JOIN ... USING`, `NATURAL JOIN`, a `USING` key under a later outer join, and unmatched `ASOF JOIN` rows; `CONNECT BY` beside a column named `level`; a recursive CTE without `RECURSIVE` whose columns shift per iteration; `LAST_DAY` with a date part; positional columns and the names Snowflake gives unaliased expressions; `TEXT(5)` and widened `COUNT` contracts | +| Python functions | volatile tokens and numbers from a column, a constant and no argument, and `DIV0` of a volatile call beside look-alike names, checked by assertions that hold for any values; a handler answering NULL input without `STRICT`; Python `None` as JSON null, and datetimes, Decimals and integers past 2^53 handed to `VARIANT` and `FLOAT` results | | Scenarios and lifecycle | typed values across model boundaries; `VARIANT` expected output; a star under a lower-case contract and a quoted camelCase export; a snapshot keyed and compared on quoted camelCase columns; a cursor-window incremental model over far-apart fixture loads; a change log merged across two loads, checked against a full rebuild | -Unit-test expectations in the edge-case area were captured on Snowflake under the `edge` target's +Unit-test expectations in the edge-case area are captured on Snowflake under the `edge` target's pinned session, rather than derived by hand; scenario expectations, and the assertions in tests -about ties, are written by hand from those captured values. `fct_edge_delivery_events` is disabled: it is the -acceptance case for cursor `delete_insert` models that emit rows outside the window they replace. -See [RUNBOOK.md](RUNBOOK.md), step 7. +about ties, volatile values and values Snowflake does not compute exactly, are written by hand +from those captured values. `fct_edge_delivery_events` is disabled: it is the acceptance case for cursor `delete_insert` models +that emit rows outside the window they replace. See [RUNBOOK.md](RUNBOOK.md), step 7. Benchmarks and core measurements leave the area out: @@ -210,6 +216,9 @@ them as expected differences or refusals instead of errors: | `TIMESTAMP_LTZ` | `stg_tickets`, `int_ticket_first_response`, `rpt_customer_order_lookup` | | `CURRENT_TIMESTAMP()` | `int_promotion_performance`, `int_ticket_sla_breaches`, `rpt_customer_order_lookup` | +In the edge-case area, `int_edge_zoned_texts` (`TIMESTAMP_LTZ`) and `int_edge_ticket_tokens` +(volatile Python functions, whose values change on every run) carry the tag too. + ## Known limitations These SQLBuild limitations shape parts of the project; each workaround is a reviewed exception or @@ -228,6 +237,34 @@ noted here: - `SQLCLEAN018` can only take proof from single-column keys. Append-only feeds redeliver rows, so their id is unique only together with `_loaded_at` (the `unique_per_delivery` audit), and the staging models that keep the latest delivery carry reviewed exceptions. +- SQLBuild 0.186.2 reports errors on some valid Snowflake SQL, so the edge-case area cannot hold + these shapes until compilation accepts them: a reference to an alias defined later in the same + `SELECT` or to an enclosing `SELECT`'s alias from a correlated subquery, a qualified positional + column (`o.$2`), a `MATCH_RECOGNIZE` alias used as a qualifier, `ARRAY_UNIQUE_AGG`, + `BITOR_AGG`, `BITXOR_AGG` and `MINHASH` under `GROUP BY`, the unqualified key of a + `NATURAL JOIN`, a timestamp compared with a text column, implicit text and number arguments to + `LPAD`, `REPEAT`, `REVERSE`, `LN` and `EXP`, a `DATE` plus a cast `NUMBER`, `::BYTEINT`, `GROUP BY` + expressions that differ only in case, an outer aggregate inside a subquery and a `WHERE` over a + number. `DATEADD` on a `TIME(3)` is typed `TIME(3)`, where Snowflake returns `TIME(9)`, so + `int_edge_clock_arithmetic` casts those results and records Snowflake's type as text. +- Some shapes need objects DuckShop does not define: fully qualified `schema.table.column` names + (model SQL has no intrinsic for its own schema, and unit tests replace references with CTEs), + `METADATA$` columns of a stream or `CHANGES` clause, and sequence `NEXTVAL`. SQLBuild's `@udf` + has no option for `STRICT` (`RETURNS NULL ON NULL INPUT`), so no function declares it. +- Snowflake rejects some shapes, so the edge-case area leaves them out: a window frame without + `ORDER BY` (such as a running `COUNT_IF`), `CONVERT_TIMEZONE` with a lower-case `'utc'`, and a + scalar subquery inside a `CONNECT BY` query (an internal error). `MAX` over `SYSTEM$TYPEOF` + values that are then joined on also fails with an internal error, so `int_edge_null_arithmetic` + reads one row's type instead. +- Snowflake answers some shapes in ways worth knowing, and the captured expectations record them: + `SYSTEM$TYPEOF(COUNT(x))` does not make a query aggregate; in a `CONNECT BY` query that joins + another relation, a column named `level` reads as the walk depth even when qualified, and + `CONNECT_BY_ROOT` returns the row's own value; `COUNT` of a `VARIANT` skips JSON null; `MODE` of + a number over no rows can return 0; `MEDIAN` above scale 12 is not exact and differs between + plans; `JOIN ... USING` types the qualified keys of an inner join as the narrower side's type. +- SQLBuild 0.186.2 reads the type of a Snowflake source column declared with a precision, such as + `TIME(3)` or `TIMESTAMP_NTZ(3)`, without it, and stops the build with a contract mismatch. The + `edge__date_texts` columns are declared without a precision and cast in `stg_edge_date_texts`. ## Compile diff --git a/benchmarks/duckshop/RUNBOOK.md b/benchmarks/duckshop/RUNBOOK.md index 93a836b905..2c78605335 100644 --- a/benchmarks/duckshop/RUNBOOK.md +++ b/benchmarks/duckshop/RUNBOOK.md @@ -177,9 +177,14 @@ sqb test --target edge --select tag:edge_case --concurrency 8 sqb scenario test --target edge --select edge_cases ``` -The build loads the `edge__*` sources from fixed rows that `data_scale` does not change, then -builds the edge models and runs their tests and audits. `fct_edge_delivery_events` is disabled and -reports `skip: disabled` (see [Pending acceptance cases](#pending-acceptance-cases)). +The build loads the `edge__*` sources from fixed rows that `data_scale` does not change, creates +the area's Python functions (`functions/python/edge_*.py`, tagged `edge_case`), then builds the edge +models and runs their tests and audits. `fct_edge_delivery_events` is disabled and reports +`skip: disabled` (see [Pending acceptance cases](#pending-acceptance-cases)). + +`int_edge_ticket_tokens` calls volatile Python functions, so its values change on every run; its +test compares only the deterministic columns and asserts facts that hold for any values, such as +every token being distinct. ### Replay the delivery change log as two loads @@ -222,6 +227,12 @@ which is the point, but a fixture mistake is captured as faithfully as a real ed expectations, and the assertions in tests about ties (where Snowflake may return any of the tied rows), are written by hand from the captured values. +Some edge models read values whose order Snowflake does not define, such as an `ARRAY_AGG` +without `WITHIN GROUP` or a `MAX_BY` whose keys tie. They output only order-free facts, and a +comment in the model or test says so; leave any column that can still differ between runs out of +the expected CTE and check it with an assertion instead. Run the capture twice on such tests: the +second run must leave the files unchanged. + Capture scenario snapshots for the edge scenarios with `sqb scenario capture --target edge --select edge_cases`. diff --git a/benchmarks/duckshop/project/functions/python/edge_float_value.py b/benchmarks/duckshop/project/functions/python/edge_float_value.py new file mode 100644 index 0000000000..cafb6cd1fc --- /dev/null +++ b/benchmarks/duckshop/project/functions/python/edge_float_value.py @@ -0,0 +1,12 @@ +from sqlbuild.functions import udf + + +@udf( + arguments={"amount": "NUMBER(38, 0)"}, + returns="FLOAT", + runtime_version="3.11", + tags=["edge_case"], +) +def main(amount: int | None) -> int | None: + """The amount handed back as a Python int to a FLOAT result; past 2^53 it loses digits.""" + return amount diff --git a/benchmarks/duckshop/project/functions/python/edge_next_ticket.py b/benchmarks/duckshop/project/functions/python/edge_next_ticket.py new file mode 100644 index 0000000000..42de94418c --- /dev/null +++ b/benchmarks/duckshop/project/functions/python/edge_next_ticket.py @@ -0,0 +1,14 @@ +import uuid + +from sqlbuild.functions import udf + + +@udf( + arguments={}, + returns="NUMBER(38, 0)", + runtime_version="3.11", + tags=["edge_case"], +) +def main() -> int: + """A fresh random positive ticket number on every call, from no arguments.""" + return uuid.uuid4().int % 10**15 + 1 diff --git a/benchmarks/duckshop/project/functions/python/edge_payload_value.py b/benchmarks/duckshop/project/functions/python/edge_payload_value.py new file mode 100644 index 0000000000..e6942a8a4e --- /dev/null +++ b/benchmarks/duckshop/project/functions/python/edge_payload_value.py @@ -0,0 +1,29 @@ +from datetime import UTC, datetime +from decimal import Decimal + +from sqlbuild.functions import udf + + +@udf( + arguments={"kind": "VARCHAR"}, + returns="VARIANT", + runtime_version="3.11", + tags=["edge_case"], +) +def main(kind: str | None) -> object: + """A partner payload value of the named kind, as Python hands it to a VARIANT. + + None becomes JSON null, not SQL NULL. Datetimes, Decimals and integers past 2^53 cross as + Snowflake converts them. + """ + values: dict[str, object] = { + "nothing": None, + "zoned_time": datetime(2026, 3, 1, 9, 30, tzinfo=UTC), + "naive_time": datetime(2026, 3, 1, 9, 30), + "naive_time_fraction": datetime(2026, 3, 1, 9, 30, 0, 250000), + "exponent_decimal": Decimal("1E+3"), + "plain_decimal": Decimal("1.50"), + "wide_integer": 2**60 + 1, + "nested": {"b": [1, None], "a": True}, + } + return values.get(kind or "", kind) diff --git a/benchmarks/duckshop/project/functions/python/edge_tag_label.py b/benchmarks/duckshop/project/functions/python/edge_tag_label.py new file mode 100644 index 0000000000..0ca6c90889 --- /dev/null +++ b/benchmarks/duckshop/project/functions/python/edge_tag_label.py @@ -0,0 +1,18 @@ +from sqlbuild.functions import udf + + +@udf( + arguments={"tag": "VARCHAR"}, + returns="VARCHAR", + runtime_version="3.11", + tags=["edge_case"], +) +def main(tag: str | None) -> str: + """A basket tag as a display label; 'Untagged' for a missing tag. + + The function is not declared STRICT, so Snowflake calls the handler for NULL input too and + returns its answer, not NULL. + """ + if tag is None or not tag.strip(): + return "Untagged" + return tag.strip().capitalize() diff --git a/benchmarks/duckshop/project/functions/python/edge_ticket_token.py b/benchmarks/duckshop/project/functions/python/edge_ticket_token.py new file mode 100644 index 0000000000..a0480d61fe --- /dev/null +++ b/benchmarks/duckshop/project/functions/python/edge_ticket_token.py @@ -0,0 +1,15 @@ +import uuid + +from sqlbuild.functions import udf + + +@udf( + arguments={"seed": "VARCHAR"}, + returns="VARCHAR", + runtime_version="3.11", + tags=["edge_case"], +) +def main(seed: str | None) -> str: + """A fresh random ticket token on every call, whatever the seed: Python UDFs are volatile.""" + del seed + return uuid.uuid4().hex diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_edges.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_edges.sql new file mode 100644 index 0000000000..edd8ed7fb1 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_edges.sql @@ -0,0 +1,57 @@ +MODEL ( + description """ + One row per check with numeric edges: DIV0 and DIV0NULL by literal zeros and NULL, TRUNC of + FLOAT ratios that sit just below a whole number of cents, FLOAT values as text (pi among them), + infinity and NaN words in other scripts read by TRY_TO_DOUBLE, and LAG with offset 0 and IGNORE + NULLS. A running window frame without ORDER BY is left out: Snowflake rejects it + """, + tags [edge_case], + columns ( + check_id (type VARCHAR, nullable false, audits [not_null, unique]), + group_code (type VARCHAR), + one_over_zero (type NUMBER(38, 6)), + amount_over_zero (type NUMBER(38, 6)), + amount_over_scaled_zero (type NUMBER(38, 6)), + amount_over_minus_zero (type NUMBER(38, 6)), + amount_over_null (type NUMBER(38, 6)), + null_over_zero (type NUMBER(38, 6)), + ratio_truncated_2 (type FLOAT), + ratio_truncated_3 (type FLOAT), + ratio_text (type VARCHAR), + pi_text (type VARCHAR), + key_text_as_double (type FLOAT), + same_amount_lag (type NUMBER(18, 2)), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.group_code, + c.key_text, + c.amount, + c.ratio + FROM __ref("stg_edge_amount_checks") AS c +), + +final AS ( + SELECT + c.check_id, + c.group_code, + DIV0(1, 0)::NUMBER(38, 6) AS one_over_zero, + DIV0(c.amount, 0)::NUMBER(38, 6) AS amount_over_zero, + DIV0(c.amount, 0.00)::NUMBER(38, 6) AS amount_over_scaled_zero, + DIV0(c.amount, -0)::NUMBER(38, 6) AS amount_over_minus_zero, + DIV0NULL(c.amount, NULL)::NUMBER(38, 6) AS amount_over_null, + DIV0(NULL, 0)::NUMBER(38, 6) AS null_over_zero, + TRUNC(c.ratio, 2)::FLOAT AS ratio_truncated_2, + TRUNCATE(c.ratio, 3)::FLOAT AS ratio_truncated_3, + c.ratio::VARCHAR AS ratio_text, + PI()::VARCHAR AS pi_text, + TRY_TO_DOUBLE(c.key_text)::FLOAT AS key_text_as_double, + LAG(c.amount, 0) IGNORE NULLS OVER (ORDER BY c.check_id)::NUMBER(18, 2) AS same_amount_lag + FROM checks AS c +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_statistics.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_statistics.sql new file mode 100644 index 0000000000..f23b88903d --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_amount_statistics.sql @@ -0,0 +1,99 @@ +MODEL ( + description """ + One row per check group with MEDIAN at scales 0 to 15, where the two middle values differ in + their last digits; MODE and COUNT_IF per group; and MODE, MEDIAN and COUNT_IF over no rows, + without GROUP BY and with HAVING but no GROUP BY, which Snowflake answers with one row; MODE of + a number over no rows can come back as 0, not NULL. Tied modes, the pick of a MAX_BY whose keys + all tie and MEDIAN above scale 12, which Snowflake does not compute exactly, are checked by + assertions rather than compared + """, + tags [edge_case], + columns ( + group_code (type VARCHAR, nullable false, audits [not_null, unique]), + median_scale_0 (type NUMBER(38, 3)), + median_scale_2 (type NUMBER(38, 5)), + median_scale_4 (type NUMBER(38, 7)), + median_scale_10 (type NUMBER(38, 13)), + median_scale_11 (type NUMBER(38, 14)), + median_scale_12 (type NUMBER(38, 15)), + median_scale_15 (type NUMBER(38, 18)), + mode_amount (type NUMBER(18, 2)), + paid_count (type NUMBER(38, 0)), + signed_zero_pick (type FLOAT), + mode_over_no_rows (type NUMBER(18, 2)), + median_over_no_rows (type NUMBER(38, 5)), + paid_count_over_no_rows (type NUMBER(38, 0)), + mode_with_having_only (type NUMBER(18, 0)), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.group_code, + c.narrow_key, + c.amount, + c.fine_amount, + c.ratio + FROM __ref("stg_edge_amount_checks") AS c +), + +per_group AS ( + SELECT + c.group_code, + MEDIAN(c.fine_amount::NUMBER(20, 0)) AS median_scale_0, + MEDIAN(c.fine_amount::NUMBER(20, 2)) AS median_scale_2, + MEDIAN(c.fine_amount::NUMBER(20, 4)) AS median_scale_4, + MEDIAN(c.fine_amount::NUMBER(20, 10)) AS median_scale_10, + MEDIAN(c.fine_amount::NUMBER(20, 11)) AS median_scale_11, + MEDIAN(c.fine_amount::NUMBER(20, 12)) AS median_scale_12, + MEDIAN(c.fine_amount) AS median_scale_15, + MODE(c.amount) AS mode_amount, + COUNT_IF(c.amount > 0) AS paid_count, + -- Every key ties, so either row's value is a valid pick; the test checks only that it is zero. + MAX_BY(c.ratio * 0, c.group_code) AS signed_zero_pick + FROM checks AS c + GROUP BY + c.group_code +), + +over_no_rows AS ( + SELECT + MODE(c.amount) AS mode_over_no_rows, + MEDIAN(c.amount) AS median_over_no_rows, + COUNT_IF(c.amount > 0) AS paid_count_over_no_rows + FROM checks AS c + WHERE c.check_id IS NULL +), + +having_only AS ( + SELECT MODE(c.narrow_key) AS mode_with_having_only + FROM checks AS c + WHERE c.check_id IS NULL + HAVING COUNT(*) >= 0 +), + +final AS ( + SELECT + g.group_code, + g.median_scale_0::NUMBER(38, 3) AS median_scale_0, + g.median_scale_2::NUMBER(38, 5) AS median_scale_2, + g.median_scale_4::NUMBER(38, 7) AS median_scale_4, + g.median_scale_10::NUMBER(38, 13) AS median_scale_10, + g.median_scale_11::NUMBER(38, 14) AS median_scale_11, + g.median_scale_12::NUMBER(38, 15) AS median_scale_12, + g.median_scale_15::NUMBER(38, 18) AS median_scale_15, + g.mode_amount::NUMBER(18, 2) AS mode_amount, + g.paid_count::NUMBER(38, 0) AS paid_count, + g.signed_zero_pick::FLOAT AS signed_zero_pick, + n.mode_over_no_rows::NUMBER(18, 2) AS mode_over_no_rows, + n.median_over_no_rows::NUMBER(38, 5) AS median_over_no_rows, + n.paid_count_over_no_rows::NUMBER(38, 0) AS paid_count_over_no_rows, + h.mode_with_having_only::NUMBER(18, 0) AS mode_with_having_only + FROM per_group AS g + CROSS JOIN over_no_rows AS n + CROSS JOIN having_only AS h +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_analysis_patterns.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_analysis_patterns.sql new file mode 100644 index 0000000000..0bed0a7ecf --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_analysis_patterns.sql @@ -0,0 +1,102 @@ +MODEL ( + description """ + One row per basket built from valid Snowflake SQL that name and type checks have reported as + errors: LAST_DAY with a date part as its second argument, an aggregate aliased as the column it + reads, positional columns, columns read by the names Snowflake gives unaliased expressions, and + contracts that declare TEXT(5) and a NUMBER(38,0) fed by COUNT + """, + tags [edge_case], + columns ( + basket_id (type VARCHAR, nullable false, audits [not_null, unique]), + month_end (type DATE), + line_no (type NUMBER(38, 0)), + positional_sku (type VARCHAR), + next_line_no (type NUMBER(38, 0)), + line_no_text (type VARCHAR), + sku_prefix (type TEXT(5)), + line_count (type NUMBER(38, 0)), + ), +); + +WITH lines AS ( + SELECT + l.basket_id, + l.sku, + l.line_no + FROM __ref("stg_edge_basket_lines") AS l +), + +dated AS ( + SELECT MIN(TRY_TO_DATE(d.date_text, 'YYYY-MM-DD')) AS first_date + FROM __ref("stg_edge_date_texts") AS d +), + +own_names AS ( + SELECT + l.basket_id, + MAX(l.line_no) AS line_no + FROM lines AS l + GROUP BY + l.basket_id +), + +positional AS ( + SELECT + $1 AS basket_id, + MIN($2) AS positional_sku + FROM lines + GROUP BY + 1 +), + +unnamed AS ( + -- No aliases: Snowflake names these columns LINE_NO + 1 and LINE_NO::VARCHAR. + SELECT + basket_id, + line_no + 1, + line_no::VARCHAR + FROM lines +), + +unnamed_per_basket AS ( + SELECT + u.basket_id, + MAX(u."LINE_NO + 1") AS next_line_no, + MIN(u."LINE_NO::VARCHAR") AS line_no_text + FROM unnamed AS u + GROUP BY + u.basket_id +), + +counted AS ( + SELECT + l.basket_id, + LEFT(MIN(l.sku), 5)::VARCHAR(5) AS sku_prefix, + COUNT(*) AS line_count + FROM lines AS l + GROUP BY + l.basket_id +), + +final AS ( + SELECT + c.basket_id, + LAST_DAY(d.first_date, month)::DATE AS month_end, + n.line_no, + p.positional_sku::VARCHAR AS positional_sku, + u.next_line_no::NUMBER(38, 0) AS next_line_no, + u.line_no_text::VARCHAR AS line_no_text, + c.sku_prefix, + c.line_count + FROM counted AS c + CROSS JOIN dated AS d + LEFT JOIN own_names AS n + ON c.basket_id = n.basket_id + LEFT JOIN positional AS p + ON c.basket_id = p.basket_id + LEFT JOIN unnamed_per_basket AS u + ON c.basket_id = u.basket_id +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_pairs.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_pairs.sql new file mode 100644 index 0000000000..4c829442fa --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_pairs.sql @@ -0,0 +1,51 @@ +MODEL ( + description """ + One row per distinct pair of tags seen together in a basket, written in both orders. Arrays keep + their element order, so SELECT DISTINCT keeps [bath, gift] and [gift, bath] apart and merges + only pairs repeated in the same order; a NULL tag stays an undefined element + """, + tags [edge_case], + columns ( + tag_pair_text (type VARCHAR, nullable false, audits [not_null, unique]), + tag_pair (type ARRAY), + pair_size (type NUMBER(38, 0)), + ), +); + +WITH lines AS ( + SELECT + l.basket_id, + l.line_no, + l.tag + FROM __ref("stg_edge_basket_lines") AS l +), + +pairs AS ( + SELECT ARRAY_CONSTRUCT(a.tag, b.tag) AS tag_pair + FROM lines AS a + INNER JOIN lines AS b + ON a.basket_id = b.basket_id + AND a.line_no < b.line_no + UNION ALL + SELECT ARRAY_CONSTRUCT(b.tag, a.tag) AS tag_pair + FROM lines AS a + INNER JOIN lines AS b + ON a.basket_id = b.basket_id + AND a.line_no < b.line_no +), + +distinct_pairs AS ( + SELECT DISTINCT p.tag_pair + FROM pairs AS p +), + +final AS ( + SELECT + ARRAY_TO_STRING(d.tag_pair, '|')::VARCHAR AS tag_pair_text, + d.tag_pair::ARRAY AS tag_pair, + ARRAY_SIZE(d.tag_pair)::NUMBER(38, 0) AS pair_size + FROM distinct_pairs AS d +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_sets.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_sets.sql new file mode 100644 index 0000000000..ecf87605cd --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_tag_sets.sql @@ -0,0 +1,73 @@ +MODEL ( + description """ + One row per basket with order-free reads of an unordered ARRAY_AGG of its tags. Snowflake does + not define the element order of an ARRAY_AGG without WITHIN GROUP, so the model outputs only + facts that hold for every order: sizes, membership, overlaps, sorted distinct sets and the + values FLATTEN reads back through a CTE + """, + tags [edge_case], + columns ( + basket_id (type VARCHAR, nullable false, audits [not_null, unique]), + tag_count (type NUMBER(38, 0)), + has_gift (type BOOLEAN), + overlaps_pond_or_garden (type BOOLEAN), + distinct_tag_count (type NUMBER(38, 0)), + distinct_tags (type ARRAY), + flattened_count (type NUMBER(38, 0)), + flattened_tags (type VARCHAR), + ), +); + +WITH lines AS ( + SELECT + l.basket_id, + l.tag + FROM __ref("stg_edge_basket_lines") AS l +), + +baskets AS ( + -- Unordered on purpose: every read below gives the same answer for any element order. + SELECT + l.basket_id, + ARRAY_AGG(l.tag) AS tags + FROM lines AS l + GROUP BY + l.basket_id +), + +flattened AS ( + SELECT + b.basket_id, + f.value::VARCHAR AS tag + FROM baskets AS b, LATERAL FLATTEN(input => b.tags) AS f +), + +flattened_tags AS ( + SELECT + f.basket_id, + COUNT(*) AS flattened_count, + LISTAGG(DISTINCT f.tag, ',') WITHIN GROUP (ORDER BY f.tag) AS flattened_tags + FROM flattened AS f + GROUP BY + f.basket_id +), + +final AS ( + SELECT + b.basket_id, + ARRAY_SIZE(b.tags)::NUMBER(38, 0) AS tag_count, + ARRAY_CONTAINS('gift'::VARIANT, b.tags)::BOOLEAN AS has_gift, + ARRAYS_OVERLAP(b.tags, ARRAY_CONSTRUCT('pond', 'garden'))::BOOLEAN AS overlaps_pond_or_garden, + ARRAY_SIZE( + ARRAY_DISTINCT(ARRAY_CAT(b.tags, ARRAY_CONSTRUCT('gift'))) + )::NUMBER(38, 0) AS distinct_tag_count, + ARRAY_SORT(ARRAY_DISTINCT(ARRAY_CAT(b.tags, ARRAY_CONSTRUCT('gift'))))::ARRAY AS distinct_tags, + COALESCE(t.flattened_count, 0)::NUMBER(38, 0) AS flattened_count, + t.flattened_tags::VARCHAR AS flattened_tags + FROM baskets AS b + LEFT JOIN flattened_tags AS t + ON b.basket_id = t.basket_id +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_top_tags.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_top_tags.sql new file mode 100644 index 0000000000..215a2046c1 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_basket_top_tags.sql @@ -0,0 +1,120 @@ +MODEL ( + description """ + The basket whose unordered tag array sorts last, picked by ORDER BY ... DESC LIMIT 1 written by + position, by name and through IFF, over a UNION ALL, and the first line by a positional ORDER BY + over a constructed array. How Snowflake ranks an ARRAY_AGG depends on its element order, which + it does not define; the fixtures give every basket a different set of first elements, so the + pick is the same for every element order + """, + tags [edge_case], + columns ( + pick_kind (type VARCHAR, nullable false, audits [not_null, unique]), + picked_id (type VARCHAR), + picked_size (type NUMBER(38, 0)), + ), +); + +WITH lines AS ( + SELECT + l.line_id, + l.basket_id, + l.sku, + l.tag + FROM __ref("stg_edge_basket_lines") AS l +), + +baskets AS ( + SELECT + l.basket_id, + ARRAY_AGG(l.tag) AS tags + FROM lines AS l + GROUP BY + l.basket_id +), + +by_position AS ( + SELECT + b.basket_id, + b.tags + FROM baskets AS b + ORDER BY + 2 DESC + LIMIT 1 +), + +by_name AS ( + SELECT b.basket_id + FROM baskets AS b + ORDER BY + b.tags DESC + LIMIT 1 +), + +through_iff AS ( + SELECT + b.basket_id, + IFF(TRUE, b.tags, b.tags) AS tags + FROM baskets AS b + ORDER BY + 2 DESC + LIMIT 1 +), + +over_union AS ( + SELECT + b.basket_id, + b.tags + FROM baskets AS b + UNION ALL + SELECT + 'B-0' AS basket_id, + ARRAY_CONSTRUCT('aaa') AS tags +ORDER BY 2 DESC +LIMIT 1 +), + +constructed AS ( + SELECT + l.line_id, + ARRAY_CONSTRUCT(l.sku) AS skus + FROM lines AS l + ORDER BY + 2, + 1 + LIMIT 1 +), + +final AS ( + SELECT + 'by_position' AS pick_kind, + p.basket_id AS picked_id, + ARRAY_SIZE(p.tags)::NUMBER(38, 0) AS picked_size + FROM by_position AS p + UNION ALL + SELECT + 'by_name' AS pick_kind, + n.basket_id AS picked_id, + NULL::NUMBER(38, 0) AS picked_size + FROM by_name AS n + UNION ALL + SELECT + 'through_iff' AS pick_kind, + i.basket_id AS picked_id, + ARRAY_SIZE(i.tags)::NUMBER(38, 0) AS picked_size + FROM through_iff AS i + UNION ALL + SELECT + 'over_union' AS pick_kind, + u.basket_id AS picked_id, + ARRAY_SIZE(u.tags)::NUMBER(38, 0) AS picked_size + FROM over_union AS u + UNION ALL + SELECT + 'constructed' AS pick_kind, + c.line_id AS picked_id, + ARRAY_SIZE(c.skus)::NUMBER(38, 0) AS picked_size + FROM constructed AS c +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_category_walk.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_category_walk.sql new file mode 100644 index 0000000000..b2091bbc42 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_category_walk.sql @@ -0,0 +1,80 @@ +MODEL ( + description """ + One row per category reached by walking the category tree with START WITH ... CONNECT BY, with + its walk depth (the LEVEL pseudo-column), root name and path. A second walk joins in a relation + with a column named level: there Snowflake reads even the qualified d.level as the walk depth, + and CONNECT_BY_ROOT returns the row's own value instead of its root's. After the walk, a scalar + subquery reads the level column itself. Snowflake fails with an internal error on a scalar + subquery inside a CONNECT BY query, so none is written there + """, + tags [edge_case], + columns ( + category_id (type VARCHAR, nullable false, audits [not_null, unique]), + walk_depth (type NUMBER(38, 0)), + root_name (type VARCHAR), + category_path (type VARCHAR), + joined_level (type NUMBER(38, 0)), + joined_root_name (type VARCHAR), + deepest_shelf (type NUMBER(38, 0)), + ), +); + +WITH links AS ( + SELECT + l.category_id, + l.parent_id, + l.category_name + FROM __ref("stg_edge_category_links") AS l +), + +shelves AS ( + SELECT l.shelf_depth AS level + FROM __ref("stg_edge_category_links") AS l +), + +deepest AS ( + SELECT MAX(s.level) AS level + FROM shelves AS s +), + +walk AS ( + SELECT + k.category_id, + LEVEL AS walk_depth, + CONNECT_BY_ROOT k.category_name AS root_name, + SYS_CONNECT_BY_PATH(k.category_name, ' > ') AS category_path + FROM links AS k +START WITH k.parent_id IS NULL +CONNECT BY k.parent_id = PRIOR k.category_id +), + +joined_walk AS ( + SELECT + k.category_id, + d.level AS joined_level, + CONNECT_BY_ROOT k.category_name AS joined_root_name + FROM links AS k + CROSS JOIN deepest AS d +START WITH k.parent_id IS NULL +CONNECT BY k.parent_id = PRIOR k.category_id +), + +final AS ( + SELECT + w.category_id, + w.walk_depth::NUMBER(38, 0) AS walk_depth, + w.root_name::VARCHAR AS root_name, + w.category_path::VARCHAR AS category_path, + j.joined_level::NUMBER(38, 0) AS joined_level, + j.joined_root_name::VARCHAR AS joined_root_name, + ( + SELECT MAX(level) + FROM shelves + )::NUMBER(38, 0) AS deepest_shelf + FROM walk AS w + INNER JOIN joined_walk AS j + ON w.category_id = j.category_id +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_clock_arithmetic.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_clock_arithmetic.sql new file mode 100644 index 0000000000..8f6521bf7d --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_clock_arithmetic.sql @@ -0,0 +1,126 @@ +MODEL ( + description """ + One row per partner clock reading with time arithmetic at its edges: DATEADD and TIMEADD on a + TIME(3) and the type they return, DATEDIFF across units, TIME(9) values formatted with fewer + fraction digits (Snowflake truncates), FF3 and bare FF on timestamps of precision 3 and 9, a + TIMESTAMP_NTZ(3) and its NTZ(0) cast compared with text holding finer fractions (cast to + TIMESTAMP_NTZ, which Snowflake reads at precision 9), and the types of casts to TIME(3), + TO_TIMESTAMP of a NUMBER(13,3) and a bare TIMESTAMP cast + """, + tags [edge_case], + columns ( + text_id (type VARCHAR, nullable false, audits [not_null, unique]), + time_plus_minutes (type TIME), + time_minus_hours (type TIME), + time_plus_millisecond (type TIME), + time_add_type (type VARCHAR), + time_cast_type (type VARCHAR), + wall_clock_plus_day (type TIMESTAMP_NTZ), + wall_clock_add_type (type VARCHAR), + years_to_2027 (type NUMBER(38, 0)), + months_to_2027 (type NUMBER(38, 0)), + weeks_to_2027 (type NUMBER(38, 0)), + hours_to_2027 (type NUMBER(38, 0)), + milliseconds_to_2027 (type NUMBER(38, 0)), + minutes_to_midnight (type NUMBER(38, 0)), + fine_time_micro_text (type VARCHAR), + fine_time_second_text (type VARCHAR), + fine_time_milli_text (type VARCHAR), + text_clock_ff3 (type VARCHAR), + wall_clock_bare_ff (type VARCHAR), + wall_clock_equals_text (type BOOLEAN), + whole_second_equals_text (type BOOLEAN), + whole_second_before_text (type BOOLEAN), + epoch_millis_type (type VARCHAR), + bare_timestamp_type (type VARCHAR), + ), +); + +WITH clocks AS ( + SELECT + c.text_id, + c.wall_clock, + c.wall_clock_text, + c.local_time, + c.fine_time + FROM __ref("stg_edge_date_texts") AS c +), + +final AS ( + SELECT + c.text_id, + DATEADD('minute', 30, c.local_time)::TIME AS time_plus_minutes, + TIMEADD('hour', -10, c.local_time)::TIME AS time_minus_hours, + DATEADD('millisecond', 1, c.local_time)::TIME AS time_plus_millisecond, + REGEXP_REPLACE( + SYSTEM$TYPEOF(DATEADD('minute', 30, c.local_time)), + '\\[.*\\]$', + '' + )::VARCHAR AS time_add_type, + REGEXP_REPLACE( + SYSTEM$TYPEOF(c.wall_clock::TIME(3)), + '\\[.*\\]$', + '' + )::VARCHAR AS time_cast_type, + DATEADD('day', 1, c.wall_clock) AS wall_clock_plus_day, + REGEXP_REPLACE( + SYSTEM$TYPEOF(DATEADD('day', 1, c.wall_clock)), + '\\[.*\\]$', + '' + )::VARCHAR AS wall_clock_add_type, + DATEDIFF( + 'year', + c.wall_clock, + '2027-01-01 00:00:00'::TIMESTAMP_NTZ + )::NUMBER(38, 0) AS years_to_2027, + DATEDIFF( + 'month', + c.wall_clock, + '2027-01-01 00:00:00'::TIMESTAMP_NTZ + )::NUMBER(38, 0) AS months_to_2027, + DATEDIFF( + 'week', + c.wall_clock, + '2027-01-01 00:00:00'::TIMESTAMP_NTZ + )::NUMBER(38, 0) AS weeks_to_2027, + DATEDIFF( + 'hour', + c.wall_clock, + '2027-01-01 00:00:00'::TIMESTAMP_NTZ + )::NUMBER(38, 0) AS hours_to_2027, + DATEDIFF( + 'millisecond', + c.wall_clock, + '2027-01-01 00:00:00'::TIMESTAMP_NTZ + )::NUMBER(38, 0) AS milliseconds_to_2027, + DATEDIFF( + 'minute', + c.local_time, + '23:59:59'::TIME + )::NUMBER(38, 0) AS minutes_to_midnight, + TO_CHAR(c.fine_time, 'HH24:MI:SS.FF6')::VARCHAR AS fine_time_micro_text, + TO_CHAR(c.fine_time, 'HH24:MI:SS')::VARCHAR AS fine_time_second_text, + TO_CHAR(c.fine_time, 'HH24:MI:SS.FF3')::VARCHAR AS fine_time_milli_text, + TO_CHAR( + TRY_TO_TIMESTAMP_NTZ(c.wall_clock_text), + 'YYYY-MM-DD HH24:MI:SS.FF3' + )::VARCHAR AS text_clock_ff3, + TO_CHAR(c.wall_clock, 'HH24:MI:SS.FF')::VARCHAR AS wall_clock_bare_ff, + c.wall_clock = c.wall_clock_text::TIMESTAMP_NTZ AS wall_clock_equals_text, + c.wall_clock::TIMESTAMP_NTZ(0) = c.wall_clock_text::TIMESTAMP_NTZ AS whole_second_equals_text, + c.wall_clock::TIMESTAMP_NTZ(0) < c.wall_clock_text::TIMESTAMP_NTZ AS whole_second_before_text, + REGEXP_REPLACE( + SYSTEM$TYPEOF(TO_TIMESTAMP(1772359200.123::NUMBER(13, 3))), + '\\[.*\\]$', + '' + )::VARCHAR AS epoch_millis_type, + REGEXP_REPLACE( + SYSTEM$TYPEOF('2026-03-01 10:15:00'::TIMESTAMP), + '\\[.*\\]$', + '' + )::VARCHAR AS bare_timestamp_type + FROM clocks AS c +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_grouped_windows.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_grouped_windows.sql new file mode 100644 index 0000000000..7bda5d16bb --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_grouped_windows.sql @@ -0,0 +1,78 @@ +MODEL ( + description """ + One row per check group and one grand-total row from GROUP BY ROLLUP, whose total row holds a + NULL group code. Aggregates sit inside window functions in the grouped query: a grand total and + a share, a RANK ordered by COUNT(*), and a QUALIFY that keeps the group with the largest SUM + """, + tags [edge_case], + columns ( + group_label (type VARCHAR, nullable false, audits [not_null, unique]), + group_code (type VARCHAR), + group_amount (type NUMBER(38, 2)), + check_count (type NUMBER(38, 0)), + total_amount (type NUMBER(38, 2)), + amount_share (type NUMBER(38, 6)), + size_rank (type NUMBER(38, 0)), + is_largest_group (type BOOLEAN), + ), +); + +WITH checks AS ( + SELECT + c.group_code, + c.amount + FROM __ref("stg_edge_amount_checks") AS c +), + +grouped AS ( + SELECT + c.group_code, + SUM(SUM(c.amount)) OVER () AS total_amount, + DIV0(SUM(c.amount), SUM(SUM(c.amount)) OVER ()) AS amount_share, + RANK() OVER (ORDER BY COUNT(*) DESC, c.group_code) AS size_rank + FROM checks AS c + GROUP BY + c.group_code +), + +largest AS ( + SELECT c.group_code + FROM checks AS c + GROUP BY + c.group_code + QUALIFY ROW_NUMBER() OVER (ORDER BY SUM(c.amount) DESC NULLS LAST, c.group_code) = 1 +), + +rolled_up AS ( + SELECT + c.group_code, + SUM(c.amount) AS group_amount, + COUNT(*) AS check_count + FROM checks AS c + GROUP BY + ROLLUP ( + c.group_code + ) +), + +final AS ( + SELECT + COALESCE(r.group_code, 'ALL')::VARCHAR AS group_label, + r.group_code::VARCHAR AS group_code, + r.group_amount::NUMBER(38, 2) AS group_amount, + r.check_count::NUMBER(38, 0) AS check_count, + g.total_amount::NUMBER(38, 2) AS total_amount, + g.amount_share::NUMBER(38, 6) AS amount_share, + g.size_rank::NUMBER(38, 0) AS size_rank, + ( + l.group_code IS NOT NULL + )::BOOLEAN AS is_largest_group + FROM rolled_up AS r + LEFT JOIN grouped AS g + ON r.group_code = g.group_code + LEFT JOIN largest AS l + ON r.group_code = l.group_code +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_join_keys.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_join_keys.sql new file mode 100644 index 0000000000..ae423cb7cf --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_join_keys.sql @@ -0,0 +1,125 @@ +MODEL ( + description """ + One row per check group matched to a group name by join shapes whose merged keys and outer sides + are easy to trace wrongly: RIGHT JOIN ... USING (the key comes from the right), NATURAL JOIN, a + USING key followed by a later RIGHT JOIN that makes it NULL, and an ASOF JOIN whose unmatched + rows hold NULL on the right + """, + tags [edge_case], + columns ( + row_key (type VARCHAR, nullable false, audits [not_null, unique]), + join_shape (type VARCHAR), + group_code (type VARCHAR), + group_name (type VARCHAR), + threshold_name (type VARCHAR), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.group_code, + c.narrow_key + FROM __ref("stg_edge_amount_checks") AS c +), + +check_groups AS ( + SELECT DISTINCT c.group_code + FROM checks AS c + WHERE c.group_code <> 'G-3' +), + +group_names AS ( + SELECT + v.group_code, + v.group_name + FROM VALUES + ('G-1', 'Mallard desk'), + ('G-2', 'Teal desk'), + ('G-9', 'Eider desk') AS v(group_code, group_name) +), + +desks AS ( + SELECT v.desk_code + FROM VALUES ('G-1'), ('G-7') AS v(desk_code) +), + +thresholds AS ( + SELECT + v.threshold_key, + v.threshold_name + FROM VALUES (10, 'ten or more'), (100, 'a hundred or more') AS v(threshold_key, threshold_name) +), + +right_using AS ( + SELECT + 'right_using' AS join_shape, + group_code, + n.group_name, + NULL AS threshold_name + FROM check_groups AS g + RIGHT JOIN group_names AS n + USING (group_code) +), + +natural_joined AS ( + SELECT + 'natural' AS join_shape, + n.group_code, + n.group_name, + NULL AS threshold_name + FROM check_groups AS g + NATURAL JOIN group_names AS n +), + +later_outer AS ( + SELECT + 'using_then_right' AS join_shape, + group_code, + n.group_name, + d.desk_code AS threshold_name + FROM check_groups AS g + INNER JOIN group_names AS n + USING (group_code) + RIGHT JOIN desks AS d + ON n.group_code = d.desk_code +), + +asof_matched AS ( + SELECT + 'asof:' || c.check_id AS join_shape, + c.group_code, + NULL AS group_name, + t.threshold_name + FROM checks AS c + ASOF JOIN thresholds AS t MATCH_CONDITION (c.narrow_key >= t.threshold_key) +), + +all_rows AS ( + SELECT * + FROM right_using + UNION ALL BY NAME + SELECT * + FROM natural_joined + UNION ALL BY NAME + SELECT * + FROM later_outer + UNION ALL BY NAME + SELECT * + FROM asof_matched +), + +final AS ( + SELECT + ( + a.join_shape || ':' || COALESCE(a.group_code, '-') || ':' || COALESCE(a.threshold_name, '-') + )::VARCHAR AS row_key, + a.join_shape::VARCHAR AS join_shape, + a.group_code::VARCHAR AS group_code, + a.group_name::VARCHAR AS group_name, + a.threshold_name::VARCHAR AS threshold_name + FROM all_rows AS a +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_json_numbers.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_json_numbers.sql new file mode 100644 index 0000000000..6473251cac --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_json_numbers.sql @@ -0,0 +1,48 @@ +MODEL ( + description """ + One row per check whose JSON number text is parsed with TRY_PARSE_JSON: whole numbers written + with zero fractions, invalid numbers with two decimal points, a 38-digit number ending in .0, a + one followed by forty zero decimals, negative zero and exponents, with the parsed kind and the + JSON text Snowflake writes back + """, + tags [edge_case], + columns ( + check_id (type VARCHAR, nullable false, audits [not_null, unique]), + json_text (type VARCHAR), + parsed_kind (type VARCHAR), + parsed_text (type VARCHAR), + amount_kind (type VARCHAR), + amount_text (type VARCHAR), + is_valid_json (type BOOLEAN), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.json_text + FROM __ref("stg_edge_amount_checks") AS c +), + +parsed AS ( + SELECT + c.check_id, + c.json_text, + TRY_PARSE_JSON(c.json_text) AS document + FROM checks AS c +), + +final AS ( + SELECT + p.check_id, + p.json_text, + TYPEOF(p.document)::VARCHAR AS parsed_kind, + TO_JSON(p.document)::VARCHAR AS parsed_text, + TYPEOF(p.document:amount)::VARCHAR AS amount_kind, + p.document:amount::VARCHAR AS amount_text, + CHECK_JSON(p.json_text) IS NULL AS is_valid_json + FROM parsed AS p +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_key_widths.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_key_widths.sql new file mode 100644 index 0000000000..71ba3b0458 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_key_widths.sql @@ -0,0 +1,144 @@ +MODEL ( + description """ + One row per check pair matched by JOIN ... USING between a NUMBER(18,0) key and a NUMBER(38,0) + key, inner, left and full, including a 23-digit key, with the types Snowflake gives the merged + key and each side's qualified key; and a text key joined to a number key, where Snowflake + compares the text as a number + """, + tags [edge_case], + columns ( + pair_id (type VARCHAR, nullable false, audits [not_null, unique]), + join_kind (type VARCHAR), + check_key_text (type VARCHAR), + narrow_check_id (type VARCHAR), + wide_check_id (type VARCHAR), + merged_key_type (type VARCHAR), + narrow_side_type (type VARCHAR), + wide_side_type (type VARCHAR), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.narrow_key, + c.wide_key, + c.key_text + FROM __ref("stg_edge_amount_checks") AS c +), + +narrow AS ( + SELECT + c.check_id AS narrow_check_id, + c.narrow_key AS check_key + FROM checks AS c + WHERE c.narrow_key IS NOT NULL +), + +wide AS ( + SELECT + c.check_id AS wide_check_id, + c.wide_key AS check_key + FROM checks AS c + WHERE c.wide_key IS NOT NULL +), + +text_keys AS ( + SELECT + c.check_id AS text_check_id, + c.key_text AS check_key + FROM checks AS c + WHERE TRY_TO_NUMBER(c.key_text) IS NOT NULL +), + +inner_pairs AS ( + SELECT + 'inner' AS join_kind, + check_key::VARCHAR AS check_key_text, + n.narrow_check_id, + w.wide_check_id, + SYSTEM$TYPEOF(check_key) AS merged_key_type, + SYSTEM$TYPEOF(n.check_key) AS narrow_side_type, + SYSTEM$TYPEOF(w.check_key) AS wide_side_type + FROM narrow AS n + INNER JOIN wide AS w + USING (check_key) +), + +left_pairs AS ( + SELECT + 'left' AS join_kind, + check_key::VARCHAR AS check_key_text, + n.narrow_check_id, + w.wide_check_id, + SYSTEM$TYPEOF(check_key) AS merged_key_type, + SYSTEM$TYPEOF(n.check_key) AS narrow_side_type, + SYSTEM$TYPEOF(w.check_key) AS wide_side_type + FROM narrow AS n + LEFT JOIN wide AS w + USING (check_key) +), + +full_pairs AS ( + SELECT + 'full' AS join_kind, + check_key::VARCHAR AS check_key_text, + n.narrow_check_id, + w.wide_check_id, + SYSTEM$TYPEOF(check_key) AS merged_key_type, + SYSTEM$TYPEOF(n.check_key) AS narrow_side_type, + SYSTEM$TYPEOF(w.check_key) AS wide_side_type + FROM narrow AS n + FULL JOIN wide AS w + USING (check_key) +), + +text_pairs AS ( + SELECT + 'text' AS join_kind, + check_key::VARCHAR AS check_key_text, + t.text_check_id AS narrow_check_id, + w.wide_check_id, + SYSTEM$TYPEOF(check_key) AS merged_key_type, + SYSTEM$TYPEOF(t.check_key) AS narrow_side_type, + SYSTEM$TYPEOF(w.check_key) AS wide_side_type + FROM text_keys AS t + INNER JOIN wide AS w + USING (check_key) +), + +all_pairs AS ( + SELECT * + FROM inner_pairs + UNION ALL BY NAME + SELECT * + FROM left_pairs + UNION ALL BY NAME + SELECT * + FROM full_pairs + UNION ALL BY NAME + SELECT * + FROM text_pairs +), + +final AS ( + SELECT + ( + a.join_kind + || ':' + || COALESCE(a.narrow_check_id, '-') + || ':' + || COALESCE(a.wide_check_id, '-') + )::VARCHAR AS pair_id, + a.join_kind::VARCHAR AS join_kind, + a.check_key_text::VARCHAR AS check_key_text, + a.narrow_check_id::VARCHAR AS narrow_check_id, + a.wide_check_id::VARCHAR AS wide_check_id, + REGEXP_REPLACE(a.merged_key_type, '\\[.*\\]$', '')::VARCHAR AS merged_key_type, + REGEXP_REPLACE(a.narrow_side_type, '\\[.*\\]$', '')::VARCHAR AS narrow_side_type, + REGEXP_REPLACE(a.wide_side_type, '\\[.*\\]$', '')::VARCHAR AS wide_side_type + FROM all_pairs AS a +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_note_patterns.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_note_patterns.sql new file mode 100644 index 0000000000..f51010d93d --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_note_patterns.sql @@ -0,0 +1,72 @@ +MODEL ( + description """ + One row per support ticket note searched with regular expressions at their edges: a lazy + quantifier followed by another ?, a bracket class that opens with ] or ^], lazy one-or-more in a + position search, positions of patterns that match empty text, . against a carriage return in + multi-line mode, and case-insensitive matches of the micro sign and final sigma. Also ILIKE with + a long s, alone and as the pattern beside an ESCAPE character, name similarity of accented names + in both cases, Base64 text with line feeds inside groups and padding, and the word at a position + numbered by a window ordered by a VARIANT + """, + tags [edge_case], + columns ( + note_id (type VARCHAR, nullable false, audits [not_null, unique]), + lazy_then_optional_count (type NUMBER(38, 0)), + lazy_then_optional_match (type VARCHAR), + optional_twice_match (type VARCHAR), + bracket_close_match (type VARCHAR), + bracket_negated_match (type VARCHAR), + lazy_plus_position (type NUMBER(38, 0)), + end_anchor_position (type NUMBER(38, 0)), + star_position (type NUMBER(38, 0)), + multiline_dot_match (type VARCHAR), + matches_micro_sign (type BOOLEAN), + matches_sigma (type BOOLEAN), + ilike_s_prefix (type BOOLEAN), + ilike_long_s_escaped (type BOOLEAN), + name_similarity (type NUMBER(38, 0)), + decoded_tags (type VARCHAR), + ranked_word (type VARCHAR), + ), +); + +WITH notes AS ( + SELECT + n.note_id, + n.body, + n.tags_text + FROM __ref("stg_edge_ticket_notes") AS n +), + +final AS ( + SELECT + n.note_id, + REGEXP_COUNT(n.body, '(xa)+??x')::NUMBER(38, 0) AS lazy_then_optional_count, + REGEXP_SUBSTR(n.body, '(xa)+??x')::VARCHAR AS lazy_then_optional_match, + REGEXP_SUBSTR(n.body, 'xa???')::VARCHAR AS optional_twice_match, + REGEXP_SUBSTR(n.body, '[]x+?]+')::VARCHAR AS bracket_close_match, + REGEXP_SUBSTR(n.body, '[^]+?]+')::VARCHAR AS bracket_negated_match, + REGEXP_INSTR(n.body, 'ba+?')::NUMBER(38, 0) AS lazy_plus_position, + REGEXP_INSTR(n.body, '$')::NUMBER(38, 0) AS end_anchor_position, + REGEXP_INSTR(n.body, 'a*')::NUMBER(38, 0) AS star_position, + REGEXP_SUBSTR(n.body, '^a.$', 1, 1, 'm')::VARCHAR AS multiline_dot_match, + REGEXP_LIKE(n.body, '.*\u00b5.*', 'i')::BOOLEAN AS matches_micro_sign, + REGEXP_LIKE(n.body, '.*\u03c3.*', 'i')::BOOLEAN AS matches_sigma, + ( + n.body ILIKE 's%' + )::BOOLEAN AS ilike_s_prefix, + ( + n.body ILIKE '\u017f%' ESCAPE 's' + )::BOOLEAN AS ilike_long_s_escaped, + JAROWINKLER_SIMILARITY(n.body, n.tags_text)::NUMBER(38, 0) AS name_similarity, + TRY_BASE64_DECODE_STRING(n.tags_text)::VARCHAR AS decoded_tags, + SPLIT_PART( + n.body, + ' ', + ROW_NUMBER() OVER (ORDER BY TO_VARIANT(n.note_id)) + )::VARCHAR AS ranked_word + FROM notes AS n +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_null_arithmetic.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_null_arithmetic.sql new file mode 100644 index 0000000000..6b9e6d3453 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_null_arithmetic.sql @@ -0,0 +1,188 @@ +MODEL ( + description """ + One row per check with NULL arithmetic and literal typing: NULL written first, FLOAT / NULL, + NUMBER(38,2) * NULL, a NULL column from a CTE, and the types Snowflake gives them, decimal and + exponent literals, COUNT, ROW_NUMBER, % and NULLIF, a column NULL in every branch of a UNION, + and a UNION with a decimal literal branch. Also CASE without ELSE and DECODE mixing text with + numbers, where no branch reads the text that is not a number + """, + tags [edge_case], + columns ( + check_id (type VARCHAR, nullable false, audits [not_null, unique]), + null_plus_amount (type NUMBER(38, 2)), + null_times_amount (type NUMBER(38, 2)), + null_over_amount (type NUMBER(38, 6)), + ratio_over_null (type FLOAT), + wide_amount_times_null (type NUMBER(38, 2)), + missing_times_amount (type NUMBER(38, 2)), + null_times_amount_or_zero (type VARCHAR), + null_over_amount_type (type VARCHAR), + ratio_over_null_type (type VARCHAR), + wide_amount_times_null_type (type VARCHAR), + missing_times_amount_type (type VARCHAR), + amount_or_decimal (type VARCHAR), + amount_or_decimal_type (type VARCHAR), + exponent_type (type VARCHAR), + small_exponent_product_type (type VARCHAR), + remainder_type (type VARCHAR), + key_remainder_type (type VARCHAR), + nullif_type (type VARCHAR), + ilike_null_type (type VARCHAR), + row_number_type (type VARCHAR), + count_type (type VARCHAR), + union_decimal_type (type VARCHAR), + all_null_union_type (type VARCHAR), + case_without_else (type VARCHAR), + decoded (type VARCHAR), + ), +); + +WITH checks AS ( + SELECT + c.check_id, + c.narrow_key, + c.key_text, + c.amount, + c.ratio + FROM __ref("stg_edge_amount_checks") AS c +), + +missing AS ( + SELECT NULL AS missing_amount +), + +counted AS ( + SELECT COUNT(c.check_id) AS check_count + FROM checks AS c +), + +-- SYSTEM$TYPEOF of an aggregate does not make its query aggregate, so each type is read from a +-- column of a finished CTE. +count_typed AS ( + SELECT SYSTEM$TYPEOF(k.check_count) AS count_type + FROM counted AS k +), + +with_decimal AS ( + SELECT c.amount AS mixed_amount + FROM checks AS c + UNION ALL + SELECT 1.5 AS mixed_amount +), + +union_types AS ( + SELECT SYSTEM$TYPEOF(w.mixed_amount) AS mixed_type + FROM with_decimal AS w +), + +-- Every row has the same type. MAX over SYSTEM$TYPEOF fails in Snowflake with an internal error +-- once the result is joined on, so one row is taken instead. +union_typed AS ( + SELECT u.mixed_type AS union_decimal_type + FROM union_types AS u + ORDER BY + u.mixed_type + LIMIT 1 +), + +all_null AS ( + SELECT NULL AS note + UNION ALL + SELECT NULL AS note +), + +all_null_types AS ( + SELECT SYSTEM$TYPEOF(a.note) AS note_type + FROM all_null AS a +), + +all_null_typed AS ( + SELECT n.note_type AS all_null_union_type + FROM all_null_types AS n + ORDER BY + n.note_type + LIMIT 1 +), + +computed AS ( + SELECT + c.check_id, + NULL + c.amount AS null_plus_amount, + NULL * c.amount AS null_times_amount, + NULL / c.amount AS null_over_amount, + c.ratio / NULL AS ratio_over_null, + c.amount::NUMBER(38, 2) * NULL AS wide_amount_times_null, + m.missing_amount * c.amount AS missing_times_amount, + COALESCE(NULL * c.amount, 0)::VARCHAR AS null_times_amount_or_zero, + SYSTEM$TYPEOF(NULL / c.amount) AS null_over_amount_type, + SYSTEM$TYPEOF(c.ratio / NULL) AS ratio_over_null_type, + SYSTEM$TYPEOF(c.amount::NUMBER(38, 2) * NULL) AS wide_amount_times_null_type, + SYSTEM$TYPEOF(m.missing_amount * c.amount) AS missing_times_amount_type, + COALESCE(c.amount, 1.5)::VARCHAR AS amount_or_decimal, + SYSTEM$TYPEOF(COALESCE(c.amount, 1.5)) AS amount_or_decimal_type, + SYSTEM$TYPEOF(1e5) AS exponent_type, + SYSTEM$TYPEOF(c.amount * 1e-12) AS small_exponent_product_type, + SYSTEM$TYPEOF(7 % 3) AS remainder_type, + SYSTEM$TYPEOF(c.narrow_key % 3) AS key_remainder_type, + SYSTEM$TYPEOF(NULLIF(c.amount, 1)) AS nullif_type, + SYSTEM$TYPEOF(c.key_text ILIKE NULL) AS ilike_null_type, + SYSTEM$TYPEOF(ROW_NUMBER() OVER (ORDER BY c.check_id)) AS row_number_type, + CASE + WHEN c.narrow_key = 7 + THEN c.narrow_key + WHEN c.narrow_key = 12 + THEN c.key_text + END AS case_without_else, + DECODE(c.narrow_key, 7, c.amount, 12, c.key_text) AS decoded + FROM checks AS c + CROSS JOIN missing AS m +), + +final AS ( + SELECT + c.check_id, + c.null_plus_amount::NUMBER(38, 2) AS null_plus_amount, + c.null_times_amount::NUMBER(38, 2) AS null_times_amount, + c.null_over_amount::NUMBER(38, 6) AS null_over_amount, + c.ratio_over_null::FLOAT AS ratio_over_null, + c.wide_amount_times_null::NUMBER(38, 2) AS wide_amount_times_null, + c.missing_times_amount::NUMBER(38, 2) AS missing_times_amount, + c.null_times_amount_or_zero, + REGEXP_REPLACE(c.null_over_amount_type, '\\[.*\\]$', '')::VARCHAR AS null_over_amount_type, + REGEXP_REPLACE(c.ratio_over_null_type, '\\[.*\\]$', '')::VARCHAR AS ratio_over_null_type, + REGEXP_REPLACE( + c.wide_amount_times_null_type, + '\\[.*\\]$', + '' + )::VARCHAR AS wide_amount_times_null_type, + REGEXP_REPLACE( + c.missing_times_amount_type, + '\\[.*\\]$', + '' + )::VARCHAR AS missing_times_amount_type, + c.amount_or_decimal, + REGEXP_REPLACE(c.amount_or_decimal_type, '\\[.*\\]$', '')::VARCHAR AS amount_or_decimal_type, + REGEXP_REPLACE(c.exponent_type, '\\[.*\\]$', '')::VARCHAR AS exponent_type, + REGEXP_REPLACE( + c.small_exponent_product_type, + '\\[.*\\]$', + '' + )::VARCHAR AS small_exponent_product_type, + REGEXP_REPLACE(c.remainder_type, '\\[.*\\]$', '')::VARCHAR AS remainder_type, + REGEXP_REPLACE(c.key_remainder_type, '\\[.*\\]$', '')::VARCHAR AS key_remainder_type, + REGEXP_REPLACE(c.nullif_type, '\\[.*\\]$', '')::VARCHAR AS nullif_type, + REGEXP_REPLACE(c.ilike_null_type, '\\[.*\\]$', '')::VARCHAR AS ilike_null_type, + REGEXP_REPLACE(c.row_number_type, '\\[.*\\]$', '')::VARCHAR AS row_number_type, + REGEXP_REPLACE(k.count_type, '\\[.*\\]$', '')::VARCHAR AS count_type, + REGEXP_REPLACE(u.union_decimal_type, '\\[.*\\]$', '')::VARCHAR AS union_decimal_type, + REGEXP_REPLACE(n.all_null_union_type, '\\[.*\\]$', '')::VARCHAR AS all_null_union_type, + c.case_without_else::VARCHAR AS case_without_else, + c.decoded::VARCHAR AS decoded + FROM computed AS c + CROSS JOIN count_typed AS k + CROSS JOIN union_typed AS u + CROSS JOIN all_null_typed AS n +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_payload_paths.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_payload_paths.sql new file mode 100644 index 0000000000..1c5e1ff66c --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_payload_paths.sql @@ -0,0 +1,52 @@ +MODEL ( + description """ + One row per element of a partner attribute document flattened with RECURSIVE, with the PATH + Snowflake writes for keys holding $, -, @, [, a space, a non-ASCII letter or nothing unusual, + and for array indexes inside them + """, + tags [edge_case], + columns ( + element_id (type VARCHAR, nullable false, audits [not_null, unique]), + value_id (type VARCHAR), + element_path (type VARCHAR), + element_key (type VARCHAR), + element_index (type NUMBER(38, 0)), + element_kind (type VARCHAR), + ), +); + +WITH documents AS ( + SELECT + v.value_id, + v.attribute_value + FROM __ref("stg_edge_variant_values") AS v + WHERE + IS_OBJECT(v.attribute_value) + OR IS_ARRAY(v.attribute_value) +), + +elements AS ( + SELECT + d.value_id, + f.path, + f.key, + f.index, + f.value + FROM documents AS d, LATERAL FLATTEN(input => d.attribute_value, recursive => TRUE) AS f +), + +final AS ( + SELECT + ( + e.value_id || ':' || e.path + )::VARCHAR AS element_id, + e.value_id, + e.path::VARCHAR AS element_path, + e.key::VARCHAR AS element_key, + e.index::NUMBER(38, 0) AS element_index, + TYPEOF(e.value)::VARCHAR AS element_kind + FROM elements AS e +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_python_values.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_python_values.sql new file mode 100644 index 0000000000..cb97cda47e --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_python_values.sql @@ -0,0 +1,49 @@ +MODEL ( + description """ + One row per distinct basket tag, read as the kind of payload value a project Python function + hands to Snowflake: Python None in a VARIANT (JSON null, not SQL NULL), zoned and naive + datetimes, Decimals written with an exponent and with a trailing zero, an integer past 2^53, a + nested document, and an integer past 2^53 returned as FLOAT + """, + tags [edge_case], + columns ( + kind (type VARCHAR, nullable false, audits [not_null, unique]), + payload (type VARIANT), + payload_is_sql_null (type BOOLEAN), + payload_is_json_null (type BOOLEAN), + payload_kind (type VARCHAR), + payload_text (type VARCHAR), + amount_as_float (type FLOAT), + ), +); + +WITH kinds AS ( + SELECT DISTINCT LOWER(TRIM(l.tag)) AS kind + FROM __ref("stg_edge_basket_lines") AS l + WHERE l.tag IS NOT NULL +), + +values_out AS ( + SELECT + k.kind, + __udf("edge_payload_value")(k.kind) AS payload, + __udf("edge_float_value")(9007199254740993) AS amount_as_float + FROM kinds AS k +), + +final AS ( + SELECT + v.kind::VARCHAR AS kind, + v.payload, + ( + v.payload IS NULL + )::BOOLEAN AS payload_is_sql_null, + IS_NULL_VALUE(v.payload)::BOOLEAN AS payload_is_json_null, + TYPEOF(v.payload)::VARCHAR AS payload_kind, + TO_JSON(v.payload)::VARCHAR AS payload_text, + v.amount_as_float::FLOAT AS amount_as_float + FROM values_out AS v +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_recursive_counts.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_recursive_counts.sql new file mode 100644 index 0000000000..fa81a85dc2 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_recursive_counts.sql @@ -0,0 +1,57 @@ +MODEL ( + description """ + One row per category reached by a recursive CTE written without the RECURSIVE keyword, which + Snowflake does not require. Its columns shift by one step per iteration, so the grandparent's + name reaches a category only after two iterations + """, + tags [edge_case], + columns ( + category_id (type VARCHAR, nullable false, audits [not_null, unique]), + tree_depth (type NUMBER(38, 0)), + parent_name (type VARCHAR), + grandparent_name (type VARCHAR), + ), +); + +WITH links AS ( + SELECT + l.category_id, + l.parent_id, + l.category_name + FROM __ref("stg_edge_category_links") AS l +), + +tree(category_id, tree_depth, own_name, parent_name, grandparent_name) AS ( + SELECT + l.category_id, + 1::NUMBER(38, 0) AS tree_depth, + l.category_name::VARCHAR AS own_name, + NULL::VARCHAR AS parent_name, + NULL::VARCHAR AS grandparent_name + FROM links AS l + WHERE l.parent_id IS NULL + UNION ALL + SELECT + l.category_id, + ( + t.tree_depth + 1 + )::NUMBER(38, 0) AS tree_depth, + l.category_name::VARCHAR AS own_name, + t.own_name::VARCHAR AS parent_name, + t.parent_name::VARCHAR AS grandparent_name + FROM links AS l + INNER JOIN tree AS t + ON l.parent_id = t.category_id +), + +final AS ( + SELECT + t.category_id, + t.tree_depth::NUMBER(38, 0) AS tree_depth, + t.parent_name::VARCHAR AS parent_name, + t.grandparent_name::VARCHAR AS grandparent_name + FROM tree AS t +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_row_deciders.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_row_deciders.sql new file mode 100644 index 0000000000..315b424bf9 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_row_deciders.sql @@ -0,0 +1,199 @@ +MODEL ( + description """ + One row per basket picked by a clause whose columns decide which rows come back but are never + output: WHERE, QUALIFY, GROUP BY 1 with HAVING, GROUP BY ALL, ORDER BY under LIMIT, EXISTS (as a + filter and as an output), a FLATTEN whose input decides how many rows come out, GROUP BY ROLLUP + by position, and EXCEPT ordered by position under LIMIT. Lineage that follows only the output + columns misses every one of these + """, + tags [edge_case], + columns ( + decider_row (type VARCHAR, nullable false, audits [not_null, unique]), + decider (type VARCHAR), + basket_id (type VARCHAR), + detail (type VARCHAR), + ), +); + +WITH lines AS ( + SELECT + l.line_id, + l.basket_id, + l.line_no, + l.sku, + l.tag + FROM __ref("stg_edge_basket_lines") AS l +), + +baskets AS ( + SELECT DISTINCT l.basket_id + FROM lines AS l +), + +filtered AS ( + SELECT + 'where' AS decider, + l.basket_id, + l.line_id AS detail + FROM lines AS l + WHERE l.tag = 'bath' +), + +first_lines AS ( + SELECT + 'qualify' AS decider, + l.basket_id, + l.line_id AS detail + FROM lines AS l + QUALIFY ROW_NUMBER() OVER (PARTITION BY l.basket_id ORDER BY l.line_no, l.line_id) = 1 +), + +busy AS ( + SELECT + 'group_by_position' AS decider, + l.basket_id, + NULL AS detail + FROM lines AS l + GROUP BY + 2 + HAVING COUNT(*) > 1 +), + +counted AS ( + SELECT + 'group_by_all' AS decider, + l.basket_id, + COUNT(*)::VARCHAR AS detail + FROM lines AS l + GROUP BY ALL +), + +lowest_sku AS ( + SELECT + 'order_by_limit' AS decider, + l.basket_id, + l.line_id AS detail + FROM lines AS l + ORDER BY + l.sku, + l.line_id + LIMIT 1 +), + +with_gift AS ( + SELECT + 'exists_filter' AS decider, + b.basket_id, + NULL AS detail + FROM baskets AS b + WHERE + EXISTS( + SELECT 1 + FROM lines AS g + WHERE + g.basket_id = b.basket_id + AND g.tag = 'gift' + ) +), + +gift_flags AS ( + SELECT + 'exists_output' AS decider, + b.basket_id, + ( + EXISTS( + SELECT 1 + FROM lines AS g + WHERE + g.basket_id = b.basket_id + AND g.tag = 'gift' + ) + )::VARCHAR AS detail + FROM baskets AS b +), + +sku_parts AS ( + SELECT + 'flatten_input' AS decider, + l.basket_id, + l.line_id || '#' || f.index::VARCHAR AS detail + FROM lines AS l, LATERAL FLATTEN(input => SPLIT(l.sku, '-')) AS f +), + +rolled AS ( + SELECT + 'rollup_position' AS decider, + l.basket_id, + GROUPING(l.tag)::VARCHAR || ':' || COALESCE(l.tag, '~') || '=' || COUNT(*)::VARCHAR AS detail + FROM lines AS l + GROUP BY + ROLLUP ( + 2, + l.tag + ) +), + +without_pond AS ( + SELECT l.basket_id + FROM lines AS l + EXCEPT + SELECT l.basket_id + FROM lines AS l + WHERE l.tag = 'pond' +ORDER BY 1 DESC +LIMIT 1 +), + +remaining AS ( + SELECT + 'except_limit' AS decider, + w.basket_id, + NULL AS detail + FROM without_pond AS w +), + +all_rows AS ( + SELECT * + FROM filtered + UNION ALL BY NAME + SELECT * + FROM first_lines + UNION ALL BY NAME + SELECT * + FROM busy + UNION ALL BY NAME + SELECT * + FROM counted + UNION ALL BY NAME + SELECT * + FROM lowest_sku + UNION ALL BY NAME + SELECT * + FROM with_gift + UNION ALL BY NAME + SELECT * + FROM gift_flags + UNION ALL BY NAME + SELECT * + FROM sku_parts + UNION ALL BY NAME + SELECT * + FROM rolled + UNION ALL BY NAME + SELECT * + FROM remaining +), + +final AS ( + SELECT + ( + a.decider || ':' || COALESCE(a.basket_id, '-') || ':' || COALESCE(a.detail, '-') + )::VARCHAR AS decider_row, + a.decider::VARCHAR AS decider, + a.basket_id::VARCHAR AS basket_id, + a.detail::VARCHAR AS detail + FROM all_rows AS a +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_text_dates.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_text_dates.sql new file mode 100644 index 0000000000..2a0a9b7c8f --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_text_dates.sql @@ -0,0 +1,43 @@ +MODEL ( + description """ + One row per partner date text read with TRY_TO_DATE under numeric and month-name formats: + unpadded days and months, two-digit years, ISO T and Z separators, trailing letters, a long s in + a month name, mixed separators and runs of spaces + """, + tags [edge_case], + columns ( + text_id (type VARCHAR, nullable false, audits [not_null, unique]), + date_text (type VARCHAR), + day_month_dots (type DATE), + iso_date (type DATE), + day_month_short_year (type DATE), + month_day_slashes (type DATE), + day_month_name (type DATE), + month_name_first (type DATE), + auto_date (type DATE), + ), +); + +WITH texts AS ( + SELECT + t.text_id, + t.date_text + FROM __ref("stg_edge_date_texts") AS t +), + +final AS ( + SELECT + t.text_id, + t.date_text, + TRY_TO_DATE(t.date_text, 'DD.MM.YYYY') AS day_month_dots, + TRY_TO_DATE(t.date_text, 'YYYY-MM-DD') AS iso_date, + TRY_TO_DATE(t.date_text, 'DD/MM/YY') AS day_month_short_year, + TRY_TO_DATE(t.date_text, 'MM/DD/YYYY') AS month_day_slashes, + TRY_TO_DATE(t.date_text, 'DD MON YYYY') AS day_month_name, + TRY_TO_DATE(t.date_text, 'MMMM DD YYYY') AS month_name_first, + TRY_TO_DATE(t.date_text) AS auto_date + FROM texts AS t +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_ticket_tokens.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_ticket_tokens.sql new file mode 100644 index 0000000000..73c7972ba8 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_ticket_tokens.sql @@ -0,0 +1,43 @@ +MODEL ( + description """ + One row per basket line with values from volatile project Python functions: a ticket token from + the line id, from a constant and from no argument at all, and half a ticket number through DIV0, + beside a column and text that only look like the function's name. Snowflake runs a Python + function once per call and row, so the values change on every run; the test checks only facts + that hold for any values, such as every token being distinct + """, + tags [edge_case, known_divergence], + columns ( + line_id (type VARCHAR, nullable false, audits [not_null, unique]), + line_token (type VARCHAR), + constant_seed_token (type VARCHAR), + next_ticket (type NUMBER(38, 0)), + half_ticket (type NUMBER(38, 6)), + edge_next_ticket_source (type VARCHAR), + look_alike_text (type VARCHAR), + tag_label (type VARCHAR), + ), +); + +WITH lines AS ( + SELECT + l.line_id, + l.tag + FROM __ref("stg_edge_basket_lines") AS l +), + +final AS ( + SELECT + l.line_id, + __udf("edge_ticket_token")(l.line_id)::VARCHAR AS line_token, + __udf("edge_ticket_token")('x')::VARCHAR AS constant_seed_token, + __udf("edge_next_ticket")()::NUMBER(38, 0) AS next_ticket, + DIV0(__udf("edge_next_ticket")(), 2)::NUMBER(38, 6) AS half_ticket, + l.line_id AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket'::VARCHAR AS look_alike_text, + __udf("edge_tag_label")(l.tag)::VARCHAR AS tag_label + FROM lines AS l +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_extremes.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_extremes.sql new file mode 100644 index 0000000000..43b8b98a19 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_extremes.sql @@ -0,0 +1,55 @@ +MODEL ( + description """ + One row per partner attribute value with VARIANT orderings and array reads: ARRAY_MAX, ARRAY_MIN + and ARRAY_REVERSE over arrays mixing numbers, strings, booleans, JSON nulls and integers past + 2^53; the largest value of each group by window MAX, across kinds and non-ASCII strings; + ARRAY_REMOVE of a JSON null; JSON nulls in ARRAY_DISTINCT and ARRAYS_OVERLAP; and the value + looked up in an array of decimals written with trailing zeros + """, + tags [edge_case], + columns ( + value_id (type VARCHAR, nullable false, audits [not_null, unique]), + largest_element (type VARIANT), + smallest_element (type VARIANT), + reversed_elements (type ARRAY), + group_largest (type VARIANT), + without_json_nulls (type ARRAY), + distinct_element_count (type NUMBER(38, 0)), + overlaps_json_null (type BOOLEAN), + in_decimal_array (type BOOLEAN), + decimal_array_position (type NUMBER(38, 0)), + ), +); + +WITH attribute_values AS ( + SELECT + v.value_id, + v.group_code, + v.attribute_value, + AS_ARRAY(v.attribute_value) AS elements + FROM __ref("stg_edge_variant_values") AS v +), + +final AS ( + SELECT + a.value_id, + ARRAY_MAX(a.elements)::VARIANT AS largest_element, + ARRAY_MIN(a.elements)::VARIANT AS smallest_element, + ARRAY_REVERSE(a.elements)::ARRAY AS reversed_elements, + MAX(a.attribute_value) OVER (PARTITION BY a.group_code) AS group_largest, + ARRAY_REMOVE(a.elements, PARSE_JSON('null'))::ARRAY AS without_json_nulls, + ARRAY_SIZE(ARRAY_DISTINCT(a.elements))::NUMBER(38, 0) AS distinct_element_count, + ARRAYS_OVERLAP(a.elements, ARRAY_CONSTRUCT(PARSE_JSON('null')))::BOOLEAN AS overlaps_json_null, + ARRAY_CONTAINS( + a.attribute_value, + ARRAY_CONSTRUCT(-7.00, 1.00, 'a') + )::BOOLEAN AS in_decimal_array, + ARRAY_POSITION( + a.attribute_value, + ARRAY_CONSTRUCT(1.50, 1.00, 0.00) + )::NUMBER(38, 0) AS decimal_array_position + FROM attribute_values AS a +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_matches.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_matches.sql new file mode 100644 index 0000000000..53eecce497 --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_variant_matches.sql @@ -0,0 +1,157 @@ +MODEL ( + description """ + One row per pair of partner attribute values in a group, compared with = and EQUAL_NULL: 1 + against "1" and 1.0, JSON null against SQL NULL, objects with reordered keys, and a DATE or + TIMESTAMP held in a VARIANT against the same text, which Snowflake keeps typed. Each value also + carries its group's MIN_BY and MAX_BY VARIANT picks, how many rows share its value in a window + partition, the group's distinct values by SELECT DISTINCT (counted with COUNT, which skips JSON + null) and ARRAY_AGG(DISTINCT), a scalar cast of the value, and an OBJECT_CONSTRUCT star + excluding a quoted lower-case column. Picks and objects that can hold typed values are output as + their kind and JSON text, since JSON text alone would lose the type + """, + tags [edge_case], + columns ( + pair_id (type VARCHAR, nullable false, audits [not_null, unique]), + left_value (type VARIANT), + right_value (type VARIANT), + values_equal (type BOOLEAN), + values_equal_null (type BOOLEAN), + left_partition_size (type NUMBER(38, 0)), + group_first_value (type VARCHAR), + group_last_value (type VARCHAR), + group_distinct_rows (type NUMBER(38, 0)), + group_distinct_elements (type NUMBER(38, 0)), + left_scalar_text (type VARCHAR), + left_object_without_note (type VARCHAR), + ), +); + +WITH loaded AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key, + v.attribute_value, + v.value_text + FROM __ref("stg_edge_variant_values") AS v +), + +typed AS ( + -- A DATE and a TIMESTAMP stay typed inside a VARIANT, unlike the text they are written as. + SELECT + l.value_id || '-DATE' AS value_id, + l.group_code, + l.sort_key + 100 AS sort_key, + TO_VARIANT(TRY_TO_DATE(l.attribute_value::VARCHAR)) AS attribute_value, + l.value_text + FROM loaded AS l + WHERE + IS_VARCHAR(l.attribute_value) + AND TRY_TO_DATE(l.attribute_value::VARCHAR) IS NOT NULL + UNION ALL + SELECT + l.value_id || '-TIMESTAMP' AS value_id, + l.group_code, + l.sort_key + 200 AS sort_key, + TO_VARIANT(TRY_TO_TIMESTAMP_NTZ(l.attribute_value::VARCHAR)) AS attribute_value, + l.value_text + FROM loaded AS l + WHERE + IS_VARCHAR(l.attribute_value) + AND TRY_TO_DATE(l.attribute_value::VARCHAR) IS NOT NULL +), + +all_values AS ( + SELECT * + FROM loaded + UNION ALL BY NAME + SELECT * + FROM typed +), + +with_note AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key, + v.attribute_value, + v.value_text AS "note" + FROM all_values AS v +), + +partitioned AS ( + SELECT + a.value_id, + a.group_code, + a.sort_key, + a.attribute_value, + COUNT(*) OVER (PARTITION BY a.group_code, a.attribute_value) AS partition_size, + OBJECT_CONSTRUCT(* EXCLUDE ("note")) AS object_without_note + FROM with_note AS a +), + +group_picks AS ( + SELECT + a.group_code, + MIN_BY(a.attribute_value, a.sort_key) AS group_first_value, + MAX_BY(a.attribute_value, a.sort_key) AS group_last_value, + ARRAY_SIZE(ARRAY_AGG(DISTINCT a.attribute_value)) AS group_distinct_elements + FROM all_values AS a + GROUP BY + a.group_code +), + +distinct_values AS ( + SELECT DISTINCT + a.group_code, + a.attribute_value + FROM all_values AS a +), + +distinct_counts AS ( + SELECT + d.group_code, + COUNT(d.attribute_value) AS group_distinct_rows + FROM distinct_values AS d + GROUP BY + d.group_code +), + +final AS ( + SELECT + ( + l.value_id || '~' || r.value_id + )::VARCHAR AS pair_id, + l.attribute_value AS left_value, + r.attribute_value AS right_value, + ( + l.attribute_value = r.attribute_value + )::BOOLEAN AS values_equal, + EQUAL_NULL(l.attribute_value, r.attribute_value)::BOOLEAN AS values_equal_null, + l.partition_size::NUMBER(38, 0) AS left_partition_size, + ( + TYPEOF(g.group_first_value) || ' ' || TO_JSON(g.group_first_value) + )::VARCHAR AS group_first_value, + ( + TYPEOF(g.group_last_value) || ' ' || TO_JSON(g.group_last_value) + )::VARCHAR AS group_last_value, + c.group_distinct_rows::NUMBER(38, 0) AS group_distinct_rows, + g.group_distinct_elements::NUMBER(38, 0) AS group_distinct_elements, + IFF( + IS_OBJECT(l.attribute_value) OR IS_ARRAY(l.attribute_value), + NULL, + l.attribute_value + )::VARCHAR AS left_scalar_text, + TO_JSON(l.object_without_note)::VARCHAR AS left_object_without_note + FROM partitioned AS l + INNER JOIN partitioned AS r + ON l.group_code = r.group_code + AND l.sort_key < r.sort_key + INNER JOIN group_picks AS g + ON l.group_code = g.group_code + INNER JOIN distinct_counts AS c + ON l.group_code = c.group_code +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_zoned_texts.sql b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_zoned_texts.sql new file mode 100644 index 0000000000..3367ace75b --- /dev/null +++ b/benchmarks/duckshop/project/models/intermediate/edge_cases/int_edge_zoned_texts.sql @@ -0,0 +1,84 @@ +MODEL ( + description """ + One row per zoned partner timestamp text read as TIMESTAMP_TZ and TIMESTAMP_LTZ, with T, Z, + +00:00, +0000, +09:00 and -05:30 spellings, read back as the instant at UTC, its date, year and + day start, and compared with wall-clock text, a TIMESTAMP_NTZ and a DATE. Also the date of the + first zoned value by window and the UTC hour of a wall-clock time just before 1970. + CONVERT_TIMEZONE with a lower-case 'utc' is left out: Snowflake rejects it as an unknown time + zone. TIMESTAMP_LTZ results depend on the session time zone, which the edge target pins to UTC + """, + tags [edge_case, known_divergence], + columns ( + text_id (type VARCHAR, nullable false, audits [not_null, unique]), + zoned_text (type VARCHAR), + zoned (type TIMESTAMP_TZ), + local_zoned (type TIMESTAMP_LTZ), + zoned_utc (type TIMESTAMP_NTZ), + local_zoned_utc (type TIMESTAMP_NTZ), + zoned_date (type DATE), + local_zoned_date (type DATE), + zoned_year (type NUMBER(38, 0)), + local_zoned_year (type NUMBER(38, 0)), + zoned_day_start (type TIMESTAMP_TZ), + local_zoned_day_start (type TIMESTAMP_LTZ), + zoned_equals_wall_text (type BOOLEAN), + local_zoned_equals_wall_text (type BOOLEAN), + zoned_after_wall_clock (type BOOLEAN), + local_zoned_after_wall_clock (type BOOLEAN), + zoned_from_next_day (type BOOLEAN), + local_zoned_from_next_day (type BOOLEAN), + first_zoned_date (type DATE), + wall_clock_utc_hour (type NUMBER(38, 0)), + ), +); + +WITH texts AS ( + SELECT + t.text_id, + t.zoned_text, + t.wall_clock, + t.wall_clock_text + FROM __ref("stg_edge_date_texts") AS t +), + +parsed AS ( + SELECT + t.text_id, + t.zoned_text, + t.wall_clock, + t.wall_clock_text, + TRY_TO_TIMESTAMP_TZ(t.zoned_text) AS zoned, + TRY_TO_TIMESTAMP_LTZ(t.zoned_text) AS local_zoned + FROM texts AS t +), + +final AS ( + SELECT + p.text_id, + p.zoned_text, + p.zoned, + p.local_zoned, + CONVERT_TIMEZONE('UTC', p.zoned)::TIMESTAMP_NTZ AS zoned_utc, + CONVERT_TIMEZONE('UTC', p.local_zoned)::TIMESTAMP_NTZ AS local_zoned_utc, + TO_DATE(p.zoned) AS zoned_date, + TO_DATE(p.local_zoned) AS local_zoned_date, + YEAR(p.zoned)::NUMBER(38, 0) AS zoned_year, + YEAR(p.local_zoned)::NUMBER(38, 0) AS local_zoned_year, + DATE_TRUNC('day', p.zoned) AS zoned_day_start, + DATE_TRUNC('day', p.local_zoned) AS local_zoned_day_start, + p.zoned = p.wall_clock_text::TIMESTAMP_TZ AS zoned_equals_wall_text, + p.local_zoned = p.wall_clock_text::TIMESTAMP_LTZ AS local_zoned_equals_wall_text, + p.zoned > p.wall_clock AS zoned_after_wall_clock, + p.local_zoned > p.wall_clock AS local_zoned_after_wall_clock, + p.zoned >= '2026-03-06'::DATE AS zoned_from_next_day, + p.local_zoned >= '2026-03-06'::DATE AS local_zoned_from_next_day, + FIRST_VALUE(p.zoned) OVER ( + ORDER BY p.text_id + ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING + )::DATE AS first_zoned_date, + DATE_PART(hour, CONVERT_TIMEZONE('UTC', p.wall_clock))::NUMBER(38, 0) AS wall_clock_utc_hour + FROM parsed AS p +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_amount_checks.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_amount_checks.sql new file mode 100644 index 0000000000..a96f28ae2e --- /dev/null +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_amount_checks.sql @@ -0,0 +1,40 @@ +MODEL ( + description """ + One row per reconciliation check, keyed by a NUMBER(18,0), a NUMBER(38,0) and a text key, with + amounts at scales 2 and 15, a FLOAT ratio and raw JSON number text + """, + tags [edge_case], + columns ( + check_id (type VARCHAR, nullable false, audits [not_null, unique]), + group_code (type VARCHAR), + narrow_key (type NUMBER(18, 0)), + wide_key (type NUMBER(38, 0)), + key_text (type VARCHAR), + amount (type NUMBER(18, 2)), + fine_amount (type NUMBER(20, 15)), + ratio (type FLOAT), + json_text (type VARCHAR), + ), +); + +WITH src AS ( + SELECT * + FROM __source("edge__amount_checks") +), + +final AS ( + SELECT + s.check_id, + s.group_code, + s.narrow_key, + s.wide_key, + s.key_text, + s.amount, + s.fine_amount, + s.ratio, + s.json_text + FROM src AS s +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_basket_lines.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_basket_lines.sql new file mode 100644 index 0000000000..d95d4b3553 --- /dev/null +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_basket_lines.sql @@ -0,0 +1,32 @@ +MODEL ( + description """ + One row per basket line with its tag in lower case, the input to the edge models that collect + tags into arrays whose element order Snowflake does not define + """, + tags [edge_case], + columns ( + line_id (type VARCHAR, nullable false, audits [not_null, unique]), + basket_id (type VARCHAR), + line_no (type NUMBER(38, 0)), + sku (type VARCHAR), + tag (type VARCHAR), + ), +); + +WITH src AS ( + SELECT * + FROM __source("edge__basket_lines") +), + +final AS ( + SELECT + s.line_id, + s.basket_id, + s.line_no, + s.sku, + LOWER(TRIM(s.tag))::VARCHAR AS tag + FROM src AS s +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_category_links.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_category_links.sql new file mode 100644 index 0000000000..fd304cdf2a --- /dev/null +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_category_links.sql @@ -0,0 +1,30 @@ +MODEL ( + description """ + One row per catalogue category link, with the shelf depth each category is displayed at, for + walking the category tree with CONNECT BY and a recursive CTE + """, + tags [edge_case], + columns ( + category_id (type VARCHAR, nullable false, audits [not_null, unique]), + parent_id (type VARCHAR), + category_name (type VARCHAR), + shelf_depth (type NUMBER(38, 0)), + ), +); + +WITH src AS ( + SELECT * + FROM __source("edge__category_links") +), + +final AS ( + SELECT + s.category_id, + s.parent_id, + s.category_name, + s.shelf_depth + FROM src AS s +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_date_texts.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_date_texts.sql new file mode 100644 index 0000000000..03fcc0100c --- /dev/null +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_date_texts.sql @@ -0,0 +1,37 @@ +MODEL ( + description """ + One row per partner date and time text, with the clock columns cast to fixed precisions + (TIMESTAMP_NTZ(3), TIME(3) and TIME(9)) so later models can read them as text, zoned times and + times of day + """, + tags [edge_case], + columns ( + text_id (type VARCHAR, nullable false, audits [not_null, unique]), + date_text (type VARCHAR), + zoned_text (type VARCHAR), + wall_clock (type TIMESTAMP_NTZ(3)), + wall_clock_text (type VARCHAR), + local_time (type TIME(3)), + fine_time (type TIME(9)), + ), +); + +WITH src AS ( + SELECT * + FROM __source("edge__date_texts") +), + +final AS ( + SELECT + s.text_id, + s.date_text, + s.zoned_text, + s.wall_clock::TIMESTAMP_NTZ(3) AS wall_clock, + s.wall_clock_text, + s.local_time::TIME(3) AS local_time, + s.fine_time::TIME(9) AS fine_time + FROM src AS s +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_ticket_notes.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_ticket_notes.sql index 6fac313799..bd9fd1d85d 100644 --- a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_ticket_notes.sql +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_ticket_notes.sql @@ -3,7 +3,7 @@ MODEL ( One row per support ticket note with order references and reasons found by regular expressions, written with escaped and dollar-quoted patterns, a SKU normalised by a macro after an escaped quote, campaign and discount codes matched by pattern lists and escapes, and SKUs split at edge - positions + positions; the note text and tags are passed on as written """, tags [edge_case], columns ( @@ -29,6 +29,8 @@ MODEL ( sku_from_zero (type VARCHAR), sku_last_two (type VARCHAR), sku_before_start (type VARCHAR), + body (type VARCHAR), + tags_text (type VARCHAR), ), ); @@ -67,7 +69,9 @@ final AS ( SPLIT_PART(s.sku, '', 1)::VARCHAR AS sku_part_no_delimiter, SUBSTR(s.sku, 0, 3)::VARCHAR AS sku_from_zero, SUBSTR(s.sku, -2)::VARCHAR AS sku_last_two, - SUBSTR(s.sku, -10, 3)::VARCHAR AS sku_before_start + SUBSTR(s.sku, -10, 3)::VARCHAR AS sku_before_start, + s.body, + s.tags_text FROM src AS s ) diff --git a/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_variant_values.sql b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_variant_values.sql new file mode 100644 index 0000000000..6699eaf16c --- /dev/null +++ b/benchmarks/duckshop/project/models/staging/edge_cases/stg_edge_variant_values.sql @@ -0,0 +1,32 @@ +MODEL ( + description """ + One row per partner attribute value held as VARIANT, never SQL NULL (a missing value arrives as + JSON null), with the raw text beside it + """, + tags [edge_case], + columns ( + value_id (type VARCHAR, nullable false, audits [not_null, unique]), + group_code (type VARCHAR), + sort_key (type NUMBER(38, 0)), + attribute_value (type VARIANT, nullable false, audits [not_null]), + value_text (type VARCHAR), + ), +); + +WITH src AS ( + SELECT * + FROM __source("edge__variant_values") +), + +final AS ( + SELECT + s.value_id, + s.group_code, + s.sort_key, + COALESCE(s.attribute_value, PARSE_JSON('null')) AS attribute_value, + s.value_text + FROM src AS s +) + +SELECT * +FROM final diff --git a/benchmarks/duckshop/project/python/loaders/_edge_cases.py b/benchmarks/duckshop/project/python/loaders/_edge_cases.py index b555839004..0c42460042 100644 --- a/benchmarks/duckshop/project/python/loaders/_edge_cases.py +++ b/benchmarks/duckshop/project/python/loaders/_edge_cases.py @@ -7,7 +7,7 @@ from __future__ import annotations -from datetime import datetime +from datetime import datetime, time from decimal import Decimal Rows = list[dict[str, object]] @@ -490,6 +490,220 @@ def partner_listings() -> Rows: ] +def basket_lines() -> Rows: + lines = [ + # basket, line, sku, tag: B-1 repeats a tag, B-3 holds one line, B-4 has a NULL tag + ("B-1", 1, "MALLARD-01", "bath"), + ("B-1", 2, "TEAL-02", "gift"), + ("B-1", 3, "EIDER-03", "bath"), + ("B-2", 1, "PINTAIL-04", "garden"), + ("B-2", 2, "MALLARD-01", "bath"), + ("B-3", 1, "MERGANSER-05", "pond"), + ("B-4", 1, "TEAL-02", None), + ("B-4", 2, "EIDER-03", "gift"), + ] + return [ + { + "line_id": f"BL-{index + 1:02d}", + "basket_id": basket, + "line_no": line_no, + "sku": sku, + "tag": tag, + "_loaded_at": _at("2026-03-06 00:00:00"), + } + for index, (basket, line_no, sku, tag) in enumerate(lines) + ] + + +def date_texts() -> Rows: + texts = [ + # date text, zoned text, wall clock, wall clock text, local time, fine time + ( + "5.3.2026", + "2026-03-05T23:30:00Z", + "2026-03-05 23:30:00.123", + "2026-03-05 23:30:00.1239", + time(23, 59, 59, 999000), + "23:59:59.9999999", + ), + ( + "2026-3-5", + "2026-03-05 23:30:00+00:00", + "2026-03-05 23:30:00", + "2026-03-05 23:30:00.9", + time(0, 0, 0), + "12:00:00.0000005", + ), + ( + "05/03/26", + "2026-03-05 23:30:00 +0000", + "2026-03-05 23:30:00.120", + "2026-03-05 23:30:00.12", + time(12, 30, 15, 250000), + "00:00:00.000000001", + ), + ( + "2026-03-05T10:00", + "2026-03-06T08:30:00+09:00", + "1969-12-31 23:59:59.999", + "1969-12-31 23:59:59.999", + time(23, 30), + "23:59:59.999999999", + ), + ( + "2026-03-05Z", + "2026-03-05T18:00:00-05:30", + "2026-12-31 23:59:59.999", + "2026-12-31 23:59:59.999000", + time(6, 0, 0, 1000), + "06:00:00.0000001", + ), + ( + "05 Auguſt 2026", + "2026-12-31T23:30:00-05:30", + "2026-03-29 01:30:00", + "2026-03-29 01:30", + None, + None, + ), + ("5 Mar 2026", None, None, None, None, None), + ] + return [ + { + "text_id": f"DT-{index + 1:02d}", + "date_text": date_text, + "zoned_text": zoned, + "wall_clock": None if wall is None else _at(wall), + "wall_clock_text": wall_text, + "local_time": local, + "fine_time": fine, + "_loaded_at": _at("2026-03-06 00:00:00"), + } + for index, (date_text, zoned, wall, wall_text, local, fine) in enumerate(texts) + ] + + +def amount_checks() -> Rows: + checks = [ + # group, narrow key, wide key, key text, amount, fine amount, ratio, JSON text + ( + "G-1", + 7, + 7, + "01", + Decimal("10.00"), + Decimal("0.123456789012345"), + 0.29, + '{"amount": 2.0, "qty": 1.00}', + ), + ( + "G-1", + 12, + 12345678901234567890123, + "1", + Decimal("-0.50"), + Decimal("0.123456789012344"), + 1.005, + "1.0.5", + ), + ( + "G-1", + None, + 12, + "12", + Decimal("2.50"), + Decimal("0.500000000000001"), + 2.675, + '{"a": 1.00.25}', + ), + ( + "G-2", + 123456789012345678, + 123456789012345678, + " 7", + Decimal("0.00"), + Decimal("1.000000000000000"), + -155.5, + "[1.0.5, 2.0]", + ), + ("G-2", 99, None, None, None, None, None, "12345678901234567890123456789012345678.0"), + ( + "G-3", + 5, + 5, + "n/a", + Decimal("999999999999.99"), + Decimal("0.000000000000001"), + 0.57, + "1." + "0" * 40, + ), + ] + return [ + { + "check_id": f"AC-{index + 1:02d}", + "group_code": group, + "narrow_key": narrow, + "wide_key": wide, + "key_text": key_text, + "amount": amount, + "fine_amount": fine, + "ratio": ratio, + "json_text": json_text, + "_loaded_at": _at("2026-03-06 00:00:00"), + } + for index, (group, narrow, wide, key_text, amount, fine, ratio, json_text) in enumerate( + checks + ) + ] + + +def variant_values() -> Rows: + values = [ + # group, sort key, JSON document + ("V-1", 1, "1"), + ("V-1", 2, '"1"'), + ("V-1", 3, "1.0"), + ("V-1", 4, "null"), + ("V-2", 1, '{"a": 1, "b": 2}'), + ("V-2", 2, '{"b": 2, "a": 1}'), + ("V-2", 3, '"2026-03-01"'), + ("V-3", 1, '[1, "a", 2, true, null, 12345678901234567890]'), + ("V-3", 2, '["a", "B", "b", "\\u00e9", "z"]'), + ("V-3", 3, '{"a$b": 1, "a-b": {"@x": 2}, "a[b": [3], "\\u00e9": 4, "plain key": 5}'), + ] + return [ + { + "value_id": f"VV-{index + 1:02d}", + "group_code": group, + "sort_key": sort_key, + "attribute_value": document, + "value_text": document, + "_loaded_at": _at("2026-03-06 00:00:00"), + } + for index, (group, sort_key, document) in enumerate(values) + ] + + +def category_links() -> Rows: + links = [ + ("CAT-1", None, "Ducks", 1), + ("CAT-2", "CAT-1", "Rubber ducks", 2), + ("CAT-3", "CAT-2", "Collectible series", 3), + ("CAT-4", "CAT-1", "Decoys", 2), + ("CAT-5", None, "Pond supplies", 1), + ] + return [ + { + "category_id": category, + "parent_id": parent, + "category_name": name, + "shelf_depth": depth, + "_loaded_at": _at("2026-03-06 00:00:00"), + } + for category, parent, name, depth in links + ] + + DELIVERY_BATCH_VAR = "edge_delivery_batch" """Project variable naming the last delivery-update load to include; "2" (all loads) by default.""" @@ -543,4 +757,9 @@ def delivery_updates(batch: int = 2) -> Rows: "edge__ticket_notes": ticket_notes(), "edge__partner_listings": partner_listings(), "edge__delivery_updates": delivery_updates(), + "edge__basket_lines": basket_lines(), + "edge__date_texts": date_texts(), + "edge__amount_checks": amount_checks(), + "edge__variant_values": variant_values(), + "edge__category_links": category_links(), } diff --git a/benchmarks/duckshop/project/python/loaders/edge_cases.py b/benchmarks/duckshop/project/python/loaders/edge_cases.py index c00991b8ac..522dd0b37f 100644 --- a/benchmarks/duckshop/project/python/loaders/edge_cases.py +++ b/benchmarks/duckshop/project/python/loaders/edge_cases.py @@ -53,6 +53,31 @@ def edge__partner_listings(ctx: LoaderContext) -> object: return _load(ctx, "edge__partner_listings") +@loader +def edge__basket_lines(ctx: LoaderContext) -> object: + return _load(ctx, "edge__basket_lines") + + +@loader +def edge__date_texts(ctx: LoaderContext) -> object: + return _load(ctx, "edge__date_texts") + + +@loader +def edge__amount_checks(ctx: LoaderContext) -> object: + return _load(ctx, "edge__amount_checks") + + +@loader +def edge__variant_values(ctx: LoaderContext) -> object: + return _load(ctx, "edge__variant_values") + + +@loader +def edge__category_links(ctx: LoaderContext) -> object: + return _load(ctx, "edge__category_links") + + @loader def edge__delivery_updates(ctx: LoaderContext) -> object: batch = int(ctx.vars.get(DELIVERY_BATCH_VAR, 2)) # type: ignore[call-overload] diff --git a/benchmarks/duckshop/project/sources/edge_cases.yml b/benchmarks/duckshop/project/sources/edge_cases.yml index 6b7b5795a1..738e9b0f2a 100644 --- a/benchmarks/duckshop/project/sources/edge_cases.yml +++ b/benchmarks/duckshop/project/sources/edge_cases.yml @@ -271,3 +271,143 @@ sources: nullable: false audits: - not_null + - name: edge__basket_lines + description: >- + Append-only basket lines with a free-form tag per line, collected into arrays whose element + order Snowflake does not define + managed: true + contract: enforced + columns: + - name: line_id + type: VARCHAR + nullable: false + audits: + - not_null + - unique + - name: basket_id + type: VARCHAR + - name: line_no + type: NUMBER(38,0) + - name: sku + type: VARCHAR + - name: tag + type: VARCHAR + - name: _loaded_at + type: TIMESTAMP_NTZ + nullable: false + audits: + - not_null + - name: edge__date_texts + description: >- + Dates, zoned times and clock times as partners write them: unpadded fields, two-digit years, + ISO separators, offsets in several spellings, and times with sub-millisecond digits + managed: true + contract: enforced + columns: + - name: text_id + type: VARCHAR + nullable: false + audits: + - not_null + - unique + - name: date_text + type: VARCHAR + - name: zoned_text + type: VARCHAR + - name: wall_clock + type: TIMESTAMP_NTZ + - name: wall_clock_text + type: VARCHAR + - name: local_time + type: TIME + - name: fine_time + type: TIME + - name: _loaded_at + type: TIMESTAMP_NTZ + nullable: false + audits: + - not_null + - name: edge__amount_checks + description: >- + Reconciliation checks keyed by numbers of two widths and by text, with amounts at several + scales, FLOAT ratios and raw JSON number text + managed: true + contract: enforced + columns: + - name: check_id + type: VARCHAR + nullable: false + audits: + - not_null + - unique + - name: group_code + type: VARCHAR + - name: narrow_key + type: NUMBER(18,0) + - name: wide_key + type: NUMBER(38,0) + - name: key_text + type: VARCHAR + - name: amount + type: NUMBER(18,2) + - name: fine_amount + type: NUMBER(20,15) + - name: ratio + type: FLOAT + - name: json_text + type: VARCHAR + - name: _loaded_at + type: TIMESTAMP_NTZ + nullable: false + audits: + - not_null + - name: edge__variant_values + description: >- + Partner attribute values held as VARIANT, mixing numbers, strings, booleans, JSON nulls, + objects with reordered keys and wide integers, with the raw text beside each + managed: true + contract: enforced + columns: + - name: value_id + type: VARCHAR + nullable: false + audits: + - not_null + - unique + - name: group_code + type: VARCHAR + - name: sort_key + type: NUMBER(38,0) + - name: attribute_value + type: VARIANT + - name: value_text + type: VARCHAR + - name: _loaded_at + type: TIMESTAMP_NTZ + nullable: false + audits: + - not_null + - name: edge__category_links + description: >- + Catalogue category links for walking the category tree, with the shelf depth each category + is displayed at + managed: true + contract: enforced + columns: + - name: category_id + type: VARCHAR + nullable: false + audits: + - not_null + - unique + - name: parent_id + type: VARCHAR + - name: category_name + type: VARCHAR + - name: shelf_depth + type: NUMBER(38,0) + - name: _loaded_at + type: TIMESTAMP_NTZ + nullable: false + audits: + - not_null diff --git a/benchmarks/duckshop/project/sqlbuild_project.toml b/benchmarks/duckshop/project/sqlbuild_project.toml index fdcc6f7f58..103f00c940 100644 --- a/benchmarks/duckshop/project/sqlbuild_project.toml +++ b/benchmarks/duckshop/project/sqlbuild_project.toml @@ -286,4 +286,42 @@ The edge case selects the key columns that FULL OUTER JOIN ... USING merges. The side, so qualifying them with either alias would drop the keys of rows found only on the other side. """ +[[rules.rule_ignores]] +rules = ["SQLCLEAN003", "SQLCLEAN023", "SQLCLEAN027", "SQLCLEAN051"] +paths = ["models/intermediate/edge_cases/int_edge_join_keys.sql"] +reason = """ +The edge cases read the merged key of RIGHT JOIN ... USING and of a USING join followed by an outer +join unqualified, because it belongs to neither side; the side aliases are then unused. An ASOF +JOIN's condition is its MATCH_CONDITION, so it has no ON clause. The NATURAL JOIN is the case +under test. +""" + +[[rules.rule_ignores]] +rules = ["SQLCLEAN018"] +paths = [ + "models/intermediate/edge_cases/int_edge_grouped_windows.sql", + "models/intermediate/edge_cases/int_edge_null_arithmetic.sql", +] +reason = """ +The ranking windows order by the group code of a grouped query and by the check id, both unique +where they are ranked, which Rules cannot prove through a GROUP BY or a CTE. +""" + +[[rules.rule_ignores]] +rules = ["SQLCLEAN022"] +paths = ["models/intermediate/edge_cases/int_edge_analysis_patterns.sql"] +reason = """ +The edge case leaves two calculated columns unaliased on purpose, and reads them by the names +Snowflake gives them. +""" + +[[rules.rule_ignores]] +rules = ["SQLCLEAN027", "SQLCLEAN028"] +paths = ["models/intermediate/edge_cases/int_edge_category_walk.sql"] +reason = """ +LEVEL and CONNECT_BY_ROOT belong to the walk, not to a relation, so they cannot be qualified, and +the edge case reads a column named level from another relation unqualified inside a scalar +subquery on purpose. +""" + # <<< edge-case area diff --git a/benchmarks/duckshop/project/tests/unit/functions/python/test_edge_tag_label__labels_missing_tags.sql b/benchmarks/duckshop/project/tests/unit/functions/python/test_edge_tag_label__labels_missing_tags.sql new file mode 100644 index 0000000000..927b7c2226 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/functions/python/test_edge_tag_label__labels_missing_tags.sql @@ -0,0 +1,25 @@ +TEST (mode udf, name "edge_tag_label__labels_missing_tags"); + +WITH input_values AS ( + SELECT v.tag + FROM VALUES (' bath '), ('GIFT'), (''), (NULL) AS v(tag) +), + +__udf_actual__ AS ( + SELECT + i.tag, + __udf("edge_tag_label")(i.tag) AS tag_label + FROM input_values AS i +), + +-- Not declared STRICT, so Snowflake calls the handler for NULL and returns its label. +__udf_expected__ AS ( + SELECT + v.tag::VARCHAR AS tag, + v.tag_label::VARCHAR AS tag_label + FROM VALUES + (' bath ', 'Bath'), + ('GIFT', 'Gift'), + ('', 'Untagged'), + (NULL, 'Untagged') AS v(tag, tag_label) +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_edges.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_edges.sql new file mode 100644 index 0000000000..feb7d5d802 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_edges.sql @@ -0,0 +1,119 @@ +TEST (name "int_edge_amount_edges__divides_by_zeros_and_truncates_float_ratios"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', NULL, NULL, '\u0130nf', 10.00, NULL, 0.29, NULL), + ('AC-02', 'G-1', NULL, NULL, 'inf', NULL, NULL, 1.005, NULL), + ('AC-03', 'G-1', NULL, NULL, 'NaN', 2.50, NULL, 2.675, NULL), + ('AC-04', 'G-2', NULL, NULL, '-Infinity', -0.50, NULL, -155.5, NULL), + ('AC-05', 'G-2', NULL, NULL, 'n/a', 0.00, NULL, 0.57, NULL), + ('AC-06', 'G-2', NULL, NULL, '1e3', 4.00, NULL, 3.0, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_amount_edges AS ( + SELECT + 'AC-01' AS check_id, + 'G-1' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_scaled_zero, + 0.000000::NUMBER(38, 6) AS amount_over_minus_zero, + 0.000000::NUMBER(38, 6) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '0.28'::FLOAT AS ratio_truncated_2, + '0.29'::FLOAT AS ratio_truncated_3, + '0.29' AS ratio_text, + '3.141592654' AS pi_text, + CAST(NULL AS FLOAT) AS key_text_as_double, + 10.00::NUMBER(18, 2) AS same_amount_lag + UNION ALL + SELECT + 'AC-02' AS check_id, + 'G-1' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + CAST(NULL AS NUMBER(38, 6)) AS amount_over_zero, + CAST(NULL AS NUMBER(38, 6)) AS amount_over_scaled_zero, + CAST(NULL AS NUMBER(38, 6)) AS amount_over_minus_zero, + CAST(NULL AS NUMBER(38, 6)) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '1.0'::FLOAT AS ratio_truncated_2, + '1.004'::FLOAT AS ratio_truncated_3, + '1.005' AS ratio_text, + '3.141592654' AS pi_text, + 'inf'::FLOAT AS key_text_as_double, + CAST(NULL AS NUMBER(18, 2)) AS same_amount_lag + UNION ALL + SELECT + 'AC-03' AS check_id, + 'G-1' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_scaled_zero, + 0.000000::NUMBER(38, 6) AS amount_over_minus_zero, + 0.000000::NUMBER(38, 6) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '2.67'::FLOAT AS ratio_truncated_2, + '2.675'::FLOAT AS ratio_truncated_3, + '2.675' AS ratio_text, + '3.141592654' AS pi_text, + 'nan'::FLOAT AS key_text_as_double, + 2.50::NUMBER(18, 2) AS same_amount_lag + UNION ALL + SELECT + 'AC-04' AS check_id, + 'G-2' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_scaled_zero, + 0.000000::NUMBER(38, 6) AS amount_over_minus_zero, + 0.000000::NUMBER(38, 6) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '-155.5'::FLOAT AS ratio_truncated_2, + '-155.5'::FLOAT AS ratio_truncated_3, + '-155.5' AS ratio_text, + '3.141592654' AS pi_text, + '-inf'::FLOAT AS key_text_as_double, + -0.50::NUMBER(18, 2) AS same_amount_lag + UNION ALL + SELECT + 'AC-05' AS check_id, + 'G-2' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_scaled_zero, + 0.000000::NUMBER(38, 6) AS amount_over_minus_zero, + 0.000000::NUMBER(38, 6) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '0.56'::FLOAT AS ratio_truncated_2, + '0.57'::FLOAT AS ratio_truncated_3, + '0.57' AS ratio_text, + '3.141592654' AS pi_text, + CAST(NULL AS FLOAT) AS key_text_as_double, + 0.00::NUMBER(18, 2) AS same_amount_lag + UNION ALL + SELECT + 'AC-06' AS check_id, + 'G-2' AS group_code, + 0.000000::NUMBER(38, 6) AS one_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_zero, + 0.000000::NUMBER(38, 6) AS amount_over_scaled_zero, + 0.000000::NUMBER(38, 6) AS amount_over_minus_zero, + 0.000000::NUMBER(38, 6) AS amount_over_null, + CAST(NULL AS NUMBER(38, 6)) AS null_over_zero, + '3.0'::FLOAT AS ratio_truncated_2, + '3.0'::FLOAT AS ratio_truncated_3, + '3' AS ratio_text, + '3.141592654' AS pi_text, + '1000.0'::FLOAT AS key_text_as_double, + 4.00::NUMBER(18, 2) AS same_amount_lag +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_statistics.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_statistics.sql new file mode 100644 index 0000000000..5c980da579 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_amount_statistics.sql @@ -0,0 +1,93 @@ +TEST (name "int_edge_amount_statistics__takes_medians_at_every_scale_and_aggregates_over_no_rows"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', 7, NULL, NULL, 10.00, 0.123456789012345, -155.5, NULL), + ('AC-02', 'G-1', 7, NULL, NULL, 10.00, 0.123456789012344, 0.29, NULL), + ('AC-03', 'G-2', 12, NULL, NULL, NULL, 1.000000000000001, 2.675, NULL), + ('AC-04', 'G-3', 5, NULL, NULL, 0.00, 0.000000000000001, 0.57, NULL), + ('AC-05', 'G-3', 5, NULL, NULL, -0.50, 0.000000000000002, -1.0, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_amount_statistics AS ( + SELECT + 'G-1' AS group_code, + 0.000::NUMBER(38, 3) AS median_scale_0, + 0.12000::NUMBER(38, 5) AS median_scale_2, + 0.1235000::NUMBER(38, 7) AS median_scale_4, + 0.1234567890000::NUMBER(38, 13) AS median_scale_10, + 0.12345678901000::NUMBER(38, 14) AS median_scale_11, + 0.123456789012000::NUMBER(38, 15) AS median_scale_12, + 2 AS paid_count, + CAST(NULL AS NUMBER(18, 2)) AS mode_over_no_rows, + CAST(NULL AS NUMBER(38, 5)) AS median_over_no_rows, + CAST(NULL AS NUMBER(38, 0)) AS paid_count_over_no_rows, + 0 AS mode_with_having_only + UNION ALL + SELECT + 'G-2' AS group_code, + 1.000::NUMBER(38, 3) AS median_scale_0, + 1.00000::NUMBER(38, 5) AS median_scale_2, + 1.0000000::NUMBER(38, 7) AS median_scale_4, + 1.0000000000000::NUMBER(38, 13) AS median_scale_10, + 1.00000000000000::NUMBER(38, 14) AS median_scale_11, + 1.000000000000000::NUMBER(38, 15) AS median_scale_12, + 0 AS paid_count, + CAST(NULL AS NUMBER(18, 2)) AS mode_over_no_rows, + CAST(NULL AS NUMBER(38, 5)) AS median_over_no_rows, + CAST(NULL AS NUMBER(38, 0)) AS paid_count_over_no_rows, + 0 AS mode_with_having_only + UNION ALL + SELECT + 'G-3' AS group_code, + 0.000::NUMBER(38, 3) AS median_scale_0, + 0.00000::NUMBER(38, 5) AS median_scale_2, + 0.0000000::NUMBER(38, 7) AS median_scale_4, + 0.0000000000000::NUMBER(38, 13) AS median_scale_10, + 0.00000000000000::NUMBER(38, 14) AS median_scale_11, + 0.000000000000000::NUMBER(38, 15) AS median_scale_12, + 0 AS paid_count, + CAST(NULL AS NUMBER(18, 2)) AS mode_over_no_rows, + CAST(NULL AS NUMBER(38, 5)) AS median_over_no_rows, + CAST(NULL AS NUMBER(38, 0)) AS paid_count_over_no_rows, + 0 AS mode_with_having_only +), + +-- Ties and scale-15 medians are not compared exactly: G-3's two amounts are each the mode, and +-- Snowflake's MEDIAN above scale 12 is not exact (0.123456789012344496 or ...498 for a midpoint of +-- 0.1234567890123445, depending on the plan). +__assert__picks_and_fine_medians_are_close AS ( + SELECT s.group_code + FROM __ref("int_edge_amount_statistics") AS s + WHERE + s.signed_zero_pick <> 0 + OR ( + s.group_code = 'G-1' + AND s.mode_amount <> 10.00 + ) + OR ( + s.group_code = 'G-2' + AND s.mode_amount IS NOT NULL + ) + OR ( + s.group_code = 'G-3' + AND s.mode_amount NOT IN (0.00, -0.50) + ) + OR ABS(s.median_scale_15 + - DECODE( + s.group_code, + 'G-1', 0.1234567890123445, + 'G-2', 1.000000000000001, + 'G-3', 0.0000000000000015 + )) > 0.00000000000000001 +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_analysis_patterns.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_analysis_patterns.sql new file mode 100644 index 0000000000..dac9d2c2b1 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_analysis_patterns.sql @@ -0,0 +1,76 @@ +TEST (name "int_edge_analysis_patterns__reads_positional_and_unaliased_columns"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__ref__stg_edge_date_texts AS ( + SELECT + v.text_id, + v.date_text, + NULL AS zoned_text, + NULL::TIMESTAMP_NTZ(3) AS wall_clock, + NULL AS wall_clock_text, + NULL::TIME(3) AS local_time, + NULL::TIME(9) AS fine_time + FROM VALUES + ('DT-01', '2026-03-05'), + ('DT-02', '2026-02-14'), + ('DT-03', 'not a date') AS v(text_id, date_text) +), + +__expected__int_edge_analysis_patterns AS ( + SELECT + 'B-1' AS basket_id, + '2026-02-28'::DATE AS month_end, + 3 AS line_no, + 'EIDER-03' AS positional_sku, + 4 AS next_line_no, + '1' AS line_no_text, + 'EIDER' AS sku_prefix, + 3 AS line_count + UNION ALL + SELECT + 'B-2' AS basket_id, + '2026-02-28'::DATE AS month_end, + 2 AS line_no, + 'MALLARD-01' AS positional_sku, + 3 AS next_line_no, + '1' AS line_no_text, + 'MALLA' AS sku_prefix, + 2 AS line_count + UNION ALL + SELECT + 'B-3' AS basket_id, + '2026-02-28'::DATE AS month_end, + 1 AS line_no, + 'MERGANSER-05' AS positional_sku, + 2 AS next_line_no, + '1' AS line_no_text, + 'MERGA' AS sku_prefix, + 1 AS line_count + UNION ALL + SELECT + 'B-4' AS basket_id, + '2026-02-28'::DATE AS month_end, + 2 AS line_no, + 'EIDER-03' AS positional_sku, + 3 AS next_line_no, + '1' AS line_no_text, + 'EIDER' AS sku_prefix, + 2 AS line_count +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_pairs.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_pairs.sql new file mode 100644 index 0000000000..b000f7c84a --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_pairs.sql @@ -0,0 +1,56 @@ +TEST (name "int_edge_basket_tag_pairs__keeps_pairs_in_different_orders_apart"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_basket_tag_pairs AS ( + SELECT + 'bath|bath' AS tag_pair_text, + PARSE_JSON('["bath","bath"]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + 'bath|garden' AS tag_pair_text, + PARSE_JSON('["bath","garden"]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + 'bath|gift' AS tag_pair_text, + PARSE_JSON('["bath","gift"]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + 'garden|bath' AS tag_pair_text, + PARSE_JSON('["garden","bath"]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + 'gift|' AS tag_pair_text, + PARSE_JSON('["gift",undefined]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + 'gift|bath' AS tag_pair_text, + PARSE_JSON('["gift","bath"]')::ARRAY AS tag_pair, + 2 AS pair_size + UNION ALL + SELECT + '|gift' AS tag_pair_text, + PARSE_JSON('[undefined,"gift"]')::ARRAY AS tag_pair, + 2 AS pair_size +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_sets.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_sets.sql new file mode 100644 index 0000000000..a49ed62932 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_tag_sets.sql @@ -0,0 +1,61 @@ +TEST (name "int_edge_basket_tag_sets__reads_unordered_arrays_without_their_order"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_basket_tag_sets AS ( + SELECT + 'B-1' AS basket_id, + 3 AS tag_count, + TRUE AS has_gift, + FALSE AS overlaps_pond_or_garden, + 2 AS distinct_tag_count, + PARSE_JSON('["bath","gift"]')::ARRAY AS distinct_tags, + 3 AS flattened_count, + 'bath,gift' AS flattened_tags + UNION ALL + SELECT + 'B-2' AS basket_id, + 2 AS tag_count, + FALSE AS has_gift, + TRUE AS overlaps_pond_or_garden, + 3 AS distinct_tag_count, + PARSE_JSON('["bath","garden","gift"]')::ARRAY AS distinct_tags, + 2 AS flattened_count, + 'bath,garden' AS flattened_tags + UNION ALL + SELECT + 'B-3' AS basket_id, + 1 AS tag_count, + FALSE AS has_gift, + TRUE AS overlaps_pond_or_garden, + 2 AS distinct_tag_count, + PARSE_JSON('["gift","pond"]')::ARRAY AS distinct_tags, + 1 AS flattened_count, + 'pond' AS flattened_tags + UNION ALL + SELECT + 'B-4' AS basket_id, + 1 AS tag_count, + TRUE AS has_gift, + FALSE AS overlaps_pond_or_garden, + 1 AS distinct_tag_count, + PARSE_JSON('["gift"]')::ARRAY AS distinct_tags, + 1 AS flattened_count, + 'gift' AS flattened_tags +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_top_tags.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_top_tags.sql new file mode 100644 index 0000000000..0c975296da --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_basket_top_tags.sql @@ -0,0 +1,46 @@ +TEST (name "int_edge_basket_top_tags__ranks_arrays_by_position_and_name"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_basket_top_tags AS ( + SELECT + 'by_name' AS pick_kind, + 'B-3' AS picked_id, + CAST(NULL AS NUMBER(38, 0)) AS picked_size + UNION ALL + SELECT + 'by_position' AS pick_kind, + 'B-3' AS picked_id, + 1 AS picked_size + UNION ALL + SELECT + 'constructed' AS pick_kind, + 'BL-03' AS picked_id, + 1 AS picked_size + UNION ALL + SELECT + 'over_union' AS pick_kind, + 'B-3' AS picked_id, + 1 AS picked_size + UNION ALL + SELECT + 'through_iff' AS pick_kind, + 'B-3' AS picked_id, + 1 AS picked_size +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_category_walk.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_category_walk.sql new file mode 100644 index 0000000000..354824034b --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_category_walk.sql @@ -0,0 +1,62 @@ +TEST (name "int_edge_category_walk__reads_a_level_column_beside_the_walk_depth"); + +WITH __ref__stg_edge_category_links AS ( + SELECT + v.category_id, + v.parent_id, + v.category_name, + v.shelf_depth::NUMBER(38, 0) AS shelf_depth + FROM VALUES + ('CAT-1', NULL, 'Ducks', 1), + ('CAT-2', 'CAT-1', 'Rubber ducks', 4), + ('CAT-3', 'CAT-2', 'Collectible series', 7), + ('CAT-4', 'CAT-1', 'Decoys', 4), + ('CAT-5', NULL, 'Pond supplies', 1) AS v(category_id, parent_id, category_name, shelf_depth) +), + +__expected__int_edge_category_walk AS ( + SELECT + 'CAT-1' AS category_id, + 1 AS walk_depth, + 'Ducks' AS root_name, + ' > Ducks' AS category_path, + 1 AS joined_level, + 'Ducks' AS joined_root_name, + 7 AS deepest_shelf + UNION ALL + SELECT + 'CAT-2' AS category_id, + 2 AS walk_depth, + 'Ducks' AS root_name, + ' > Ducks > Rubber ducks' AS category_path, + 2 AS joined_level, + 'Rubber ducks' AS joined_root_name, + 7 AS deepest_shelf + UNION ALL + SELECT + 'CAT-3' AS category_id, + 3 AS walk_depth, + 'Ducks' AS root_name, + ' > Ducks > Rubber ducks > Collectible series' AS category_path, + 3 AS joined_level, + 'Collectible series' AS joined_root_name, + 7 AS deepest_shelf + UNION ALL + SELECT + 'CAT-4' AS category_id, + 2 AS walk_depth, + 'Ducks' AS root_name, + ' > Ducks > Decoys' AS category_path, + 2 AS joined_level, + 'Decoys' AS joined_root_name, + 7 AS deepest_shelf + UNION ALL + SELECT + 'CAT-5' AS category_id, + 1 AS walk_depth, + 'Pond supplies' AS root_name, + ' > Pond supplies' AS category_path, + 1 AS joined_level, + 'Pond supplies' AS joined_root_name, + 7 AS deepest_shelf +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_clock_arithmetic.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_clock_arithmetic.sql new file mode 100644 index 0000000000..b9599190ee --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_clock_arithmetic.sql @@ -0,0 +1,177 @@ +TEST (name "int_edge_clock_arithmetic__adds_diffs_and_formats_fine_clock_values"); + +WITH __ref__stg_edge_date_texts AS ( + SELECT + v.text_id, + NULL AS date_text, + NULL AS zoned_text, + v.wall_clock::TIMESTAMP_NTZ(3) AS wall_clock, + v.wall_clock_text, + v.local_time::TIME(3) AS local_time, + v.fine_time::TIME(9) AS fine_time + FROM VALUES + ('CK-01', '2026-03-05 23:30:00.123', '2026-03-05 23:30:00.1239', '23:59:59.999', '23:59:59.9999999'), + ('CK-02', '2026-03-05 23:30:00.000', '2026-03-05 23:30:00.9', '00:00:00', '12:00:00.0000005'), + ('CK-03', '2026-03-05 23:30:00.120', '2026-03-05 23:30:00.12', '12:30:15.250', '00:00:00.000000001'), + ('CK-04', '1969-12-31 23:59:59.999', '1969-12-31 23:59:59.999', '23:30:00', '23:59:59.999999999'), + ('CK-05', '2026-12-31 23:59:59.999', '2026-12-31 23:59:59.999000', '06:00:00.001', '06:00:00.0000001'), + ('CK-06', NULL, NULL, NULL, NULL) AS v(text_id, wall_clock, wall_clock_text, local_time, fine_time) +), + +__expected__int_edge_clock_arithmetic AS ( + SELECT + 'CK-01' AS text_id, + '00:29:59.999'::TIME(9) AS time_plus_minutes, + '13:59:59.999'::TIME(9) AS time_minus_hours, + '00:00:00'::TIME(9) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + '2026-03-06 23:30:00.123'::TIMESTAMP_NTZ(9) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + 1 AS years_to_2027, + 10 AS months_to_2027, + 43 AS weeks_to_2027, + 7225 AS hours_to_2027, + 26008199877 AS milliseconds_to_2027, + 0 AS minutes_to_midnight, + '23:59:59.999999' AS fine_time_micro_text, + '23:59:59' AS fine_time_second_text, + '23:59:59.999' AS fine_time_milli_text, + '2026-03-05 23:30:00.123' AS text_clock_ff3, + '23:30:00.123' AS wall_clock_bare_ff, + FALSE AS wall_clock_equals_text, + FALSE AS whole_second_equals_text, + TRUE AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type + UNION ALL + SELECT + 'CK-02' AS text_id, + '00:30:00'::TIME(9) AS time_plus_minutes, + '14:00:00'::TIME(9) AS time_minus_hours, + '00:00:00.001'::TIME(9) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + '2026-03-06 23:30:00'::TIMESTAMP_NTZ(9) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + 1 AS years_to_2027, + 10 AS months_to_2027, + 43 AS weeks_to_2027, + 7225 AS hours_to_2027, + 26008200000 AS milliseconds_to_2027, + 1439 AS minutes_to_midnight, + '12:00:00.000000' AS fine_time_micro_text, + '12:00:00' AS fine_time_second_text, + '12:00:00.000' AS fine_time_milli_text, + '2026-03-05 23:30:00.900' AS text_clock_ff3, + '23:30:00.000' AS wall_clock_bare_ff, + FALSE AS wall_clock_equals_text, + FALSE AS whole_second_equals_text, + TRUE AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type + UNION ALL + SELECT + 'CK-03' AS text_id, + '13:00:15.25'::TIME(9) AS time_plus_minutes, + '02:30:15.25'::TIME(9) AS time_minus_hours, + '12:30:15.251'::TIME(9) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + '2026-03-06 23:30:00.12'::TIMESTAMP_NTZ(9) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + 1 AS years_to_2027, + 10 AS months_to_2027, + 43 AS weeks_to_2027, + 7225 AS hours_to_2027, + 26008199880 AS milliseconds_to_2027, + 689 AS minutes_to_midnight, + '00:00:00.000000' AS fine_time_micro_text, + '00:00:00' AS fine_time_second_text, + '00:00:00.000' AS fine_time_milli_text, + '2026-03-05 23:30:00.120' AS text_clock_ff3, + '23:30:00.120' AS wall_clock_bare_ff, + TRUE AS wall_clock_equals_text, + FALSE AS whole_second_equals_text, + TRUE AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type + UNION ALL + SELECT + 'CK-04' AS text_id, + '00:00:00'::TIME(9) AS time_plus_minutes, + '13:30:00'::TIME(9) AS time_minus_hours, + '23:30:00.001'::TIME(9) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + '1970-01-01 23:59:59.999'::TIMESTAMP_NTZ(9) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + 58 AS years_to_2027, + 685 AS months_to_2027, + 2974 AS weeks_to_2027, + 499657 AS hours_to_2027, + 1798761600001 AS milliseconds_to_2027, + 29 AS minutes_to_midnight, + '23:59:59.999999' AS fine_time_micro_text, + '23:59:59' AS fine_time_second_text, + '23:59:59.999' AS fine_time_milli_text, + '1969-12-31 23:59:59.999' AS text_clock_ff3, + '23:59:59.999' AS wall_clock_bare_ff, + TRUE AS wall_clock_equals_text, + FALSE AS whole_second_equals_text, + TRUE AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type + UNION ALL + SELECT + 'CK-05' AS text_id, + '06:30:00.001'::TIME(9) AS time_plus_minutes, + '20:00:00.001'::TIME(9) AS time_minus_hours, + '06:00:00.002'::TIME(9) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + '2027-01-01 23:59:59.999'::TIMESTAMP_NTZ(9) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + 1 AS years_to_2027, + 1 AS months_to_2027, + 0 AS weeks_to_2027, + 1 AS hours_to_2027, + 1 AS milliseconds_to_2027, + 1079 AS minutes_to_midnight, + '06:00:00.000000' AS fine_time_micro_text, + '06:00:00' AS fine_time_second_text, + '06:00:00.000' AS fine_time_milli_text, + '2026-12-31 23:59:59.999' AS text_clock_ff3, + '23:59:59.999' AS wall_clock_bare_ff, + TRUE AS wall_clock_equals_text, + FALSE AS whole_second_equals_text, + TRUE AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type + UNION ALL + SELECT + 'CK-06' AS text_id, + CAST(NULL AS TIME(9)) AS time_plus_minutes, + CAST(NULL AS TIME(9)) AS time_minus_hours, + CAST(NULL AS TIME(9)) AS time_plus_millisecond, + 'TIME(9)' AS time_add_type, + 'TIME(3)' AS time_cast_type, + CAST(NULL AS TIMESTAMP_NTZ(9)) AS wall_clock_plus_day, + 'TIMESTAMP_NTZ(9)' AS wall_clock_add_type, + CAST(NULL AS NUMBER(38, 0)) AS years_to_2027, + CAST(NULL AS NUMBER(38, 0)) AS months_to_2027, + CAST(NULL AS NUMBER(38, 0)) AS weeks_to_2027, + CAST(NULL AS NUMBER(38, 0)) AS hours_to_2027, + CAST(NULL AS NUMBER(38, 0)) AS milliseconds_to_2027, + CAST(NULL AS NUMBER(38, 0)) AS minutes_to_midnight, + CAST(NULL AS VARCHAR) AS fine_time_micro_text, + CAST(NULL AS VARCHAR) AS fine_time_second_text, + CAST(NULL AS VARCHAR) AS fine_time_milli_text, + CAST(NULL AS VARCHAR) AS text_clock_ff3, + CAST(NULL AS VARCHAR) AS wall_clock_bare_ff, + CAST(NULL AS BOOLEAN) AS wall_clock_equals_text, + CAST(NULL AS BOOLEAN) AS whole_second_equals_text, + CAST(NULL AS BOOLEAN) AS whole_second_before_text, + 'TIMESTAMP_NTZ(3)' AS epoch_millis_type, + 'TIMESTAMP_NTZ(9)' AS bare_timestamp_type +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_grouped_windows.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_grouped_windows.sql new file mode 100644 index 0000000000..7a0b137c41 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_grouped_windows.sql @@ -0,0 +1,63 @@ +TEST (name "int_edge_grouped_windows__ranks_groups_by_aggregates_and_rolls_up_a_null_total"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', NULL, NULL, NULL, 10.00, NULL, NULL, NULL), + ('AC-02', 'G-1', NULL, NULL, NULL, -0.50, NULL, NULL, NULL), + ('AC-03', 'G-1', NULL, NULL, NULL, 2.50, NULL, NULL, NULL), + ('AC-04', 'G-2', NULL, NULL, NULL, 0.00, NULL, NULL, NULL), + ('AC-05', 'G-2', NULL, NULL, NULL, NULL, NULL, NULL, NULL), + ('AC-06', 'G-3', NULL, NULL, NULL, 999999999999.99, NULL, NULL, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_grouped_windows AS ( + SELECT + 'ALL' AS group_label, + CAST(NULL AS VARCHAR) AS group_code, + 1000000000011.99::NUMBER(38, 2) AS group_amount, + 6 AS check_count, + CAST(NULL AS NUMBER(38, 2)) AS total_amount, + CAST(NULL AS NUMBER(38, 6)) AS amount_share, + CAST(NULL AS NUMBER(38, 0)) AS size_rank, + FALSE AS is_largest_group + UNION ALL + SELECT + 'G-1' AS group_label, + 'G-1' AS group_code, + 12.00::NUMBER(38, 2) AS group_amount, + 3 AS check_count, + 1000000000011.99::NUMBER(38, 2) AS total_amount, + 0.000000::NUMBER(38, 6) AS amount_share, + 1 AS size_rank, + FALSE AS is_largest_group + UNION ALL + SELECT + 'G-2' AS group_label, + 'G-2' AS group_code, + 0.00::NUMBER(38, 2) AS group_amount, + 2 AS check_count, + 1000000000011.99::NUMBER(38, 2) AS total_amount, + 0.000000::NUMBER(38, 6) AS amount_share, + 2 AS size_rank, + FALSE AS is_largest_group + UNION ALL + SELECT + 'G-3' AS group_label, + 'G-3' AS group_code, + 999999999999.99::NUMBER(38, 2) AS group_amount, + 1 AS check_count, + 1000000000011.99::NUMBER(38, 2) AS total_amount, + 1.000000::NUMBER(38, 6) AS amount_share, + 3 AS size_rank, + TRUE AS is_largest_group +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_join_keys.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_join_keys.sql new file mode 100644 index 0000000000..9fe0e74795 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_join_keys.sql @@ -0,0 +1,114 @@ +TEST (name "int_edge_join_keys__takes_merged_keys_and_outer_sides_from_the_right_relation"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', 7, '7', '01', 10.00, 0.123456789012345, 0.29, NULL), + ('AC-02', 'G-1', 12, '12345678901234567890123', '1', -0.50, 0.123456789012344, 1.005, NULL), + ('AC-03', 'G-1', NULL, '12', '12', 2.50, 0.5, 2.675, NULL), + ('AC-04', 'G-2', 123456789012345678, '123456789012345678', ' 7', 0.00, 1, -155.5, NULL), + ('AC-05', 'G-2', 99, NULL, NULL, NULL, NULL, NULL, NULL), + ('AC-06', 'G-3', 5, '5', 'n/a', 999999999999.99, 0.000000000000001, 0.57, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_join_keys AS ( + SELECT + 'asof:AC-01:G-1:-' AS row_key, + 'asof:AC-01' AS join_shape, + 'G-1' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'asof:AC-02:G-1:ten or more' AS row_key, + 'asof:AC-02' AS join_shape, + 'G-1' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + 'ten or more' AS threshold_name + UNION ALL + SELECT + 'asof:AC-03:G-1:-' AS row_key, + 'asof:AC-03' AS join_shape, + 'G-1' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'asof:AC-04:G-2:a hundred or more' AS row_key, + 'asof:AC-04' AS join_shape, + 'G-2' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + 'a hundred or more' AS threshold_name + UNION ALL + SELECT + 'asof:AC-05:G-2:ten or more' AS row_key, + 'asof:AC-05' AS join_shape, + 'G-2' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + 'ten or more' AS threshold_name + UNION ALL + SELECT + 'asof:AC-06:G-3:-' AS row_key, + 'asof:AC-06' AS join_shape, + 'G-3' AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'natural:G-1:-' AS row_key, + 'natural' AS join_shape, + 'G-1' AS group_code, + 'Mallard desk' AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'natural:G-2:-' AS row_key, + 'natural' AS join_shape, + 'G-2' AS group_code, + 'Teal desk' AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'right_using:G-1:-' AS row_key, + 'right_using' AS join_shape, + 'G-1' AS group_code, + 'Mallard desk' AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'right_using:G-2:-' AS row_key, + 'right_using' AS join_shape, + 'G-2' AS group_code, + 'Teal desk' AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'right_using:G-9:-' AS row_key, + 'right_using' AS join_shape, + 'G-9' AS group_code, + 'Eider desk' AS group_name, + CAST(NULL AS VARCHAR) AS threshold_name + UNION ALL + SELECT + 'using_then_right:-:G-7' AS row_key, + 'using_then_right' AS join_shape, + CAST(NULL AS VARCHAR) AS group_code, + CAST(NULL AS VARCHAR) AS group_name, + 'G-7' AS threshold_name + UNION ALL + SELECT + 'using_then_right:G-1:G-1' AS row_key, + 'using_then_right' AS join_shape, + 'G-1' AS group_code, + 'Mallard desk' AS group_name, + 'G-1' AS threshold_name +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_json_numbers.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_json_numbers.sql new file mode 100644 index 0000000000..4d80a422ca --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_json_numbers.sql @@ -0,0 +1,107 @@ +TEST (name "int_edge_json_numbers__parses_zero_fractions_and_rejects_two_decimal_points"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + NULL AS group_code, + NULL::NUMBER(18, 0) AS narrow_key, + NULL::NUMBER(38, 0) AS wide_key, + NULL AS key_text, + NULL::NUMBER(18, 2) AS amount, + NULL::NUMBER(20, 15) AS fine_amount, + NULL::FLOAT AS ratio, + IFF(v.json_text = '@forty_zeros', '1.' || REPEAT('0', 40), v.json_text) AS json_text + FROM VALUES + ('AC-01', '{"amount": 2.0, "qty": 1.00}'), + ('AC-02', '1.0.5'), + ('AC-03', '{"amount": 1.00.25}'), + ('AC-04', '[1.0.5, 2.0]'), + ('AC-05', '12345678901234567890123456789012345678.0'), + ('AC-06', '@forty_zeros'), + ('AC-07', '{"amount": -0.0}'), + ('AC-08', '{"amount": 2.0e5}'), + ('AC-09', '{"amount": 100.00}'), + ('AC-10', '123456789012345678901234567890123456789.0') AS v(check_id, json_text) +), + +__expected__int_edge_json_numbers AS ( + SELECT + 'AC-01' AS check_id, + 'OBJECT' AS parsed_kind, + '{"amount":2,"qty":1}' AS parsed_text, + 'INTEGER' AS amount_kind, + '2' AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-02' AS check_id, + CAST(NULL AS VARCHAR) AS parsed_kind, + CAST(NULL AS VARCHAR) AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + FALSE AS is_valid_json + UNION ALL + SELECT + 'AC-03' AS check_id, + CAST(NULL AS VARCHAR) AS parsed_kind, + CAST(NULL AS VARCHAR) AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + FALSE AS is_valid_json + UNION ALL + SELECT + 'AC-04' AS check_id, + CAST(NULL AS VARCHAR) AS parsed_kind, + CAST(NULL AS VARCHAR) AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + FALSE AS is_valid_json + UNION ALL + SELECT + 'AC-05' AS check_id, + 'INTEGER' AS parsed_kind, + '12345678901234567890123456789012345678' AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-06' AS check_id, + 'INTEGER' AS parsed_kind, + '1' AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-07' AS check_id, + 'OBJECT' AS parsed_kind, + '{"amount":0}' AS parsed_text, + 'INTEGER' AS amount_kind, + '0' AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-08' AS check_id, + 'OBJECT' AS parsed_kind, + '{"amount":2.000000000000000e+05}' AS parsed_text, + 'DOUBLE' AS amount_kind, + '200000' AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-09' AS check_id, + 'OBJECT' AS parsed_kind, + '{"amount":100}' AS parsed_text, + 'INTEGER' AS amount_kind, + '100' AS amount_text, + TRUE AS is_valid_json + UNION ALL + SELECT + 'AC-10' AS check_id, + 'INTEGER' AS parsed_kind, + '123456789012345678901234567890123456789' AS parsed_text, + CAST(NULL AS VARCHAR) AS amount_kind, + CAST(NULL AS VARCHAR) AS amount_text, + TRUE AS is_valid_json +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_key_widths.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_key_widths.sql new file mode 100644 index 0000000000..da6e1500b2 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_key_widths.sql @@ -0,0 +1,193 @@ +TEST (name "int_edge_key_widths__joins_number_widths_and_text_keys_with_using"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', 7, '7', '01', 10.00, 0.123456789012345, 0.29, NULL), + ('AC-02', 'G-1', 12, '12345678901234567890123', '1', -0.50, 0.123456789012344, 1.005, NULL), + ('AC-03', 'G-1', NULL, '12', '12', 2.50, 0.5, 2.675, NULL), + ('AC-04', 'G-2', 123456789012345678, '123456789012345678', ' 7', 0.00, 1, -155.5, NULL), + ('AC-05', 'G-2', 99, NULL, NULL, NULL, NULL, NULL, NULL), + ('AC-06', 'G-3', 5, '5', 'n/a', 999999999999.99, 0.000000000000001, 0.57, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_key_widths AS ( + SELECT + 'full:-:AC-02' AS pair_id, + 'full' AS join_kind, + '12345678901234567890123' AS check_key_text, + CAST(NULL AS VARCHAR) AS narrow_check_id, + 'AC-02' AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'full:AC-01:AC-01' AS pair_id, + 'full' AS join_kind, + '7' AS check_key_text, + 'AC-01' AS narrow_check_id, + 'AC-01' AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'full:AC-02:AC-03' AS pair_id, + 'full' AS join_kind, + '12' AS check_key_text, + 'AC-02' AS narrow_check_id, + 'AC-03' AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'full:AC-04:AC-04' AS pair_id, + 'full' AS join_kind, + '123456789012345678' AS check_key_text, + 'AC-04' AS narrow_check_id, + 'AC-04' AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'full:AC-05:-' AS pair_id, + 'full' AS join_kind, + '99' AS check_key_text, + 'AC-05' AS narrow_check_id, + CAST(NULL AS VARCHAR) AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'full:AC-06:AC-06' AS pair_id, + 'full' AS join_kind, + '5' AS check_key_text, + 'AC-06' AS narrow_check_id, + 'AC-06' AS wide_check_id, + 'NUMBER(38,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'inner:AC-01:AC-01' AS pair_id, + 'inner' AS join_kind, + '7' AS check_key_text, + 'AC-01' AS narrow_check_id, + 'AC-01' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(18,0)' AS wide_side_type + UNION ALL + SELECT + 'inner:AC-02:AC-03' AS pair_id, + 'inner' AS join_kind, + '12' AS check_key_text, + 'AC-02' AS narrow_check_id, + 'AC-03' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(18,0)' AS wide_side_type + UNION ALL + SELECT + 'inner:AC-04:AC-04' AS pair_id, + 'inner' AS join_kind, + '123456789012345678' AS check_key_text, + 'AC-04' AS narrow_check_id, + 'AC-04' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(18,0)' AS wide_side_type + UNION ALL + SELECT + 'inner:AC-06:AC-06' AS pair_id, + 'inner' AS join_kind, + '5' AS check_key_text, + 'AC-06' AS narrow_check_id, + 'AC-06' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(18,0)' AS wide_side_type + UNION ALL + SELECT + 'left:AC-01:AC-01' AS pair_id, + 'left' AS join_kind, + '7' AS check_key_text, + 'AC-01' AS narrow_check_id, + 'AC-01' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'left:AC-02:AC-03' AS pair_id, + 'left' AS join_kind, + '12' AS check_key_text, + 'AC-02' AS narrow_check_id, + 'AC-03' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'left:AC-04:AC-04' AS pair_id, + 'left' AS join_kind, + '123456789012345678' AS check_key_text, + 'AC-04' AS narrow_check_id, + 'AC-04' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'left:AC-05:-' AS pair_id, + 'left' AS join_kind, + '99' AS check_key_text, + 'AC-05' AS narrow_check_id, + CAST(NULL AS VARCHAR) AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'left:AC-06:AC-06' AS pair_id, + 'left' AS join_kind, + '5' AS check_key_text, + 'AC-06' AS narrow_check_id, + 'AC-06' AS wide_check_id, + 'NUMBER(18,0)' AS merged_key_type, + 'NUMBER(18,0)' AS narrow_side_type, + 'NUMBER(38,0)' AS wide_side_type + UNION ALL + SELECT + 'text:AC-03:AC-03' AS pair_id, + 'text' AS join_kind, + '12' AS check_key_text, + 'AC-03' AS narrow_check_id, + 'AC-03' AS wide_check_id, + 'VARCHAR(3)' AS merged_key_type, + 'VARCHAR(3)' AS narrow_side_type, + 'VARCHAR(3)' AS wide_side_type + UNION ALL + SELECT + 'text:AC-04:AC-01' AS pair_id, + 'text' AS join_kind, + ' 7' AS check_key_text, + 'AC-04' AS narrow_check_id, + 'AC-01' AS wide_check_id, + 'VARCHAR(3)' AS merged_key_type, + 'VARCHAR(3)' AS narrow_side_type, + 'VARCHAR(3)' AS wide_side_type +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_note_patterns.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_note_patterns.sql new file mode 100644 index 0000000000..be945f0afc --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_note_patterns.sql @@ -0,0 +1,395 @@ +TEST (name "int_edge_note_patterns__searches_notes_with_edge_patterns"); + +WITH __ref__stg_edge_ticket_notes AS ( + SELECT + v.note_id, + IFF(v.body = '@long', REPEAT('pond duck ', 40), v.body) AS body, + v.tags_text + FROM VALUES + ('TN-01', 'xaxax', NULL), + ('TN-02', 'xab xa', NULL), + ('TN-03', 'x+', NULL), + ('TN-04', 'bxba', NULL), + ('TN-05', 'bxb', NULL), + ('TN-06', '', NULL), + ('TN-07', 'baa', NULL), + ('TN-08', 'a\r\nb', NULL), + ('TN-09', '\u03bc', NULL), + ('TN-10', '\u03c2', NULL), + ('TN-11', 'sabc', NULL), + ('TN-12', '\u017fabc', NULL), + ('TN-13', '\u00c9\u00e9', '\u00e9\u00c9'), + ('TN-14', '\u00c9mile \u00e9mile', '\u00e9mile \u00c9mile'), + ('TN-15', 'Pintail', 'b3J\nkZXI='), + ('TN-16', 'Teal', 'YQ=\n='), + ('TN-17', 'Eider', 'YQ\n=='), + ('TN-18', '@long', NULL), + ('TN-19', NULL, NULL) AS v(note_id, body, tags_text) +), + +__expected__int_edge_note_patterns AS ( + SELECT + 'TN-01' AS note_id, + 1 AS lazy_then_optional_count, + 'xaxax' AS lazy_then_optional_match, + 'xa' AS optional_twice_match, + 'x' AS bracket_close_match, + 'xaxax' AS bracket_negated_match, + 0 AS lazy_plus_position, + 6 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + 'xaxax' AS ranked_word + UNION ALL + SELECT + 'TN-02' AS note_id, + 2 AS lazy_then_optional_count, + 'x' AS lazy_then_optional_match, + 'xa' AS optional_twice_match, + 'x' AS bracket_close_match, + 'xab xa' AS bracket_negated_match, + 3 AS lazy_plus_position, + 7 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + 'xa' AS ranked_word + UNION ALL + SELECT + 'TN-03' AS note_id, + 1 AS lazy_then_optional_count, + 'x' AS lazy_then_optional_match, + 'x' AS optional_twice_match, + 'x+' AS bracket_close_match, + 'x' AS bracket_negated_match, + 0 AS lazy_plus_position, + 3 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-04' AS note_id, + 1 AS lazy_then_optional_count, + 'x' AS lazy_then_optional_match, + 'x' AS optional_twice_match, + 'x' AS bracket_close_match, + 'bxba' AS bracket_negated_match, + 1 AS lazy_plus_position, + 5 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-05' AS note_id, + 1 AS lazy_then_optional_count, + 'x' AS lazy_then_optional_match, + 'x' AS optional_twice_match, + 'x' AS bracket_close_match, + 'bxb' AS bracket_negated_match, + 1 AS lazy_plus_position, + 4 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-06' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + CAST(NULL AS VARCHAR) AS bracket_negated_match, + 0 AS lazy_plus_position, + 0 AS end_anchor_position, + 0 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-07' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'baa' AS bracket_negated_match, + 1 AS lazy_plus_position, + 4 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-08' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'a\u000d\u000ab' AS bracket_negated_match, + 4 AS lazy_plus_position, + 5 AS end_anchor_position, + 1 AS star_position, + 'a\u000d' AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-09' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'μ' AS bracket_negated_match, + 0 AS lazy_plus_position, + 2 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + TRUE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-10' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'ς' AS bracket_negated_match, + 0 AS lazy_plus_position, + 2 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + TRUE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-11' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'sabc' AS bracket_negated_match, + 3 AS lazy_plus_position, + 5 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + TRUE AS ilike_s_prefix, + TRUE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-12' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'ſabc' AS bracket_negated_match, + 3 AS lazy_plus_position, + 5 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + TRUE AS ilike_s_prefix, + TRUE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-13' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'Éé' AS bracket_negated_match, + 0 AS lazy_plus_position, + 3 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + 100 AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-14' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'Émile émile' AS bracket_negated_match, + 0 AS lazy_plus_position, + 12 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + 100 AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-15' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'Pintail' AS bracket_negated_match, + 0 AS lazy_plus_position, + 8 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + 41 AS name_similarity, + 'order' AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-16' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'Teal' AS bracket_negated_match, + 0 AS lazy_plus_position, + 5 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + 0 AS name_similarity, + 'a' AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-17' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'Eider' AS bracket_negated_match, + 0 AS lazy_plus_position, + 6 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + 0 AS name_similarity, + 'a' AS decoded_tags, + '' AS ranked_word + UNION ALL + SELECT + 'TN-18' AS note_id, + 0 AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + 'pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck ' + || 'pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck ' + || 'pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck ' + || 'pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck pond duck ' + || 'pond duck pond duck pond duck pond duck ' AS bracket_negated_match, + 0 AS lazy_plus_position, + 401 AS end_anchor_position, + 1 AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + FALSE AS matches_micro_sign, + FALSE AS matches_sigma, + FALSE AS ilike_s_prefix, + FALSE AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + 'duck' AS ranked_word + UNION ALL + SELECT + 'TN-19' AS note_id, + CAST(NULL AS NUMBER(38, 0)) AS lazy_then_optional_count, + CAST(NULL AS VARCHAR) AS lazy_then_optional_match, + CAST(NULL AS VARCHAR) AS optional_twice_match, + CAST(NULL AS VARCHAR) AS bracket_close_match, + CAST(NULL AS VARCHAR) AS bracket_negated_match, + CAST(NULL AS NUMBER(38, 0)) AS lazy_plus_position, + CAST(NULL AS NUMBER(38, 0)) AS end_anchor_position, + CAST(NULL AS NUMBER(38, 0)) AS star_position, + CAST(NULL AS VARCHAR) AS multiline_dot_match, + CAST(NULL AS BOOLEAN) AS matches_micro_sign, + CAST(NULL AS BOOLEAN) AS matches_sigma, + CAST(NULL AS BOOLEAN) AS ilike_s_prefix, + CAST(NULL AS BOOLEAN) AS ilike_long_s_escaped, + CAST(NULL AS NUMBER(38, 0)) AS name_similarity, + CAST(NULL AS VARCHAR) AS decoded_tags, + CAST(NULL AS VARCHAR) AS ranked_word +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_null_arithmetic.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_null_arithmetic.sql new file mode 100644 index 0000000000..b4aa0744c5 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_null_arithmetic.sql @@ -0,0 +1,191 @@ +TEST (name "int_edge_null_arithmetic__types_null_arithmetic_and_literals"); + +WITH __ref__stg_edge_amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text + FROM VALUES + ('AC-01', 'G-1', 7, '7', '01', 10.00, 0.123456789012345, 0.29, NULL), + ('AC-02', 'G-1', 12, '12345678901234567890123', '1', -0.50, 0.123456789012344, 1.005, NULL), + ('AC-03', 'G-1', NULL, '12', '12', 2.50, 0.5, 2.675, NULL), + ('AC-04', 'G-2', 123456789012345678, '123456789012345678', ' 7', 0.00, 1, -155.5, NULL), + ('AC-05', 'G-2', 99, NULL, NULL, NULL, NULL, NULL, NULL), + ('AC-06', 'G-3', 5, '5', 'n/a', 999999999999.99, 0.000000000000001, 0.57, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__int_edge_null_arithmetic AS ( + SELECT + 'AC-01' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '10.00' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + '7.00000' AS case_without_else, + '10.00000' AS decoded + UNION ALL + SELECT + 'AC-02' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '-0.50' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + '1.00000' AS case_without_else, + '1.00000' AS decoded + UNION ALL + SELECT + 'AC-03' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '2.50' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + CAST(NULL AS VARCHAR) AS case_without_else, + CAST(NULL AS VARCHAR) AS decoded + UNION ALL + SELECT + 'AC-04' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '0.00' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + CAST(NULL AS VARCHAR) AS case_without_else, + CAST(NULL AS VARCHAR) AS decoded + UNION ALL + SELECT + 'AC-05' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '1.50' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + CAST(NULL AS VARCHAR) AS case_without_else, + CAST(NULL AS VARCHAR) AS decoded + UNION ALL + SELECT + 'AC-06' AS check_id, + CAST(NULL AS NUMBER(38, 2)) AS null_plus_amount, + CAST(NULL AS NUMBER(38, 2)) AS null_times_amount, + CAST(NULL AS NUMBER(38, 6)) AS null_over_amount, + CAST(NULL AS FLOAT) AS ratio_over_null, + CAST(NULL AS NUMBER(38, 2)) AS wide_amount_times_null, + CAST(NULL AS NUMBER(38, 2)) AS missing_times_amount, + '0.00' AS null_times_amount_or_zero, + 'NUMBER(26,6)' AS null_over_amount_type, + 'FLOAT' AS ratio_over_null_type, + 'NUMBER(38,2)' AS wide_amount_times_null_type, + 'NUMBER(36,2)' AS missing_times_amount_type, + '999999999999.99' AS amount_or_decimal, + 'NUMBER(18,2)' AS amount_or_decimal_type, + 'NUMBER(6,0)' AS exponent_type, + 'NUMBER(29,12)' AS small_exponent_product_type, + 'NUMBER(2,0)' AS remainder_type, + 'NUMBER(18,0)' AS key_remainder_type, + 'NUMBER(18,2)' AS nullif_type, + 'NULL' AS ilike_null_type, + 'NUMBER(18,0)' AS row_number_type, + 'NUMBER(18,0)' AS count_type, + 'NUMBER(18,2)' AS union_decimal_type, + 'NULL' AS all_null_union_type, + CAST(NULL AS VARCHAR) AS case_without_else, + CAST(NULL AS VARCHAR) AS decoded +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_payload_paths.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_payload_paths.sql new file mode 100644 index 0000000000..29c778bce6 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_payload_paths.sql @@ -0,0 +1,149 @@ +TEST (name "int_edge_payload_paths__writes_recursive_paths_for_unusual_keys"); + +WITH __ref__stg_edge_variant_values AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key::NUMBER(38, 0) AS sort_key, + IFF( + v.attribute_value = '@date', + TO_VARIANT('2026-03-01'::DATE), + PARSE_JSON(v.attribute_value) + ) AS attribute_value, + v.value_text + FROM VALUES + ('VV-01', 'V-1', 1, '{"a$b": 1, "a-b": {"@x": 2}, "a[b": [3, {"k": 4}]}', NULL), + ('VV-02', 'V-1', 2, '[{"x y": 1}, [2]]', NULL), + ('VV-03', 'V-1', 3, '5', NULL), + ('VV-04', 'V-1', 4, '{"\u00e9": 5, "plain key": 6, "": 7, "a.b": 8, "9z": 9}', NULL) AS v(value_id, group_code, sort_key, attribute_value, value_text) +), + +__expected__int_edge_payload_paths AS ( + SELECT + 'VV-01:[''a$b'']' AS element_id, + 'VV-01' AS value_id, + '[''a$b'']' AS element_path, + 'a$b' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a-b'']' AS element_id, + 'VV-01' AS value_id, + '[''a-b'']' AS element_path, + 'a-b' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'OBJECT' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a-b''][''@x'']' AS element_id, + 'VV-01' AS value_id, + '[''a-b''][''@x'']' AS element_path, + '@x' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a[b'']' AS element_id, + 'VV-01' AS value_id, + '[''a[b'']' AS element_path, + 'a[b' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'ARRAY' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a[b''][0]' AS element_id, + 'VV-01' AS value_id, + '[''a[b''][0]' AS element_path, + CAST(NULL AS VARCHAR) AS element_key, + 0 AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a[b''][1]' AS element_id, + 'VV-01' AS value_id, + '[''a[b''][1]' AS element_path, + CAST(NULL AS VARCHAR) AS element_key, + 1 AS element_index, + 'OBJECT' AS element_kind + UNION ALL + SELECT + 'VV-01:[''a[b''][1].k' AS element_id, + 'VV-01' AS value_id, + '[''a[b''][1].k' AS element_path, + 'k' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-02:[0]' AS element_id, + 'VV-02' AS value_id, + '[0]' AS element_path, + CAST(NULL AS VARCHAR) AS element_key, + 0 AS element_index, + 'OBJECT' AS element_kind + UNION ALL + SELECT + 'VV-02:[0][''x y'']' AS element_id, + 'VV-02' AS value_id, + '[0][''x y'']' AS element_path, + 'x y' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-02:[1]' AS element_id, + 'VV-02' AS value_id, + '[1]' AS element_path, + CAST(NULL AS VARCHAR) AS element_key, + 1 AS element_index, + 'ARRAY' AS element_kind + UNION ALL + SELECT + 'VV-02:[1][0]' AS element_id, + 'VV-02' AS value_id, + '[1][0]' AS element_path, + CAST(NULL AS VARCHAR) AS element_key, + 0 AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-04:['''']' AS element_id, + 'VV-04' AS value_id, + '['''']' AS element_path, + '' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-04:[''9z'']' AS element_id, + 'VV-04' AS value_id, + '[''9z'']' AS element_path, + '9z' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-04:[''a.b'']' AS element_id, + 'VV-04' AS value_id, + '[''a.b'']' AS element_path, + 'a.b' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-04:[''plain key'']' AS element_id, + 'VV-04' AS value_id, + '[''plain key'']' AS element_path, + 'plain key' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind + UNION ALL + SELECT + 'VV-04:é' AS element_id, + 'VV-04' AS value_id, + 'é' AS element_path, + 'é' AS element_key, + CAST(NULL AS NUMBER(38, 0)) AS element_index, + 'INTEGER' AS element_kind +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_python_values.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_python_values.sql new file mode 100644 index 0000000000..ae793a84e4 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_python_values.sql @@ -0,0 +1,95 @@ +TEST (name "int_edge_python_values__hands_python_values_to_variants"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'nothing'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'zoned_time'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'naive_time'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'naive_time_fraction'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'exponent_decimal'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'plain_decimal'), + ('BL-07', 'B-3', 2, 'TEAL-02', 'wide_integer'), + ('BL-08', 'B-4', 1, 'EIDER-03', 'nested'), + ('BL-09', 'B-4', 2, 'EIDER-03', 'some other kind'), + ('BL-10', 'B-4', 3, 'EIDER-03', NULL) AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_python_values AS ( + SELECT + 'exponent_decimal' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"1E+3"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'naive_time' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"2026-03-01 09:30:00"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'naive_time_fraction' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"2026-03-01 09:30:00.250000"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'nested' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'OBJECT' AS payload_kind, + '{"a":true,"b":[1,null]}' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'nothing' AS kind, + FALSE AS payload_is_sql_null, + TRUE AS payload_is_json_null, + 'NULL_VALUE' AS payload_kind, + 'null' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'plain_decimal' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"1.50"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'some other kind' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"some other kind"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'wide_integer' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'INTEGER' AS payload_kind, + '1152921504606846977' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float + UNION ALL + SELECT + 'zoned_time' AS kind, + FALSE AS payload_is_sql_null, + FALSE AS payload_is_json_null, + 'VARCHAR' AS payload_kind, + '"2026-03-01 09:30:00+00:00"' AS payload_text, + '9007199254740992.0'::FLOAT AS amount_as_float +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_recursive_counts.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_recursive_counts.sql new file mode 100644 index 0000000000..a11e8e0bd6 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_recursive_counts.sql @@ -0,0 +1,47 @@ +TEST (name "int_edge_recursive_counts__recurses_without_the_recursive_keyword"); + +WITH __ref__stg_edge_category_links AS ( + SELECT + v.category_id, + v.parent_id, + v.category_name, + v.shelf_depth::NUMBER(38, 0) AS shelf_depth + FROM VALUES + ('CAT-1', NULL, 'Ducks', 1), + ('CAT-2', 'CAT-1', 'Rubber ducks', 4), + ('CAT-3', 'CAT-2', 'Collectible series', 7), + ('CAT-4', 'CAT-1', 'Decoys', 4), + ('CAT-5', NULL, 'Pond supplies', 1) AS v(category_id, parent_id, category_name, shelf_depth) +), + +__expected__int_edge_recursive_counts AS ( + SELECT + 'CAT-1' AS category_id, + 1 AS tree_depth, + CAST(NULL AS VARCHAR) AS parent_name, + CAST(NULL AS VARCHAR) AS grandparent_name + UNION ALL + SELECT + 'CAT-2' AS category_id, + 2 AS tree_depth, + 'Ducks' AS parent_name, + CAST(NULL AS VARCHAR) AS grandparent_name + UNION ALL + SELECT + 'CAT-3' AS category_id, + 3 AS tree_depth, + 'Rubber ducks' AS parent_name, + 'Ducks' AS grandparent_name + UNION ALL + SELECT + 'CAT-4' AS category_id, + 2 AS tree_depth, + 'Ducks' AS parent_name, + CAST(NULL AS VARCHAR) AS grandparent_name + UNION ALL + SELECT + 'CAT-5' AS category_id, + 1 AS tree_depth, + CAST(NULL AS VARCHAR) AS parent_name, + CAST(NULL AS VARCHAR) AS grandparent_name +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_row_deciders.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_row_deciders.sql new file mode 100644 index 0000000000..7069767ef4 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_row_deciders.sql @@ -0,0 +1,321 @@ +TEST (name "int_edge_row_deciders__picks_baskets_by_columns_it_never_outputs"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_row_deciders AS ( + SELECT + 'except_limit:B-4:-' AS decider_row, + 'except_limit' AS decider, + 'B-4' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'exists_filter:B-1:-' AS decider_row, + 'exists_filter' AS decider, + 'B-1' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'exists_filter:B-4:-' AS decider_row, + 'exists_filter' AS decider, + 'B-4' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'exists_output:B-1:true' AS decider_row, + 'exists_output' AS decider, + 'B-1' AS basket_id, + 'true' AS detail + UNION ALL + SELECT + 'exists_output:B-2:false' AS decider_row, + 'exists_output' AS decider, + 'B-2' AS basket_id, + 'false' AS detail + UNION ALL + SELECT + 'exists_output:B-3:false' AS decider_row, + 'exists_output' AS decider, + 'B-3' AS basket_id, + 'false' AS detail + UNION ALL + SELECT + 'exists_output:B-4:true' AS decider_row, + 'exists_output' AS decider, + 'B-4' AS basket_id, + 'true' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-01#0' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-01#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-01#1' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-01#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-02#0' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-02#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-02#1' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-02#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-03#0' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-03#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-1:BL-03#1' AS decider_row, + 'flatten_input' AS decider, + 'B-1' AS basket_id, + 'BL-03#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-2:BL-04#0' AS decider_row, + 'flatten_input' AS decider, + 'B-2' AS basket_id, + 'BL-04#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-2:BL-04#1' AS decider_row, + 'flatten_input' AS decider, + 'B-2' AS basket_id, + 'BL-04#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-2:BL-05#0' AS decider_row, + 'flatten_input' AS decider, + 'B-2' AS basket_id, + 'BL-05#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-2:BL-05#1' AS decider_row, + 'flatten_input' AS decider, + 'B-2' AS basket_id, + 'BL-05#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-3:BL-06#0' AS decider_row, + 'flatten_input' AS decider, + 'B-3' AS basket_id, + 'BL-06#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-3:BL-06#1' AS decider_row, + 'flatten_input' AS decider, + 'B-3' AS basket_id, + 'BL-06#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-4:BL-07#0' AS decider_row, + 'flatten_input' AS decider, + 'B-4' AS basket_id, + 'BL-07#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-4:BL-07#1' AS decider_row, + 'flatten_input' AS decider, + 'B-4' AS basket_id, + 'BL-07#1' AS detail + UNION ALL + SELECT + 'flatten_input:B-4:BL-08#0' AS decider_row, + 'flatten_input' AS decider, + 'B-4' AS basket_id, + 'BL-08#0' AS detail + UNION ALL + SELECT + 'flatten_input:B-4:BL-08#1' AS decider_row, + 'flatten_input' AS decider, + 'B-4' AS basket_id, + 'BL-08#1' AS detail + UNION ALL + SELECT + 'group_by_all:B-1:3' AS decider_row, + 'group_by_all' AS decider, + 'B-1' AS basket_id, + '3' AS detail + UNION ALL + SELECT + 'group_by_all:B-2:2' AS decider_row, + 'group_by_all' AS decider, + 'B-2' AS basket_id, + '2' AS detail + UNION ALL + SELECT + 'group_by_all:B-3:1' AS decider_row, + 'group_by_all' AS decider, + 'B-3' AS basket_id, + '1' AS detail + UNION ALL + SELECT + 'group_by_all:B-4:2' AS decider_row, + 'group_by_all' AS decider, + 'B-4' AS basket_id, + '2' AS detail + UNION ALL + SELECT + 'group_by_position:B-1:-' AS decider_row, + 'group_by_position' AS decider, + 'B-1' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'group_by_position:B-2:-' AS decider_row, + 'group_by_position' AS decider, + 'B-2' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'group_by_position:B-4:-' AS decider_row, + 'group_by_position' AS decider, + 'B-4' AS basket_id, + CAST(NULL AS VARCHAR) AS detail + UNION ALL + SELECT + 'order_by_limit:B-1:BL-03' AS decider_row, + 'order_by_limit' AS decider, + 'B-1' AS basket_id, + 'BL-03' AS detail + UNION ALL + SELECT + 'qualify:B-1:BL-01' AS decider_row, + 'qualify' AS decider, + 'B-1' AS basket_id, + 'BL-01' AS detail + UNION ALL + SELECT + 'qualify:B-2:BL-04' AS decider_row, + 'qualify' AS decider, + 'B-2' AS basket_id, + 'BL-04' AS detail + UNION ALL + SELECT + 'qualify:B-3:BL-06' AS decider_row, + 'qualify' AS decider, + 'B-3' AS basket_id, + 'BL-06' AS detail + UNION ALL + SELECT + 'qualify:B-4:BL-07' AS decider_row, + 'qualify' AS decider, + 'B-4' AS basket_id, + 'BL-07' AS detail + UNION ALL + SELECT + 'rollup_position:-:1:~=8' AS decider_row, + 'rollup_position' AS decider, + CAST(NULL AS VARCHAR) AS basket_id, + '1:~=8' AS detail + UNION ALL + SELECT + 'rollup_position:B-1:0:bath=2' AS decider_row, + 'rollup_position' AS decider, + 'B-1' AS basket_id, + '0:bath=2' AS detail + UNION ALL + SELECT + 'rollup_position:B-1:0:gift=1' AS decider_row, + 'rollup_position' AS decider, + 'B-1' AS basket_id, + '0:gift=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-1:1:~=3' AS decider_row, + 'rollup_position' AS decider, + 'B-1' AS basket_id, + '1:~=3' AS detail + UNION ALL + SELECT + 'rollup_position:B-2:0:bath=1' AS decider_row, + 'rollup_position' AS decider, + 'B-2' AS basket_id, + '0:bath=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-2:0:garden=1' AS decider_row, + 'rollup_position' AS decider, + 'B-2' AS basket_id, + '0:garden=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-2:1:~=2' AS decider_row, + 'rollup_position' AS decider, + 'B-2' AS basket_id, + '1:~=2' AS detail + UNION ALL + SELECT + 'rollup_position:B-3:0:pond=1' AS decider_row, + 'rollup_position' AS decider, + 'B-3' AS basket_id, + '0:pond=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-3:1:~=1' AS decider_row, + 'rollup_position' AS decider, + 'B-3' AS basket_id, + '1:~=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-4:0:gift=1' AS decider_row, + 'rollup_position' AS decider, + 'B-4' AS basket_id, + '0:gift=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-4:0:~=1' AS decider_row, + 'rollup_position' AS decider, + 'B-4' AS basket_id, + '0:~=1' AS detail + UNION ALL + SELECT + 'rollup_position:B-4:1:~=2' AS decider_row, + 'rollup_position' AS decider, + 'B-4' AS basket_id, + '1:~=2' AS detail + UNION ALL + SELECT + 'where:B-1:BL-01' AS decider_row, + 'where' AS decider, + 'B-1' AS basket_id, + 'BL-01' AS detail + UNION ALL + SELECT + 'where:B-1:BL-03' AS decider_row, + 'where' AS decider, + 'B-1' AS basket_id, + 'BL-03' AS detail + UNION ALL + SELECT + 'where:B-2:BL-05' AS decider_row, + 'where' AS decider, + 'B-2' AS basket_id, + 'BL-05' AS detail +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_text_dates.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_text_dates.sql new file mode 100644 index 0000000000..bb3bddbcf2 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_text_dates.sql @@ -0,0 +1,268 @@ +TEST (name "int_edge_text_dates__reads_numeric_and_month_name_formats"); + +WITH __ref__stg_edge_date_texts AS ( + SELECT + v.text_id, + v.date_text, + NULL AS zoned_text, + NULL::TIMESTAMP_NTZ(3) AS wall_clock, + NULL AS wall_clock_text, + NULL::TIME(3) AS local_time, + NULL::TIME(9) AS fine_time + FROM VALUES + ('DT-01', '5.3.2026'), + ('DT-02', '05.03.26'), + ('DT-03', '2026-3-5'), + ('DT-04', '05/03/26'), + ('DT-05', '5/3/26'), + ('DT-06', '03/05/2026'), + ('DT-07', '2026-10-05T10:00'), + ('DT-08', '2025-10-05Z'), + ('DT-09', '2025-10-05t'), + ('DT-10', '2025-10-05x'), + ('DT-11', '2025-10-05T10:00x'), + ('DT-12', '05 Augu\u017ft 2024'), + ('DT-13', '05 August 2024'), + ('DT-14', '05-Mar/2024'), + ('DT-15', '05Mar 2024'), + ('DT-16', '05 Mar2024'), + ('DT-17', ' 05 Mar 2024 '), + ('DT-18', 'March 05 2024'), + ('DT-19', '5 Mar 20245'), + ('DT-20', 'March 005 2024'), + ('DT-21', '15 oktober 2025'), + ('DT-22', '2026-02-30'), + ('DT-23', NULL) AS v(text_id, date_text) +), + +__expected__int_edge_text_dates AS ( + SELECT + 'DT-01' AS text_id, + '2026-03-05'::DATE AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-02' AS text_id, + '0026-03-05'::DATE AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-03' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + '2026-03-05'::DATE AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + '2026-03-05'::DATE AS auto_date + UNION ALL + SELECT + 'DT-04' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + '2026-03-05'::DATE AS day_month_short_year, + '0026-05-03'::DATE AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + '0026-05-03'::DATE AS auto_date + UNION ALL + SELECT + 'DT-05' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + '2026-03-05'::DATE AS day_month_short_year, + '0026-05-03'::DATE AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + '0026-05-03'::DATE AS auto_date + UNION ALL + SELECT + 'DT-06' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + '2026-03-05'::DATE AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + '2026-03-05'::DATE AS auto_date + UNION ALL + SELECT + 'DT-07' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + '2026-10-05'::DATE AS auto_date + UNION ALL + SELECT + 'DT-08' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-09' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-10' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-11' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-12' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-13' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + '2024-08-05'::DATE AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-14' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-15' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + '2024-03-05'::DATE AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-16' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + '2024-03-05'::DATE AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-17' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + '2024-03-05'::DATE AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-18' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + '2024-03-05'::DATE AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-19' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-20' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-21' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-22' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date + UNION ALL + SELECT + 'DT-23' AS text_id, + CAST(NULL AS DATE) AS day_month_dots, + CAST(NULL AS DATE) AS iso_date, + CAST(NULL AS DATE) AS day_month_short_year, + CAST(NULL AS DATE) AS month_day_slashes, + CAST(NULL AS DATE) AS day_month_name, + CAST(NULL AS DATE) AS month_name_first, + CAST(NULL AS DATE) AS auto_date +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_ticket_tokens.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_ticket_tokens.sql new file mode 100644 index 0000000000..0764ffa03f --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_ticket_tokens.sql @@ -0,0 +1,86 @@ +TEST (name "int_edge_ticket_tokens__calls_volatile_functions_once_per_row"); + +WITH __ref__stg_edge_basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', 'bath'), + ('BL-02', 'B-1', 2, 'TEAL-02', 'gift'), + ('BL-03', 'B-1', 3, 'EIDER-03', 'bath'), + ('BL-04', 'B-2', 1, 'PINTAIL-04', 'garden'), + ('BL-05', 'B-2', 2, 'MALLARD-01', 'bath'), + ('BL-06', 'B-3', 1, 'MERGANSER-05', 'pond'), + ('BL-07', 'B-4', 1, 'TEAL-02', NULL), + ('BL-08', 'B-4', 2, 'EIDER-03', 'gift') AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__int_edge_ticket_tokens AS ( + SELECT + 'BL-01' AS line_id, + 'BL-01' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Bath' AS tag_label + UNION ALL + SELECT + 'BL-02' AS line_id, + 'BL-02' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Gift' AS tag_label + UNION ALL + SELECT + 'BL-03' AS line_id, + 'BL-03' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Bath' AS tag_label + UNION ALL + SELECT + 'BL-04' AS line_id, + 'BL-04' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Garden' AS tag_label + UNION ALL + SELECT + 'BL-05' AS line_id, + 'BL-05' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Bath' AS tag_label + UNION ALL + SELECT + 'BL-06' AS line_id, + 'BL-06' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Pond' AS tag_label + UNION ALL + SELECT + 'BL-07' AS line_id, + 'BL-07' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Untagged' AS tag_label + UNION ALL + SELECT + 'BL-08' AS line_id, + 'BL-08' AS edge_next_ticket_source, + 'edge_next_ticket edge_next_ticket' AS look_alike_text, + 'Gift' AS tag_label +), + +__assert__every_call_gives_a_new_value AS ( + SELECT COUNT(*) AS line_count + FROM __ref("int_edge_ticket_tokens") AS t + HAVING + COUNT(DISTINCT t.line_token) <> COUNT(*) + OR COUNT(DISTINCT t.constant_seed_token) <> COUNT(*) + OR COUNT(DISTINCT t.next_ticket) <> COUNT(*) +), + +__assert__half_tickets_are_positive AS ( + SELECT t.line_id + FROM __ref("int_edge_ticket_tokens") AS t + WHERE + t.half_ticket IS NULL + OR t.half_ticket <= 0 +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_extremes.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_extremes.sql new file mode 100644 index 0000000000..ac8b111d7c --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_extremes.sql @@ -0,0 +1,290 @@ +TEST (name "int_edge_variant_extremes__orders_mixed_variant_values"); + +WITH __ref__stg_edge_variant_values AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key::NUMBER(38, 0) AS sort_key, + IFF( + v.attribute_value = '@date', + TO_VARIANT('2026-03-01'::DATE), + PARSE_JSON(v.attribute_value) + ) AS attribute_value, + v.value_text + FROM VALUES + ('VV-01', 'V-1', 1, '[1, "a", 2]', NULL), + ('VV-02', 'V-1', 2, '[1, null, 3]', NULL), + ('VV-03', 'V-1', 3, '[true, 2]', NULL), + ('VV-04', 'V-2', 1, '["a", "B", "b"]', NULL), + ('VV-05', 'V-2', 2, '[12345678901234567890, 1]', NULL), + ('VV-06', 'V-2', 3, '[9007199254740993, 9007199254740992]', NULL), + ('VV-07', 'V-3', 1, '[-0.0, 0]', NULL), + ('VV-08', 'V-3', 2, '[1e2, 99]', NULL), + ('VV-09', 'V-3', 3, '[]', NULL), + ('VV-10', 'V-4', 1, 'null', NULL), + ('VV-11', 'V-4', 2, '5', NULL), + ('VV-12', 'V-4', 3, '"x"', NULL), + ('VV-13', 'V-4', 4, '{"a": 1}', NULL), + ('VV-14', 'V-5', 1, '["\u00e9", "z", "b", "B"]', NULL), + ('VV-15', 'V-5', 2, '"\u00e9"', NULL), + ('VV-16', 'V-5', 3, '"z"', NULL), + ('VV-17', 'V-5', 4, '"B"', NULL), + ('VV-18', 'V-6', 1, '-7', NULL), + ('VV-19', 'V-6', 2, '1', NULL), + ('VV-20', 'V-6', 3, '"1"', NULL), + ('VV-21', 'V-6', 4, '0', NULL) AS v(value_id, group_code, sort_key, attribute_value, value_text) +), + +__expected__int_edge_variant_extremes AS ( + SELECT + 'VV-01' AS value_id, + PARSE_JSON('"a"') AS largest_element, + PARSE_JSON('1') AS smallest_element, + PARSE_JSON('[2,"a",1]')::ARRAY AS reversed_elements, + PARSE_JSON('[1,null,3]') AS group_largest, + PARSE_JSON('[1,"a",2]')::ARRAY AS without_json_nulls, + 3 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-02' AS value_id, + PARSE_JSON('null') AS largest_element, + PARSE_JSON('1') AS smallest_element, + PARSE_JSON('[3,null,1]')::ARRAY AS reversed_elements, + PARSE_JSON('[1,null,3]') AS group_largest, + PARSE_JSON('[1,3]')::ARRAY AS without_json_nulls, + 3 AS distinct_element_count, + TRUE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-03' AS value_id, + PARSE_JSON('2') AS largest_element, + PARSE_JSON('true') AS smallest_element, + PARSE_JSON('[2,true]')::ARRAY AS reversed_elements, + PARSE_JSON('[1,null,3]') AS group_largest, + PARSE_JSON('[true,2]')::ARRAY AS without_json_nulls, + 2 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-04' AS value_id, + PARSE_JSON('"b"') AS largest_element, + PARSE_JSON('"B"') AS smallest_element, + PARSE_JSON('["b","B","a"]')::ARRAY AS reversed_elements, + PARSE_JSON('["a","B","b"]') AS group_largest, + PARSE_JSON('["a","B","b"]')::ARRAY AS without_json_nulls, + 3 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-05' AS value_id, + PARSE_JSON('12345678901234567890') AS largest_element, + PARSE_JSON('1') AS smallest_element, + PARSE_JSON('[1,12345678901234567890]')::ARRAY AS reversed_elements, + PARSE_JSON('["a","B","b"]') AS group_largest, + PARSE_JSON('[12345678901234567890,1]')::ARRAY AS without_json_nulls, + 2 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-06' AS value_id, + PARSE_JSON('9007199254740993') AS largest_element, + PARSE_JSON('9007199254740992') AS smallest_element, + PARSE_JSON('[9007199254740992,9007199254740993]')::ARRAY AS reversed_elements, + PARSE_JSON('["a","B","b"]') AS group_largest, + PARSE_JSON('[9007199254740993,9007199254740992]')::ARRAY AS without_json_nulls, + 2 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-07' AS value_id, + PARSE_JSON('0') AS largest_element, + PARSE_JSON('0') AS smallest_element, + PARSE_JSON('[0,0]')::ARRAY AS reversed_elements, + PARSE_JSON('[1.000000000000000e+02,99]') AS group_largest, + PARSE_JSON('[0,0]')::ARRAY AS without_json_nulls, + 1 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-08' AS value_id, + PARSE_JSON('1.000000000000000e+02') AS largest_element, + PARSE_JSON('99') AS smallest_element, + PARSE_JSON('[99,1.000000000000000e+02]')::ARRAY AS reversed_elements, + PARSE_JSON('[1.000000000000000e+02,99]') AS group_largest, + PARSE_JSON('[1.000000000000000e+02,99]')::ARRAY AS without_json_nulls, + 2 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-09' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + PARSE_JSON('[]')::ARRAY AS reversed_elements, + PARSE_JSON('[1.000000000000000e+02,99]') AS group_largest, + PARSE_JSON('[]')::ARRAY AS without_json_nulls, + 0 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-10' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('null') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-11' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('null') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-12' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('null') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-13' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('null') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-14' AS value_id, + PARSE_JSON('"é"') AS largest_element, + PARSE_JSON('"B"') AS smallest_element, + PARSE_JSON('["B","b","z","é"]')::ARRAY AS reversed_elements, + PARSE_JSON('["é","z","b","B"]') AS group_largest, + PARSE_JSON('["é","z","b","B"]')::ARRAY AS without_json_nulls, + 4 AS distinct_element_count, + FALSE AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-15' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('["é","z","b","B"]') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-16' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('["é","z","b","B"]') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-17' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('["é","z","b","B"]') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-18' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('"1"') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + TRUE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-19' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('"1"') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + TRUE AS in_decimal_array, + 1 AS decimal_array_position + UNION ALL + SELECT + 'VV-20' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('"1"') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + CAST(NULL AS NUMBER(38, 0)) AS decimal_array_position + UNION ALL + SELECT + 'VV-21' AS value_id, + CAST(NULL AS VARIANT) AS largest_element, + CAST(NULL AS VARIANT) AS smallest_element, + CAST(NULL AS ARRAY) AS reversed_elements, + PARSE_JSON('"1"') AS group_largest, + CAST(NULL AS ARRAY) AS without_json_nulls, + CAST(NULL AS NUMBER(38, 0)) AS distinct_element_count, + CAST(NULL AS BOOLEAN) AS overlaps_json_null, + FALSE AS in_decimal_array, + 2 AS decimal_array_position +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_matches.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_matches.sql new file mode 100644 index 0000000000..1780b603ea --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_variant_matches.sql @@ -0,0 +1,630 @@ +TEST (name "int_edge_variant_matches__compares_variants_by_type_and_value"); + +WITH __ref__stg_edge_variant_values AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key::NUMBER(38, 0) AS sort_key, + IFF( + v.attribute_value = '@date', + TO_VARIANT('2026-03-01'::DATE), + PARSE_JSON(v.attribute_value) + ) AS attribute_value, + v.value_text + FROM VALUES + ('VV-01', 'V-1', 1, '1', 'one'), + ('VV-02', 'V-1', 2, '"1"', 'one as text'), + ('VV-03', 'V-1', 3, '1.0', 'one with a fraction'), + ('VV-04', 'V-1', 4, 'null', 'json null'), + ('VV-05', 'V-2', 1, '{"a": 1, "b": 2}', 'keys a, b'), + ('VV-06', 'V-2', 2, '{"b": 2, "a": 1}', 'keys b, a'), + ('VV-07', 'V-2', 3, '"2026-03-01"', 'date text'), + ('VV-08', 'V-2', 4, '"2026-03-01 10:15:00"', 'timestamp text'), + ('VV-09', 'V-3', 1, '[1, 2]', 'array'), + ('VV-10', 'V-3', 2, '[2, 1]', 'reversed array'), + ('VV-11', 'V-4', 1, '@date', 'stored date'), + ('VV-12', 'V-4', 2, '"2026-03-01"', 'date text') AS v(value_id, group_code, sort_key, attribute_value, value_text) +), + +__expected__int_edge_variant_matches AS ( + SELECT + 'VV-01~VV-02' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":1,"VALUE_ID":"VV-01"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-01~VV-02-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":1,"VALUE_ID":"VV-01"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-01~VV-02-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":1,"VALUE_ID":"VV-01"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-01~VV-03' AS pair_id, + TRUE AS values_equal, + TRUE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":1,"VALUE_ID":"VV-01"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-01~VV-04' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":1,"VALUE_ID":"VV-01"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-02-DATE~VV-02-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1970-01-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"1970-01-01","GROUP_CODE":"V-1","SORT_KEY":102,"VALUE_ID":"VV-02-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-02~VV-02-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"1","GROUP_CODE":"V-1","SORT_KEY":2,"VALUE_ID":"VV-02"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-02~VV-02-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"1","GROUP_CODE":"V-1","SORT_KEY":2,"VALUE_ID":"VV-02"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-02~VV-03' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"1","GROUP_CODE":"V-1","SORT_KEY":2,"VALUE_ID":"VV-02"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-02~VV-04' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"1","GROUP_CODE":"V-1","SORT_KEY":2,"VALUE_ID":"VV-02"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-03~VV-02-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":3,"VALUE_ID":"VV-03"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-03~VV-02-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":3,"VALUE_ID":"VV-03"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-03~VV-04' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + '1' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":1,"GROUP_CODE":"V-1","SORT_KEY":3,"VALUE_ID":"VV-03"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-04~VV-02-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":null,"GROUP_CODE":"V-1","SORT_KEY":4,"VALUE_ID":"VV-04"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-04~VV-02-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'INTEGER 1' AS group_first_value, + 'TIMESTAMP_NTZ "1970-01-01 00:00:01.000"' AS group_last_value, + 4 AS group_distinct_rows, + 5 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":null,"GROUP_CODE":"V-1","SORT_KEY":4,"VALUE_ID":"VV-04"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-06' AS pair_id, + TRUE AS values_equal, + TRUE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-07' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-07-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-08' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-08-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-05~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":1,"VALUE_ID":"VV-05"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-07' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-07-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-08' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-08-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-06~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":{"a":1,"b":2},"GROUP_CODE":"V-2","SORT_KEY":2,"VALUE_ID":"VV-06"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07-DATE~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":103,"VALUE_ID":"VV-07-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07-DATE~VV-08-DATE' AS pair_id, + TRUE AS values_equal, + TRUE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":103,"VALUE_ID":"VV-07-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07-DATE~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":103,"VALUE_ID":"VV-07-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07-TIMESTAMP~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01 00:00:00.000' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01 00:00:00.000","GROUP_CODE":"V-2","SORT_KEY":203,' + || '"VALUE_ID":"VV-07-TIMESTAMP"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07~VV-07-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":3,"VALUE_ID":"VV-07"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":3,"VALUE_ID":"VV-07"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07~VV-08' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":3,"VALUE_ID":"VV-07"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07~VV-08-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":3,"VALUE_ID":"VV-07"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-07~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":3,"VALUE_ID":"VV-07"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08-DATE~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":104,"VALUE_ID":"VV-08-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08-DATE~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-2","SORT_KEY":104,"VALUE_ID":"VV-08-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08~VV-07-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01 10:15:00' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01 10:15:00","GROUP_CODE":"V-2","SORT_KEY":4,"VALUE_ID":"VV-08"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08~VV-07-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01 10:15:00' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01 10:15:00","GROUP_CODE":"V-2","SORT_KEY":4,"VALUE_ID":"VV-08"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08~VV-08-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01 10:15:00' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01 10:15:00","GROUP_CODE":"V-2","SORT_KEY":4,"VALUE_ID":"VV-08"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-08~VV-08-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'OBJECT {"a":1,"b":2}' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 10:15:00.000"' AS group_last_value, + 6 AS group_distinct_rows, + 6 AS group_distinct_elements, + '2026-03-01 10:15:00' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01 10:15:00","GROUP_CODE":"V-2","SORT_KEY":4,"VALUE_ID":"VV-08"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-09~VV-10' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'ARRAY [1,2]' AS group_first_value, + 'ARRAY [2,1]' AS group_last_value, + 2 AS group_distinct_rows, + 2 AS group_distinct_elements, + CAST(NULL AS VARCHAR) AS left_scalar_text, + '{"ATTRIBUTE_VALUE":[1,2],"GROUP_CODE":"V-3","SORT_KEY":1,"VALUE_ID":"VV-09"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-11~VV-12' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":1,"VALUE_ID":"VV-11"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-11~VV-12-DATE' AS pair_id, + TRUE AS values_equal, + TRUE AS values_equal_null, + 2 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":1,"VALUE_ID":"VV-11"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-11~VV-12-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":1,"VALUE_ID":"VV-11"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-12-DATE~VV-12-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 2 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":102,"VALUE_ID":"VV-12-DATE"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-12~VV-12-DATE' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":2,"VALUE_ID":"VV-12"}' AS left_object_without_note + UNION ALL + SELECT + 'VV-12~VV-12-TIMESTAMP' AS pair_id, + FALSE AS values_equal, + FALSE AS values_equal_null, + 1 AS left_partition_size, + 'DATE "2026-03-01"' AS group_first_value, + 'TIMESTAMP_NTZ "2026-03-01 00:00:00.000"' AS group_last_value, + 3 AS group_distinct_rows, + 3 AS group_distinct_elements, + '2026-03-01' AS left_scalar_text, + '{"ATTRIBUTE_VALUE":"2026-03-01","GROUP_CODE":"V-4","SORT_KEY":2,"VALUE_ID":"VV-12"}' AS left_object_without_note +) diff --git a/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_zoned_texts.sql b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_zoned_texts.sql new file mode 100644 index 0000000000..df0676a7cd --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/intermediate/edge_cases/test_int_edge_zoned_texts.sql @@ -0,0 +1,213 @@ +TEST (name "int_edge_zoned_texts__reads_offsets_back_as_utc_instants_dates_and_years"); + +WITH __ref__stg_edge_date_texts AS ( + SELECT + v.text_id, + NULL AS date_text, + v.zoned_text, + v.wall_clock::TIMESTAMP_NTZ(3) AS wall_clock, + v.wall_clock_text, + NULL::TIME(3) AS local_time, + NULL::TIME(9) AS fine_time + FROM VALUES + ('ZT-01', '2026-03-06T08:30:00+09:00', '2026-03-05 23:30:00.000', '2026-03-05 23:30:00'), + ('ZT-02', '2026-03-05T23:30:00Z', '2026-03-05 23:00:00.000', '2026-03-05 23:30:00'), + ('ZT-03', '2026-03-05 23:30:00+00:00', '2026-03-06 00:00:00.000', '2026-03-05 23:30:00'), + ('ZT-04', '2026-03-05 23:30:00 +0000', '2026-03-05 23:30:00.000', '2026-03-05 23:30:00.000001'), + ('ZT-05', '2026-03-05T18:00:00-05:30', '2026-03-05 23:29:59.999', '2026-03-05 23:30:00'), + ('ZT-06', '2026-12-31T23:30:00-05:30', '2027-01-01 00:00:00.000', '2027-01-01 05:00:00'), + ('ZT-07', '2026-03-05T23:30:00', '2026-03-05 23:30:00.000', '2026-03-05 23:30:00'), + ('ZT-08', NULL, '1969-12-31 23:59:59.999', '1969-12-31 23:59:59.999'), + ('ZT-09', 'not a time', NULL, NULL) AS v(text_id, zoned_text, wall_clock, wall_clock_text) +), + +__expected__int_edge_zoned_texts AS ( + SELECT + 'ZT-01' AS text_id, + '2026-03-06 08:30:00 +09:00'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-06'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-06 00:00:00 +09:00'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + FALSE AS zoned_after_wall_clock, + FALSE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-02' AS text_id, + '2026-03-05 23:30:00 Z'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-05'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-05 00:00:00 Z'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + TRUE AS zoned_after_wall_clock, + TRUE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-03' AS text_id, + '2026-03-05 23:30:00 Z'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-05'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-05 00:00:00 Z'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + FALSE AS zoned_after_wall_clock, + FALSE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 0 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-04' AS text_id, + '2026-03-05 23:30:00 Z'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-05'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-05 00:00:00 Z'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + FALSE AS zoned_equals_wall_text, + FALSE AS local_zoned_equals_wall_text, + FALSE AS zoned_after_wall_clock, + FALSE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-05' AS text_id, + '2026-03-05 18:00:00 -05:30'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-05'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-05 00:00:00 -05:30'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + TRUE AS zoned_after_wall_clock, + TRUE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-06' AS text_id, + '2026-12-31 23:30:00 -05:30'::TIMESTAMP_TZ(9) AS zoned, + '2027-01-01 05:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2027-01-01 05:00:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2027-01-01 05:00:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-12-31'::DATE AS zoned_date, + '2027-01-01'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2027 AS local_zoned_year, + '2026-12-31 00:00:00 -05:30'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2027-01-01 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + TRUE AS zoned_after_wall_clock, + TRUE AS local_zoned_after_wall_clock, + TRUE AS zoned_from_next_day, + TRUE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 0 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-07' AS text_id, + '2026-03-05 23:30:00 Z'::TIMESTAMP_TZ(9) AS zoned, + '2026-03-05 23:30:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS zoned_utc, + '2026-03-05 23:30:00'::TIMESTAMP_NTZ(9) AS local_zoned_utc, + '2026-03-05'::DATE AS zoned_date, + '2026-03-05'::DATE AS local_zoned_date, + 2026 AS zoned_year, + 2026 AS local_zoned_year, + '2026-03-05 00:00:00 Z'::TIMESTAMP_TZ(9) AS zoned_day_start, + '2026-03-05 00:00:00 Z'::TIMESTAMP_LTZ(9) AS local_zoned_day_start, + TRUE AS zoned_equals_wall_text, + TRUE AS local_zoned_equals_wall_text, + FALSE AS zoned_after_wall_clock, + FALSE AS local_zoned_after_wall_clock, + FALSE AS zoned_from_next_day, + FALSE AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-08' AS text_id, + CAST(NULL AS TIMESTAMP_TZ(9)) AS zoned, + CAST(NULL AS TIMESTAMP_LTZ(9)) AS local_zoned, + CAST(NULL AS TIMESTAMP_NTZ(9)) AS zoned_utc, + CAST(NULL AS TIMESTAMP_NTZ(9)) AS local_zoned_utc, + CAST(NULL AS DATE) AS zoned_date, + CAST(NULL AS DATE) AS local_zoned_date, + CAST(NULL AS NUMBER(38, 0)) AS zoned_year, + CAST(NULL AS NUMBER(38, 0)) AS local_zoned_year, + CAST(NULL AS TIMESTAMP_TZ(9)) AS zoned_day_start, + CAST(NULL AS TIMESTAMP_LTZ(9)) AS local_zoned_day_start, + CAST(NULL AS BOOLEAN) AS zoned_equals_wall_text, + CAST(NULL AS BOOLEAN) AS local_zoned_equals_wall_text, + CAST(NULL AS BOOLEAN) AS zoned_after_wall_clock, + CAST(NULL AS BOOLEAN) AS local_zoned_after_wall_clock, + CAST(NULL AS BOOLEAN) AS zoned_from_next_day, + CAST(NULL AS BOOLEAN) AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + 23 AS wall_clock_utc_hour + UNION ALL + SELECT + 'ZT-09' AS text_id, + CAST(NULL AS TIMESTAMP_TZ(9)) AS zoned, + CAST(NULL AS TIMESTAMP_LTZ(9)) AS local_zoned, + CAST(NULL AS TIMESTAMP_NTZ(9)) AS zoned_utc, + CAST(NULL AS TIMESTAMP_NTZ(9)) AS local_zoned_utc, + CAST(NULL AS DATE) AS zoned_date, + CAST(NULL AS DATE) AS local_zoned_date, + CAST(NULL AS NUMBER(38, 0)) AS zoned_year, + CAST(NULL AS NUMBER(38, 0)) AS local_zoned_year, + CAST(NULL AS TIMESTAMP_TZ(9)) AS zoned_day_start, + CAST(NULL AS TIMESTAMP_LTZ(9)) AS local_zoned_day_start, + CAST(NULL AS BOOLEAN) AS zoned_equals_wall_text, + CAST(NULL AS BOOLEAN) AS local_zoned_equals_wall_text, + CAST(NULL AS BOOLEAN) AS zoned_after_wall_clock, + CAST(NULL AS BOOLEAN) AS local_zoned_after_wall_clock, + CAST(NULL AS BOOLEAN) AS zoned_from_next_day, + CAST(NULL AS BOOLEAN) AS local_zoned_from_next_day, + '2026-03-06'::DATE AS first_zoned_date, + CAST(NULL AS NUMBER(38, 0)) AS wall_clock_utc_hour +) diff --git a/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_amount_checks.sql b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_amount_checks.sql new file mode 100644 index 0000000000..4c5572471d --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_amount_checks.sql @@ -0,0 +1,34 @@ +TEST (name "stg_edge_amount_checks__keeps_key_widths_and_scales"); + +WITH __source__edge__amount_checks AS ( + SELECT + v.check_id, + v.group_code, + v.narrow_key::NUMBER(18, 0) AS narrow_key, + v.wide_key::NUMBER(38, 0) AS wide_key, + v.key_text, + v.amount::NUMBER(18, 2) AS amount, + v.fine_amount::NUMBER(20, 15) AS fine_amount, + v.ratio::FLOAT AS ratio, + v.json_text, + '2026-03-06 00:00:00'::TIMESTAMP_NTZ AS _loaded_at + FROM VALUES + ('AC-01', 'G-1', 7, '12345678901234567890123', '01', 10.00, 0.123456789012345, 0.29, '1.0'), + ('AC-02', 'G-2', NULL, NULL, NULL, NULL, NULL, NULL, NULL) AS v(check_id, group_code, narrow_key, wide_key, key_text, amount, fine_amount, ratio, json_text) +), + +__expected__stg_edge_amount_checks AS ( + SELECT + 'AC-01' AS check_id, + 7 AS narrow_key, + 12345678901234567890123 AS wide_key, + 10.00::NUMBER(18, 2) AS amount, + 0.123456789012345::NUMBER(20, 15) AS fine_amount + UNION ALL + SELECT + 'AC-02' AS check_id, + CAST(NULL AS NUMBER(18, 0)) AS narrow_key, + CAST(NULL AS NUMBER(38, 0)) AS wide_key, + CAST(NULL AS NUMBER(18, 2)) AS amount, + CAST(NULL AS NUMBER(20, 15)) AS fine_amount +) diff --git a/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_basket_lines.sql b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_basket_lines.sql new file mode 100644 index 0000000000..5e6717a96a --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_basket_lines.sql @@ -0,0 +1,35 @@ +TEST (name "stg_edge_basket_lines__lower_cases_and_trims_tags"); + +WITH __source__edge__basket_lines AS ( + SELECT + v.line_id, + v.basket_id, + v.line_no::NUMBER(38, 0) AS line_no, + v.sku, + v.tag, + '2026-03-06 00:00:00'::TIMESTAMP_NTZ AS _loaded_at + FROM VALUES + ('BL-01', 'B-1', 1, 'MALLARD-01', ' Bath '), + ('BL-02', 'B-1', 2, 'TEAL-02', 'GIFT'), + ('BL-03', 'B-2', 1, 'EIDER-03', NULL) AS v(line_id, basket_id, line_no, sku, tag) +), + +__expected__stg_edge_basket_lines AS ( + SELECT + 'BL-01' AS line_id, + 'B-1' AS basket_id, + 1 AS line_no, + 'bath' AS tag + UNION ALL + SELECT + 'BL-02' AS line_id, + 'B-1' AS basket_id, + 2 AS line_no, + 'gift' AS tag + UNION ALL + SELECT + 'BL-03' AS line_id, + 'B-2' AS basket_id, + 1 AS line_no, + CAST(NULL AS VARCHAR) AS tag +) diff --git a/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_category_links.sql b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_category_links.sql new file mode 100644 index 0000000000..f5e45a2ddb --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_category_links.sql @@ -0,0 +1,27 @@ +TEST (name "stg_edge_category_links__passes_links_through"); + +WITH __source__edge__category_links AS ( + SELECT + v.category_id, + v.parent_id, + v.category_name, + v.shelf_depth::NUMBER(38, 0) AS shelf_depth, + '2026-03-06 00:00:00'::TIMESTAMP_NTZ AS _loaded_at + FROM VALUES + ('CAT-1', NULL, 'Ducks', 1), + ('CAT-2', 'CAT-1', 'Rubber ducks', 2) AS v(category_id, parent_id, category_name, shelf_depth) +), + +__expected__stg_edge_category_links AS ( + SELECT + 'CAT-1' AS category_id, + CAST(NULL AS VARCHAR) AS parent_id, + 'Ducks' AS category_name, + 1 AS shelf_depth + UNION ALL + SELECT + 'CAT-2' AS category_id, + 'CAT-1' AS parent_id, + 'Rubber ducks' AS category_name, + 2 AS shelf_depth +) diff --git a/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_date_texts.sql b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_date_texts.sql new file mode 100644 index 0000000000..f68b146607 --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_date_texts.sql @@ -0,0 +1,36 @@ +TEST (name "stg_edge_date_texts__keeps_clock_precisions"); + +WITH __source__edge__date_texts AS ( + SELECT + v.text_id, + v.date_text, + v.zoned_text, + v.wall_clock::TIMESTAMP_NTZ AS wall_clock, + v.wall_clock_text, + v.local_time::TIME AS local_time, + v.fine_time::TIME AS fine_time, + '2026-03-06 00:00:00'::TIMESTAMP_NTZ AS _loaded_at + FROM VALUES + ('DT-01', '5.3.2026', '2026-03-05T23:30:00Z', '2026-03-05 23:30:00.123', '2026-03-05 23:30:00.1239', '23:59:59.999', '23:59:59.9999999'), + ('DT-02', NULL, NULL, NULL, NULL, NULL, NULL) AS v(text_id, date_text, zoned_text, wall_clock, wall_clock_text, local_time, fine_time) +), + +__expected__stg_edge_date_texts AS ( + SELECT + 'DT-01' AS text_id, + '5.3.2026' AS date_text, + '2026-03-05T23:30:00Z' AS zoned_text, + '2026-03-05 23:30:00.123'::TIMESTAMP_NTZ(3) AS wall_clock, + '2026-03-05 23:30:00.1239' AS wall_clock_text, + '23:59:59.999'::TIME(3) AS local_time, + '23:59:59.9999999'::TIME(9) AS fine_time + UNION ALL + SELECT + 'DT-02' AS text_id, + CAST(NULL AS VARCHAR) AS date_text, + CAST(NULL AS VARCHAR) AS zoned_text, + CAST(NULL AS TIMESTAMP_NTZ(3)) AS wall_clock, + CAST(NULL AS VARCHAR) AS wall_clock_text, + CAST(NULL AS TIME(3)) AS local_time, + CAST(NULL AS TIME(9)) AS fine_time +) diff --git a/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_variant_values.sql b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_variant_values.sql new file mode 100644 index 0000000000..b94bf09cca --- /dev/null +++ b/benchmarks/duckshop/project/tests/unit/staging/edge_cases/test_stg_edge_variant_values.sql @@ -0,0 +1,29 @@ +TEST (name "stg_edge_variant_values__turns_missing_values_into_json_null"); + +WITH __source__edge__variant_values AS ( + SELECT + v.value_id, + v.group_code, + v.sort_key::NUMBER(38, 0) AS sort_key, + PARSE_JSON(v.attribute_value) AS attribute_value, + v.value_text, + '2026-03-06 00:00:00'::TIMESTAMP_NTZ AS _loaded_at + FROM VALUES + ('VV-01', 'V-1', 1, '{"b": 2, "a": 1}', '{"b": 2, "a": 1}'), + ('VV-02', 'V-1', 2, 'null', 'null'), + ('VV-03', 'V-1', 3, NULL, NULL) AS v(value_id, group_code, sort_key, attribute_value, value_text) +), + +__expected__stg_edge_variant_values AS ( + SELECT + 'VV-01' AS value_id, + PARSE_JSON('{"a":1,"b":2}') AS attribute_value + UNION ALL + SELECT + 'VV-02' AS value_id, + PARSE_JSON('null') AS attribute_value + UNION ALL + SELECT + 'VV-03' AS value_id, + PARSE_JSON('null') AS attribute_value +) diff --git a/benchmarks/duckshop/tests/test_synthetic_data.py b/benchmarks/duckshop/tests/test_synthetic_data.py index 1d8ff54725..1dd7bf07b7 100644 --- a/benchmarks/duckshop/tests/test_synthetic_data.py +++ b/benchmarks/duckshop/tests/test_synthetic_data.py @@ -477,6 +477,11 @@ def test_given_any_data_scale_when_building_edge_rows_then_they_are_fixed(scale: "edge__ticket_notes": _edge_cases.ticket_notes(), "edge__partner_listings": _edge_cases.partner_listings(), "edge__delivery_updates": _edge_cases.delivery_updates(), + "edge__basket_lines": _edge_cases.basket_lines(), + "edge__date_texts": _edge_cases.date_texts(), + "edge__amount_checks": _edge_cases.amount_checks(), + "edge__variant_values": _edge_cases.variant_values(), + "edge__category_links": _edge_cases.category_links(), } assert {name: _digest(rows) for name, rows in rebuilt.items()} == {