From a20177bba37c5e6d616689cbb5a8eda16b9e5c28 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 05:03:19 +0000 Subject: [PATCH 1/3] test: record PIVOT without an alias list and TABLE(FLATTEN(...)) as dbt models write them --- cases/relational/pivot_unaliased.toml | 60 ++++++++++++++++++++++++ cases/semi-structured/table_flatten.toml | 33 +++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 cases/relational/pivot_unaliased.toml create mode 100644 cases/semi-structured/table_flatten.toml diff --git a/cases/relational/pivot_unaliased.toml b/cases/relational/pivot_unaliased.toml new file mode 100644 index 000000000..8597d8939 --- /dev/null +++ b/cases/relational/pivot_unaliased.toml @@ -0,0 +1,60 @@ +# PIVOT without an alias column list: the names Snowflake gives the key columns, as dbt pivot +# models leave them. +category = "relational" +construct = "node:pivot" +session_profile = "utc-v1" + +[[case]] +id = "pivot_star_without_alias_list" +description = "SELECT * over SUM(total) FOR channel IN ('web', 'store') with no alias list. Catches: key columns named web or WEB instead of the quoted text, a missing or NULL key, keys outside the list dropped, and the grouping column kept." +columns = [{ name = "order_id", type = "NUMBER(38,0)" }, { name = "channel", type = "VARCHAR" }, { name = "total", type = "NUMBER(12,2)" }] +rows = [ + ["1", "'web'", "10.50"], + ["1", "'web'", "2.25"], + ["1", "'store'", "4.00"], + ["2", "'store'", "NULL"], + ["2", "'phone'", "9.99"], + ["3", "NULL", "1.00"], + ["3", "'WEB'", "3.00"], +] +query = "SELECT * FROM (SELECT order_id, channel, total FROM input) PIVOT(SUM(total) FOR channel IN ('web', 'store')) ORDER BY order_id" +order = "ordered" +tags = ["null", "duplicates", "missing-key", "case-sensitive-key"] + +[[case]] +id = "pivot_quoted_key_reference" +description = "The pivoted key columns read back by their quoted names, \"'web'\" and \"'store'\", through a table alias without a column list. Catches: the names not matching the quoted key text." +columns = [{ name = "order_id", type = "NUMBER(38,0)" }, { name = "channel", type = "VARCHAR" }, { name = "total", type = "NUMBER(12,2)" }] +rows = [ + ["1", "'web'", "10.50"], + ["1", "'store'", "4.00"], + ["2", "'web'", "1.00"], +] +query = "SELECT p.order_id, p.\"'web'\" AS web_total, p.\"'store'\" AS store_total FROM (SELECT order_id, channel, total FROM input) PIVOT(SUM(total) FOR channel IN ('web', 'store')) AS p ORDER BY p.order_id" +order = "ordered" +tags = ["null", "missing-key"] + +[[case]] +id = "pivot_mixed_case_keys_without_alias_list" +description = "Keys with upper case, a space and an underscore, under MAX over BOOLEAN and no alias list. Catches: key names upper-cased, spaces changed, or the quotes dropped." +columns = [{ name = "customer_id", type = "VARCHAR" }, { name = "consent", type = "VARCHAR" }, { name = "granted", type = "BOOLEAN" }] +rows = [ + ["'c-1'", "'Email Opt'", "TRUE"], + ["'c-1'", "'SMS_OPT'", "FALSE"], + ["'c-2'", "'Email Opt'", "NULL"], + ["'c-2'", "'other'", "TRUE"], +] +query = "SELECT * FROM (SELECT customer_id, consent, granted FROM input) PIVOT(MAX(granted) FOR consent IN ('Email Opt', 'SMS_OPT')) ORDER BY customer_id" +order = "ordered" +tags = ["null", "missing-key", "case-sensitive-key"] + +[[case]] +id = "pivot_without_alias_list_over_no_rows" +description = "A PIVOT without an alias list over no rows. Catches: a row of NULLs instead of no rows, and the column names and types still described." +columns = [{ name = "order_id", type = "NUMBER(38,0)" }, { name = "channel", type = "VARCHAR" }, { name = "total", type = "NUMBER(12,2)" }] +rows = [ + ["1", "'web'", "10.50"], +] +query = "SELECT * FROM (SELECT order_id, channel, total FROM input WHERE total < 0) PIVOT(SUM(total) FOR channel IN ('web', 'store'))" +order = "bag" +tags = ["empty-input"] diff --git a/cases/semi-structured/table_flatten.toml b/cases/semi-structured/table_flatten.toml new file mode 100644 index 000000000..c332b66d8 --- /dev/null +++ b/cases/semi-structured/table_flatten.toml @@ -0,0 +1,33 @@ +# FLATTEN written as TABLE(FLATTEN(...)) after a comma, with a positional input, as dbt models +# write it: the same rows as LATERAL FLATTEN(input => ...). +category = "semi-structured" +construct = "fn:FLATTEN" +session_profile = "utc-v1" + +[[case]] +id = "table_flatten_positional_array" +description = "TABLE(FLATTEN(t.tags)) over an ARRAY column with a positional input: one row per element. Catches: the input not correlated to its row, empty arrays or NULL giving rows, or elements out of order." +columns = [{ name = "ticket_id", type = "NUMBER(38,0)" }, { name = "tags", type = "ARRAY" }] +rows = [ + ["1", "ARRAY_CONSTRUCT('billing', 'urgent')"], + ["2", "ARRAY_CONSTRUCT()"], + ["3", "NULL"], + ["4", "ARRAY_CONSTRUCT('refund')"], +] +query = "SELECT t.ticket_id, f.index, f.value::STRING AS tag FROM input t, TABLE(FLATTEN(t.tags)) f ORDER BY t.ticket_id, f.index" +order = "ordered" +tags = ["null", "array-index"] + +[[case]] +id = "table_flatten_named_variant_path" +description = "TABLE(FLATTEN(input => v:items)) over a VARIANT path, with KEY, PATH and VALUE read back. Catches: the named input ignored, objects read as arrays, or JSON null and missing paths giving rows." +columns = [{ name = "k", type = "NUMBER(2,0)" }, { name = "v", type = "VARIANT" }] +rows = [ + ["1", "PARSE_JSON('{\"items\":[{\"sku\":\"A-10\"},{\"sku\":\"B-20\"}]}')"], + ["2", "PARSE_JSON('{\"items\":{\"b\":1,\"a\":2}}')"], + ["3", "PARSE_JSON('{\"items\":null}')"], + ["4", "PARSE_JSON('{}')"], +] +query = "SELECT i.k, f.index, f.key, f.path, f.value::VARCHAR AS value FROM input i, TABLE(FLATTEN(input => i.v:items)) f ORDER BY i.k, f.index, f.key" +order = "ordered" +tags = ["null", "json-null", "missing-key", "nested", "array-index", "case-sensitive-key"] From 97da035baf7d52051153a36a41727b10b6999c56 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 05:26:29 +0000 Subject: [PATCH 2/3] feat: convert TABLE(FLATTEN(...)) after a comma as LATERAL FLATTEN Snowflake correlates TABLE(FLATTEN(t.col)) f to the sources before it, as dbt models write it; it now converts through the LATERAL FLATTEN rule, with a positional or named input. --- .../sqlquack-rules/src/catalogue/constants.rs | 2 +- .../conversion/_helpers/queries/flatten.rs | 2 +- .../conversion/_helpers/queries/sources.rs | 34 +++++++++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/crates/sqlquack-rules/src/catalogue/constants.rs b/crates/sqlquack-rules/src/catalogue/constants.rs index c6bab09bc..654427cf2 100644 --- a/crates/sqlquack-rules/src/catalogue/constants.rs +++ b/crates/sqlquack-rules/src/catalogue/constants.rs @@ -993,7 +993,7 @@ pub const RULES: &[RuleSpec] = &[ RuleSpec { id: FLATTEN_RULE, constructs: &["node:lateral", "fn:FLATTEN"], - conditions: "LATERAL FLATTEN of a VARIANT input after another FROM source or after JOIN ... ON clauses, as a comma or CROSS JOIN source, with OUTER => a boolean literal at most: INDEX and PATH [i] for array elements, KEY and PATH for object keys (raising for keys that are not plain names), VALUE and THIS; scalars, JSON null and NULL give no rows. Under OUTER => TRUE an input with no rows gives one row: PATH '' and THIS the input for an empty container MODE takes, every column NULL otherwise (JSON null, NULL, a missing path, a scalar, a kind MODE skips), as recorded. MODE => 'ARRAY' or 'OBJECT' without RECURSIVE takes only that kind. RECURSIVE => TRUE walks every depth with keys in byte order, writing a key of letters, digits, `_`, spaces and dots that is not a plain name as ['key'] and raising for any other, MODE => 'BOTH', 'OBJECT' or 'ARRAY' choosing what it descends into, a scalar input giving one row of itself under BOTH and raising under OBJECT or ARRAY; RECURSIVE with OUTER, SEQ and PATH => are not converted.", + conditions: "LATERAL FLATTEN, or TABLE(FLATTEN(...)) with an alias and no column list, which correlates the same way, of a VARIANT input after another FROM source or after JOIN ... ON clauses, as a comma or CROSS JOIN source, with OUTER => a boolean literal at most: INDEX and PATH [i] for array elements, KEY and PATH for object keys (raising for keys that are not plain names), VALUE and THIS; scalars, JSON null and NULL give no rows. Under OUTER => TRUE an input with no rows gives one row: PATH '' and THIS the input for an empty container MODE takes, every column NULL otherwise (JSON null, NULL, a missing path, a scalar, a kind MODE skips), as recorded. MODE => 'ARRAY' or 'OBJECT' without RECURSIVE takes only that kind. RECURSIVE => TRUE walks every depth with keys in byte order, writing a key of letters, digits, `_`, spaces and dots that is not a plain name as ['key'] and raising for any other, MODE => 'BOTH', 'OBJECT' or 'ARRAY' choosing what it descends into, a scalar input giving one row of itself under BOTH and raising under OBJECT or ARRAY; RECURSIVE with OUTER, SEQ and PATH => are not converted.", failures: &[], }, RuleSpec { diff --git a/crates/sqlquack-rules/src/conversion/_helpers/queries/flatten.rs b/crates/sqlquack-rules/src/conversion/_helpers/queries/flatten.rs index 48fe37a56..20075e906 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/queries/flatten.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/queries/flatten.rs @@ -27,7 +27,7 @@ use sqlquack_semantics::scopes::models::ScopeRelation; use sqlquack_semantics::types::models::{ColumnSpec, Holding}; /// The name of the table function LATERAL converts. -const FLATTEN_NAME: &str = "FLATTEN"; +pub(crate) const FLATTEN_NAME: &str = "FLATTEN"; /// A converted LATERAL FLATTEN: its join text and the relation it brings into scope. pub(crate) struct Flattened { diff --git a/crates/sqlquack-rules/src/conversion/_helpers/queries/sources.rs b/crates/sqlquack-rules/src/conversion/_helpers/queries/sources.rs index d80e69818..6bc534179 100644 --- a/crates/sqlquack-rules/src/conversion/_helpers/queries/sources.rs +++ b/crates/sqlquack-rules/src/conversion/_helpers/queries/sources.rs @@ -15,7 +15,7 @@ use crate::conversion::_helpers::expressions::project_functions::{exact_argument use crate::conversion::_helpers::expressions::scalar::convert_expression; use crate::conversion::_helpers::expressions::semi_structured::guarded_column; use crate::conversion::_helpers::queries::clauses::condition_sql; -use crate::conversion::_helpers::queries::flatten::lateral_flatten; +use crate::conversion::_helpers::queries::flatten::{FLATTEN_NAME, lateral_flatten}; use crate::conversion::_helpers::queries::pivot::pivot_subquery; use crate::conversion::_helpers::queries::query::{convert_query, require_distinct_names}; use crate::conversion::_helpers::support::collecting::{ @@ -57,7 +57,7 @@ pub(crate) fn convert_from( let sources = list(from, "expressions"); let separator = match sources .iter() - .any(|source| source.get(LATERAL_KEY).is_some()) + .any(|source| lateral_payload(source).is_some()) { true => " CROSS JOIN ", false => ", ", @@ -65,13 +65,13 @@ pub(crate) fn convert_from( let mut sql = String::from(" FROM "); for (position, source) in sources.iter().enumerate() { let before = events(); - if let Some(lateral) = source.get(LATERAL_KEY) { + if let Some(lateral) = lateral_payload(source) { let flattened = match position { 0 => Err(missing( node_construct(LATERAL_KEY), "LATERAL as the first FROM source".to_owned(), )), - _ => lateral_flatten(lateral, &relations, outer, context) + _ => lateral_flatten(&lateral, &relations, outer, context) .map(|flattened| (flattened.sql, flattened.relation)), } .map_err(|refusal| context.locator.placed(refusal, source)); @@ -161,8 +161,8 @@ fn convert_join( .get("match_condition") .filter(|matching| !matching.is_null()); let before = events(); - if let (Some(lateral), "Implicit" | "Cross", None) = (source.get(LATERAL_KEY), kind, on) { - let flattened = lateral_flatten(lateral, earlier, outer, context) + if let (Some(lateral), "Implicit" | "Cross", None) = (lateral_payload(source), kind, on) { + let flattened = lateral_flatten(&lateral, earlier, outer, context) .map(|flattened| (flattened.sql, flattened.relation)); return recovered(flattened, source, (before, earlier.len())); } @@ -492,6 +492,28 @@ fn source_alias(source: &Value) -> Option { } } +/// The LATERAL payload of `LATERAL f(...) alias` or `TABLE(FLATTEN(...)) alias`, correlated alike. +fn lateral_payload(source: &Value) -> Option { + if let Some(lateral) = source.get(LATERAL_KEY) { + return Some(lateral.clone()); + } + let ("alias", aliased) = node_kind(source)? else { + return None; + }; + let call = table_call(aliased.get("this")?)?; + let flatten = call + .get("name") + .and_then(Value::as_str) + .is_some_and(|name| name.eq_ignore_ascii_case(FLATTEN_NAME)); + let alias = identifier_name(aliased.get("alias")?)?; + let unlisted = aliased + .get("column_aliases") + .and_then(Value::as_array) + .is_none_or(Vec::is_empty); + (flatten && unlisted && alias == alias.to_ascii_uppercase()) + .then(|| json!({ "this": { "function": call }, "alias": alias })) +} + fn lateral_subquery(source: &Value) -> bool { matches!(node_kind(source), Some(("subquery", payload)) if payload.get(LATERAL_KEY).and_then(Value::as_bool) == Some(true)) From c9f4e6b90616fd6283c1236acb003a218095d583 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 06:12:13 +0000 Subject: [PATCH 3/3] evidence: record unaliased PIVOT key names and TABLE(FLATTEN) forms Snowflake results for the six cases in cases/relational/pivot_unaliased.toml and cases/semi-structured/table_flatten.toml at a20177b. --- ...86b3ed15137cb1456ce3df3c697673e01e5d.arrow | Bin 0 -> 514 bytes ...286b3ed15137cb1456ce3df3c697673e01e5d.json | 79 ++++++++++++++ ...43b3e3f348f16c50622e0f30830b74f7cf1f.arrow | Bin 0 -> 3930 bytes ...f43b3e3f348f16c50622e0f30830b74f7cf1f.json | 97 ++++++++++++++++++ ...6d06ae71f8d5518c19c6903fae1b42467c50.arrow | Bin 0 -> 2402 bytes ...96d06ae71f8d5518c19c6903fae1b42467c50.json | 79 ++++++++++++++ ...48fa2336d677f65f0e0921cc04a04b92d05f.arrow | Bin 0 -> 1450 bytes ...e48fa2336d677f65f0e0921cc04a04b92d05f.json | 79 ++++++++++++++ ...8d0f770d9832448d0e28f5cabfebda20f9ec.arrow | Bin 0 -> 1474 bytes ...c8d0f770d9832448d0e28f5cabfebda20f9ec.json | 79 ++++++++++++++ ...dd0a88621a3249368de23e69dc33181b683d.arrow | Bin 0 -> 2474 bytes ...edd0a88621a3249368de23e69dc33181b683d.json | 79 ++++++++++++++ 12 files changed, 492 insertions(+) create mode 100644 evidence/results/21d4c9c5a220840574004b4e581286b3ed15137cb1456ce3df3c697673e01e5d.arrow create mode 100644 evidence/results/21d4c9c5a220840574004b4e581286b3ed15137cb1456ce3df3c697673e01e5d.json create mode 100644 evidence/results/542612dfa7749f96531af74a063f43b3e3f348f16c50622e0f30830b74f7cf1f.arrow create mode 100644 evidence/results/542612dfa7749f96531af74a063f43b3e3f348f16c50622e0f30830b74f7cf1f.json create mode 100644 evidence/results/7c45d391d4c1b047174ace42c9996d06ae71f8d5518c19c6903fae1b42467c50.arrow create mode 100644 evidence/results/7c45d391d4c1b047174ace42c9996d06ae71f8d5518c19c6903fae1b42467c50.json create mode 100644 evidence/results/9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f.arrow create mode 100644 evidence/results/9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f.json create mode 100644 evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.arrow create mode 100644 evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.json create mode 100644 evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.arrow create mode 100644 evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.json diff --git a/evidence/results/21d4c9c5a220840574004b4e581286b3ed15137cb1456ce3df3c697673e01e5d.arrow b/evidence/results/21d4c9c5a220840574004b4e581286b3ed15137cb1456ce3df3c697673e01e5d.arrow new file mode 100644 index 0000000000000000000000000000000000000000..05c4782015509babf6fe3741669989872c560ddb GIT binary patch literal 514 zcmZ<^3i1y(WMKFY1TPpE7zBWrfq{#Ghk=cOm4O4u0s=-BAfExq;{cK@P?CXx8OW;v zVhgY+1H%HK89>EMATuPOVnB+49f;M7OY)0S)fsxgY8V(DfMOSLsAmPKFHcQU2hw|x z)C(|(0PSW2ng?=?0MxAz`&57;EIv k@&L_c1IqFMO$3MWR;ght8 z19=t^NeHE1;u4WQiJ=Awg<%V<7@lG8De#M&hDAR1iZpsevLSj!UYt4(8ubmLeiQH5 ziggOxJkHHM%A>?^@*R2v-GeScN1*JZ{@ZaF3+w1Y3eZc?=e4y`y|B}$puP`G5+yP8 zrN<{S>B-4V7IntGva?m0tE{bVRYi{5V9V9ghQ|K1#vVgojC-?Os&m}fpYzx-Z5hXa z9KW$qDc3e@>uacw0K+*mHfkYXlZMc@->7tY=t%Xng+HNUsb~&#Md4$o$(b>e2EN55<4*>QMH#7*pUN@tx%Nx zB(1z-zRx_E$`?^G@4i3}p>Lo}WW7%Zu@2^3=(G1}*1&K+>Dw3g=aPZpdN7B$hfUsg zeK6*}JT&}4ynO~n^TxLKXNvhLl=i;ltoy+n=Y9*S`I8I$Ne}xQ^7zv^$T`eH*}e%q zgxcKA!-WzkaQ4 z9Y>ID`cZ(#pA$V{$)4p_-S)vE{||vWvQk_e0R@ zP|d^Z)aJ`qsXM9MXnHL5R$KQ*Gq5t=y#;W!o^h;JL}gOK%_Xom0pBo`JTa~Y1Ay#$ w6K%StdUo$O-TO@znP7i+y7wFG%kKRq@PA16e$&0*;Mh?2n|JuWa0gj41eY!5hu@u@Vj8UD2(}v-h@_KpC+_W-i$KWmaE&LL0 z!KdMj^Z4$%%!6%&QFB~>=+rAU>!MLb`!%4!7F4eL7tLz1T0d$YBZrUgac0{Z`?zwZ zHH#sdD0Js`rN-~<_~x-ynOpSs(m1Qy&bf11$Jiw_+%xkXf-jq9v5?=8QM7BPM-D=% ztXqoD7?{Md{~^URDJdC)PIF5KxG_eia8hw<6K12C`IVc2_`SGI1vFvYiUCa+w?>G@ z&+W!H-;LWnG=Je{VAo_TAVqeh%zEswlPKfNhOk@NDqH4yp|rQQs@T0icL%?Q@4|J> zEusE^G=&k&rW|T&?h6B*0as~$e#ztbYJU&eQLN)=V8xe?U`IBu3o3#)YHg~ zZ|)-jdND;T=`jP@d5llO$ylvrgVqkz;n}}e`J43bRqta~$g!KC%nF3H_kf4*jswkeBb}@&qxIL%jm>Ig(Ws^16PK zqlA4MO4)(7YF9b~d)ZT+up^U8WM2+dcP3i_5hbYtJ|&C46JQdd?FZFb(5_b*aiH(e z1@sXbLeHS=p-FzUjWlYHJw3$3&LEtRBiOSI3}{vS-OXoFBO3N*eUax4%(=#N`<;ow zy#9j805|P+2CVb3woeW(%LzZ@Nz{#}@o0#c3qCn1o{?wg8JOd`ep};VIg`Pt7bB(W zo_H5iARc1rueukX2rvi5lLLJ-JT);%+ltR-vW+zdKdH^TLmtT+`!qtHw~4`-`UYqG zq7IzuFK0m^kCdWXlZE%w0z!3>sllwKBrolfMV`JaA7!}?`!hr`C9Wd;H q5xyr*1)~$5h2@y4sOx{hrmT2P{Qoz&;)?I{ufM@E?n6cWNB;m>RGHKO literal 0 HcmV?d00001 diff --git a/evidence/results/9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f.json b/evidence/results/9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f.json new file mode 100644 index 000000000..cadc86969 --- /dev/null +++ b/evidence/results/9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f.json @@ -0,0 +1,79 @@ +{ + "arrow_sha256": "83906f4fd0b02362253a9a0fe43ac0d4eee5e75d68a8ee44af2739e3189a27ed", + "case_hash": "9b9e4ce500d7a86b5e5a0da9652e48fa2336d677f65f0e0921cc04a04b92d05f", + "columns": [ + { + "internal_size": null, + "name": "ORDER_ID", + "nullable": false, + "precision": 38, + "scale": 0, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "WEB_TOTAL", + "nullable": true, + "precision": 24, + "scale": 2, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "STORE_TOTAL", + "nullable": true, + "precision": 24, + "scale": 2, + "type_code": 0, + "type_name": "FIXED" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "23fbd4e24b967866c4e152b945b6b8e02600648fb2e71b50a94edd92f2bf021f", + "recorded_at": "2026-11-06T06:09:54.301398+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 2, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.arrow b/evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.arrow new file mode 100644 index 0000000000000000000000000000000000000000..9ff597c2edb7565e70d24932ad6fb94175348ff4 GIT binary patch literal 1474 zcmeHH%}N4M6h7)WPBw&ugv%Duv}j?pX_=s*5Ez3{yHJ^QKvZ;~gdU=0OCO|%@Bw;& zmhFS=`|eML6x6y7oVn-ko^#K4?wv{)HqQ@4mLM-JQV>#Jd~qc!9wbo8fiBQId>!)@ z$w0dY*OgUdfgA|VF-OVRut*LZOq0!=f8^<+bxfG4qeL^e^67MrOEi=uQ9%T9{>^qFX-+zhL=@Ul^*?t&NKIrtXb0`G%a z{|r5`AIeChd}~;*;$eHxnvEmK$x001RJe6!NpuIU)AQ-g$23IB8te*z7uZFzUrfsZP&$54bTN zlrpQj4^J>K2g#Q$b0d5;Ey*})#y8rHZyvg?%{xOJi5u^TCF1-Y**IIDSd*B8M7&7H z>Z@l#v2Jwhr^;M5pK-tI#EoSAR!^n1#nycxaK9kC@6HN8+>4Ro&FDUTpzUH3KDy?M j;PfzJ*;A74xnvIfOgHEQf35$&(ZBIV%eV&>_3wTIFI$#! literal 0 HcmV?d00001 diff --git a/evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.json b/evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.json new file mode 100644 index 000000000..732a6f0cb --- /dev/null +++ b/evidence/results/e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec.json @@ -0,0 +1,79 @@ +{ + "arrow_sha256": "53578690d219a8fc9e389871a6353ff33e789552260a9519066a0ecb17cd21f2", + "case_hash": "e2cbd488488ef8174915de51d18c8d0f770d9832448d0e28f5cabfebda20f9ec", + "columns": [ + { + "internal_size": null, + "name": "ORDER_ID", + "nullable": false, + "precision": 38, + "scale": 0, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "'web'", + "nullable": true, + "precision": 24, + "scale": 2, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "'store'", + "nullable": true, + "precision": 24, + "scale": 2, + "type_code": 0, + "type_name": "FIXED" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "3888bb3369e4f9aafe244f1ad2798a27975402e748d92dedceb04f78850854c9", + "recorded_at": "2026-11-06T06:09:54.301398+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 3, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +} diff --git a/evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.arrow b/evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.arrow new file mode 100644 index 0000000000000000000000000000000000000000..db3a278fc0b697c17e31e0164d61ae955b49b20a GIT binary patch literal 2474 zcmeHJJx?1!5S@eb2hK&Z5(~McK!_p*hA2?R0s|H(Aaqh(L<+_^%t@yM86yRWGGz)% zN`3-AfRchLN|_=*fS*7?Fz?;oMw|0FPMN}E&CTrYyP2JLyCYY3cDCMEM0yahMT$Zx zh$C6aNFE-Y^D&W4i?br%9uY&VObPxJ=7?7D6xOY-A*#p^=oR!U z^aJ!2l<(g>^m@e8$Lr!kC!qU&*bLm$BM<%^FlAU`xK>$sJzrT|oL_>^z3!cMy-hD{ zcMn8Lzy6MG9W*-{`xaO;_Z(tnpvSFdz`VQ0I!cxWBxL@hj@R;!{lgIc7hqUB_eOPV z4VEC!puu6=N2sU@8!T{LuXdu;lR;dI>#) zGLh!d!FMog;aZZ%IRnFbDsTGva}5l?2RR|fCU2ipFeYyo#`+-Mu7P2Vn!tW4!5@Kb{u~IdJRC8#VW1o$(pxJM-d!Dk%}V+EN3HTi;7!(LDn@3 u9?yjC!)-YS6tH^<>qz&3-cK$$$3t|G6j6;{I2o|7cIed0UzQ literal 0 HcmV?d00001 diff --git a/evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.json b/evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.json new file mode 100644 index 000000000..153804bc9 --- /dev/null +++ b/evidence/results/ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d.json @@ -0,0 +1,79 @@ +{ + "arrow_sha256": "267a8b880888e6609d407f286799f8c0ac964e1e9ffb3cbb5cfb946fce5b0443", + "case_hash": "ece78351de913a8b2004517ac82edd0a88621a3249368de23e69dc33181b683d", + "columns": [ + { + "internal_size": null, + "name": "TICKET_ID", + "nullable": false, + "precision": 38, + "scale": 0, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": null, + "name": "INDEX", + "nullable": true, + "precision": 38, + "scale": 0, + "type_code": 0, + "type_name": "FIXED" + }, + { + "internal_size": 134217728, + "name": "TAG", + "nullable": true, + "precision": null, + "scale": null, + "type_code": 2, + "type_name": "TEXT" + } + ], + "connector": "snowflake-connector-python 4.7.2", + "duckdb_version": "v2.0.0", + "effective_parameters": { + "BINARY_INPUT_FORMAT": "HEX", + "BINARY_OUTPUT_FORMAT": "HEX", + "DATE_INPUT_FORMAT": "AUTO", + "DATE_OUTPUT_FORMAT": "YYYY-MM-DD", + "DEFAULT_NULL_ORDERING": "LAST", + "GEOGRAPHY_OUTPUT_FORMAT": "GeoJSON", + "GEOMETRY_OUTPUT_FORMAT": "GeoJSON", + "JSON_INDENT": "2", + "QUOTED_IDENTIFIERS_IGNORE_CASE": "false", + "STRICT_JSON_OUTPUT": "false", + "TIMESTAMP_DAY_IS_ALWAYS_24H": "false", + "TIMESTAMP_INPUT_FORMAT": "AUTO", + "TIMESTAMP_LTZ_OUTPUT_FORMAT": "", + "TIMESTAMP_NTZ_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3", + "TIMESTAMP_OUTPUT_FORMAT": "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMESTAMP_TZ_OUTPUT_FORMAT": "", + "TIMEZONE": "UTC", + "TIME_INPUT_FORMAT": "AUTO", + "TIME_OUTPUT_FORMAT": "HH24:MI:SS", + "TWO_DIGIT_CENTURY_START": "1970", + "USE_CACHED_RESULT": "false", + "WEEK_OF_YEAR_POLICY": "0", + "WEEK_START": "0" + }, + "error": null, + "format": 1, + "outcome": "rows", + "parameters_sha256": "d094d15417903748055eca8fde8cf2884751253939f05ad5ad8d9bf14299ac61", + "query_id_sha256": "50c71e260d074ed287d2ae44a3d876cbbebf9f4850492b837de0975cbbf7766c", + "recorded_at": "2026-11-06T06:09:54.301398+00:00", + "recorder": "sqlquack-record 0.1.0", + "row_count": 3, + "session_parameters": { + "DEFAULT_NULL_ORDERING": "LAST", + "QUOTED_IDENTIFIERS_IGNORE_CASE": false, + "TIMESTAMP_TYPE_MAPPING": "TIMESTAMP_NTZ", + "TIMEZONE": "UTC", + "USE_CACHED_RESULT": false, + "WEEK_OF_YEAR_POLICY": 0, + "WEEK_START": 0 + }, + "warehouse_version": "10.40.102" +}