From 90f133d516ba947e6bfed26c01386dbb29bb1b8a Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 06:42:51 +0000 Subject: [PATCH 1/2] Classify GROUP BY ALL, ORDER BY ALL, WITH RECURSIVE and a table SAMPLE The clause classifier emitted clause:group_by, clause:order_by and clause:with for these forms and nothing for a SAMPLE on a table reference, so form-links could not tell group-by.all, order-by.all, with.select.syntax and sample apart. They now also emit clause:group_by_all, clause:order_by_all, clause:with_recursive and clause:sample, beside the clause keys they already had. --- .../src/classification/_helpers/clauses.rs | 27 +++++++++++++++++++ .../src/classification/tests/test_classify.rs | 20 ++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/crates/sqlquack-core/src/classification/_helpers/clauses.rs b/crates/sqlquack-core/src/classification/_helpers/clauses.rs index cdd981972..8adcedb67 100644 --- a/crates/sqlquack-core/src/classification/_helpers/clauses.rs +++ b/crates/sqlquack-core/src/classification/_helpers/clauses.rs @@ -16,6 +16,7 @@ pub(crate) fn clause_constructs(node: &Expression) -> Vec { Expression::Union(union) => union_clauses(union), Expression::Intersect(intersect) => intersect_clauses(intersect), Expression::Except(except) => except_clauses(except), + Expression::Table(table) => present(&[(table.table_sample.is_some(), "sample")]), _ => Vec::new(), }; let mut constructs: Vec = names @@ -87,6 +88,10 @@ fn select_clauses(select: &Select) -> Vec { } = select; present(&[ (with.is_some(), "with"), + ( + with.as_ref().is_some_and(|with| with.recursive), + "with_recursive", + ), (*distinct, "distinct"), (distinct_on.is_some(), "distinct_on"), (top.is_some(), "top"), @@ -94,11 +99,23 @@ fn select_clauses(select: &Select) -> Vec { (prewhere.is_some(), "prewhere"), (where_clause.is_some(), "where"), (group_by.is_some(), "group_by"), + ( + group_by + .as_ref() + .is_some_and(|group_by| group_by.all == Some(true)), + "group_by_all", + ), (having.is_some(), "having"), (windows.is_some(), "window"), (qualify.is_some(), "qualify"), (connect.is_some(), "connect_by"), (order_by.is_some(), "order_by"), + ( + order_by + .as_ref() + .is_some_and(|order_by| order_by.all.is_some()), + "order_by_all", + ), (distribute_by.is_some(), "distribute_by"), (cluster_by.is_some(), "cluster_by"), (sort_by.is_some(), "sort_by"), @@ -159,7 +176,17 @@ macro_rules! set_operation_clauses { (*strict, concat!($prefix, "_strict")), (!on_columns.is_empty(), concat!($prefix, "_on_columns")), (with.is_some(), "with"), + ( + with.as_ref().is_some_and(|with| with.recursive), + "with_recursive", + ), (order_by.is_some(), "order_by"), + ( + order_by + .as_ref() + .is_some_and(|order_by| order_by.all.is_some()), + "order_by_all", + ), (limit.is_some(), "limit"), (offset.is_some(), "offset"), (distribute_by.is_some(), "distribute_by"), diff --git a/crates/sqlquack-core/src/classification/tests/test_classify.rs b/crates/sqlquack-core/src/classification/tests/test_classify.rs index c06a2305e..46f4ad2b2 100644 --- a/crates/sqlquack-core/src/classification/tests/test_classify.rs +++ b/crates/sqlquack-core/src/classification/tests/test_classify.rs @@ -43,6 +43,26 @@ fn given_snowflake_query_when_classifying_then_reports_each_construct_family() { sql: "SELECT product_id FROM products UNION ALL SELECT product_id FROM inventory", expected_keys: &["node:union", "clause:union_all"], }, + ConstructTestCase { + description: "GROUP BY ALL and ORDER BY ALL have keys of their own beside the clause", + sql: "SELECT region, COUNT(*) FROM orders GROUP BY ALL ORDER BY ALL", + expected_keys: &[ + "clause:group_by", + "clause:group_by_all", + "clause:order_by", + "clause:order_by_all", + ], + }, + ConstructTestCase { + description: "WITH RECURSIVE has a key of its own beside the clause", + sql: "WITH RECURSIVE r AS (SELECT 1 AS n) SELECT n FROM r", + expected_keys: &["clause:with", "clause:with_recursive"], + }, + ConstructTestCase { + description: "a SAMPLE on a table reference is a sample clause", + sql: "SELECT order_id FROM orders SAMPLE (10)", + expected_keys: &["clause:sample"], + }, ConstructTestCase { description: "project functions collapse into one construct", sql: "SELECT __sqlquack_udf(o.status) FROM orders o", From a112be03490169c5adbe059533457944087bfd9d Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Fri, 6 Nov 2026 07:35:25 +0000 Subject: [PATCH 2/2] Link the new clause keys to their forms clause:group_by_all -> sf.query.group-by.all, clause:order_by_all -> sf.query.order-by.all, clause:with_recursive -> sf.query.with.select.syntax and clause:sample -> sf.query.sample. On main's recorded statements group-by.all goes unmeasured -> matching; the other three have no recorded statement yet. 0 regressions; baseline re-recorded. --- coverage/translation/form-links.toml | 16 ++++++++++++++++ coverage/translation/results-baseline.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/coverage/translation/form-links.toml b/coverage/translation/form-links.toml index 378182460..b5f6aedf1 100644 --- a/coverage/translation/form-links.toml +++ b/coverage/translation/form-links.toml @@ -77,6 +77,10 @@ forms = ["sf.query.from"] constructs = ["clause:with"] forms = ["sf.query.with.select"] +[[link]] +constructs = ["clause:with_recursive"] +forms = ["sf.query.with.select.syntax"] + [[link]] constructs = ["clause:where"] forms = ["sf.query.where"] @@ -85,6 +89,10 @@ forms = ["sf.query.where"] constructs = ["clause:group_by"] forms = ["sf.query.group-by.groupitem"] +[[link]] +constructs = ["clause:group_by_all"] +forms = ["sf.query.group-by.all"] + [[link]] constructs = ["clause:having"] forms = ["sf.query.having"] @@ -93,10 +101,18 @@ forms = ["sf.query.having"] constructs = ["clause:order_by"] forms = ["sf.query.order-by.orderitem"] +[[link]] +constructs = ["clause:order_by_all"] +forms = ["sf.query.order-by.all"] + [[link]] constructs = ["clause:qualify"] forms = ["sf.query.qualify.qualify", "sf.query.qualify.select-from-qualify"] +[[link]] +constructs = ["clause:sample"] +forms = ["sf.query.sample"] + [[link]] constructs = ["clause:limit"] forms = ["sf.query.limit.limit"] diff --git a/coverage/translation/results-baseline.json b/coverage/translation/results-baseline.json index 9e7b25139..798646b45 100644 --- a/coverage/translation/results-baseline.json +++ b/coverage/translation/results-baseline.json @@ -11098,7 +11098,7 @@ "sf.query.group-by-cube": "unmeasured", "sf.query.group-by-grouping-sets": "unmeasured", "sf.query.group-by-rollup": "unmeasured", - "sf.query.group-by.all": "unmeasured", + "sf.query.group-by.all": "matching", "sf.query.group-by.groupitem": "refused", "sf.query.having": "refused", "sf.query.into": "not_translatable",