diff --git a/corpora/everyday-models/queries.toml b/corpora/everyday-models/queries.toml new file mode 100644 index 000000000..ca347d74a --- /dev/null +++ b/corpora/everyday-models/queries.toml @@ -0,0 +1,4087 @@ +# dbt-style Snowflake models, over schema.json: staging, CTE chains, incremental filters, QUALIFY +# deduplication, FLATTEN, MERGE and other materialization DML, date spines, window frames, +# PIVOT/UNPIVOT, marts, snapshots and the SQL generic tests compile to. The fields are those of +# corpora/everyday/queries.toml: `expect` under the built-in default policy, `expect_repository` +# under the repository's sqlquack.toml with a UTC session, `snowflake` for Snowflake's recorded +# verdict from snowflake-receipts.json, and `uncertain` for a query whose validity is not recorded. + +[[query]] +category = "dbt_staging" +sql = '''with source as ( + select * from raw_orders +), +renamed as ( + select + id as order_id, + user_id as customer_id, order_date, status + from source +) +select * from renamed +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''with source as ( + select * from raw_customers +), +renamed as ( + select + id as customer_id, + first_name, last_name, email + from source +) +select * from renamed +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''with source as ( + select * from raw_payments +), +renamed as ( + select + id as payment_id, + orderid as order_id, paymentmethod as payment_method + from source +) +select * from renamed +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as payment_id, + orderid as order_id, + paymentmethod as payment_method, + amount / 100 as amount +from raw_payments''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as payment_id, + orderid as order_id, + paymentmethod as payment_method, + amount / 100.0 as amount, + created as created_at +from raw_payments +where status = 'success'''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + user_id as customer_id, + order_date, + lower(status) as status, + amount_cents / 100 as amount +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + trim(first_name) as first_name, + trim(last_name) as last_name, + lower(trim(email)) as email +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + initcap(first_name) as first_name, + initcap(last_name) as last_name, + nullif(trim(email), '') as email, + updated_at +from raw_customers''' +expect = "refused SQ0008 fn:INITCAP" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + user_id as customer_id, + order_date::date as order_date, + coalesce(status, 'unknown') as status, + amount_cents::number(12,2) / 100 as amount, + updated_at +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + user_id as customer_id, + cast(order_date as date) as order_date, + status, + cast(amount_cents as number(38,2)) / 100 as amount +from raw_orders +where status is not null''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + user_id as customer_id, + order_date, + status, + amount_cents, + _loaded_at +from raw_orders +where _loaded_at >= dateadd(day, -3, current_timestamp())''' +expect = "refused SQ0008 fn:CURRENT_TIMESTAMP" +expect_repository = "refused SQ0003 fn:DATEADD" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + status in ('returned', 'return_pending') as is_returned, + status = 'completed' as is_completed +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + case status + when 'placed' then 1 + when 'shipped' then 2 + when 'completed' then 3 + when 'return_pending' then 4 + when 'returned' then 5 + end as status_rank +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + md5(cast(coalesce(cast(id as varchar), '_dbt_utils_surrogate_key_null_') as varchar)) as order_key, + id as order_id +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + md5(cast(coalesce(cast(orderid as varchar), '_dbt_utils_surrogate_key_null_') || '-' || coalesce(cast(paymentmethod as varchar), '_dbt_utils_surrogate_key_null_') as varchar)) as payment_key, + orderid as order_id, + paymentmethod as payment_method +from raw_payments''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + md5(concat_ws('|', coalesce(to_varchar(id), ''), coalesce(email, ''))) as customer_key, + id as customer_id +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + sha2(lower(trim(email)), 256) as email_hash, + id as customer_id +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + split_part(email, '@', 2) as email_domain +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + regexp_replace(email, '\\s+', '') as email +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + first_name || ' ' || last_name as full_name +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as customer_id, + concat(first_name, ' ', last_name) as full_name +from raw_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + try_to_number(to_varchar(amount_cents)) as amount_cents +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + to_date(order_date) as order_date, + to_timestamp_ntz(updated_at) as updated_at +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + convert_timezone('UTC', 'America/New_York', updated_at) as updated_at_local +from raw_orders''' +expect = "refused SQ0008 fn:CONVERT_TIMEZONE" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + raw:id::varchar as event_id, + raw:customer_id::number as customer_id, + raw:type::varchar as event_type, + raw:occurred_at::timestamp_ntz as occurred_at, + _loaded_at +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + raw:id::string as event_id, + raw:properties:page::string as page, + raw:properties:referrer::string as referrer +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + raw['id']::varchar as event_id, + raw['properties']['page']::varchar as page +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + get_path(raw, 'properties.page')::varchar as page, + raw:id::varchar as event_id +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + raw:id::varchar as event_id, + try_to_timestamp(raw:occurred_at::varchar) as occurred_at +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + raw:id::varchar as event_id, + coalesce(raw:revenue::number(12,2), 0) as revenue +from raw_events''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as payment_id, + orderid as order_id, + iff(paymentmethod in ('credit_card', 'debit_card'), 'card', paymentmethod) as payment_type +from raw_payments''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as payment_id, + orderid as order_id, + decode(paymentmethod, 'credit_card', 'card', 'bank_transfer', 'bank', 'other') as payment_type +from raw_payments''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + session_id, + customer_id, + started_at, + attributes:utm_source::varchar as utm_source, + attributes:utm_campaign::varchar as utm_campaign +from web_sessions''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + subscription_id, + customer_id, + upper(plan) as plan, + mrr, + started_at::date as started_on, + cancelled_at::date as cancelled_on +from subscriptions''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + rate_date, + upper(currency) as currency, + rate_to_usd +from fx_rates +where rate_to_usd > 0''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select * exclude (_loaded_at) +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select * rename (id as order_id, user_id as customer_id) +from raw_orders''' +expect = "refused SQ0002 node:star" +expect_repository = "refused SQ0002 node:star" + +[[query]] +category = "dbt_staging" +sql = '''select * exclude (_loaded_at) rename (id as customer_id) +from raw_customers''' +expect = "refused SQ0002 node:star" +expect_repository = "refused SQ0002 node:star" +uncertain = true + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + user_id as customer_id, + current_timestamp() as _dbt_loaded_at +from raw_orders''' +expect = "refused SQ0008 fn:CURRENT_TIMESTAMP" +expect_repository = "translated" + +[[query]] +category = "dbt_staging" +sql = '''select + id as order_id, + '{{ invocation_id }}' as invocation_id +from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with customers as ( + select * from stg_customers +), +orders as ( + select * from stg_orders +), +customer_orders as ( + select + customer_id, + min(order_date) as first_order, + max(order_date) as most_recent_order, + count(order_id) as number_of_orders + from orders + group by customer_id +), +final as ( + select + customers.customer_id, + customers.first_name, + customers.last_name, + customer_orders.first_order, + customer_orders.most_recent_order, + coalesce(customer_orders.number_of_orders, 0) as number_of_orders + from customers + left join customer_orders using (customer_id) +) +select * from final''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with customers as ( + select * from stg_customers +), +orders as ( + select * from stg_orders +), +payments as ( + select * from stg_payments +), +customer_orders as ( + select + customer_id, + min(order_date) as first_order, + max(order_date) as most_recent_order, + count(order_id) as number_of_orders + from orders + group by customer_id +), +customer_payments as ( + select + orders.customer_id, + sum(amount) as total_amount + from payments + left join orders on payments.order_id = orders.order_id + group by orders.customer_id +), +final as ( + select + customers.customer_id, + customers.first_name, + customers.last_name, + customer_orders.first_order, + customer_orders.most_recent_order, + customer_orders.number_of_orders, + customer_payments.total_amount as customer_lifetime_value + from customers + left join customer_orders on customers.customer_id = customer_orders.customer_id + left join customer_payments on customers.customer_id = customer_payments.customer_id +) +select * from final''' +expect = "check SQ0004" +expect_repository = "check SQ0004" +snowflake = "rejects 2028" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +), +payments as ( + select * from stg_payments +), +order_payments as ( + select + order_id, + sum(case when payment_method = 'credit_card' then amount else 0 end) as credit_card_amount, + sum(case when payment_method = 'coupon' then amount else 0 end) as coupon_amount, + sum(case when payment_method = 'bank_transfer' then amount else 0 end) as bank_transfer_amount, + sum(case when payment_method = 'gift_card' then amount else 0 end) as gift_card_amount, + sum(amount) as total_amount + from payments + group by order_id +), +final as ( + select + orders.order_id, + orders.customer_id, + orders.order_date, + orders.status, + order_payments.credit_card_amount, + order_payments.coupon_amount, + order_payments.bank_transfer_amount, + order_payments.gift_card_amount, + order_payments.total_amount as amount + from orders + left join order_payments on orders.order_id = order_payments.order_id +) +select * from final''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from orders +), +aggregated as ( + select + customer_id, + count(*) as row_count, + sum(total) as total_total + from source + group by customer_id +) +select * from aggregated +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from orders +), +filtered as ( + select * from source + where total is not null +), +aggregated as ( + select + customer_id, + count(*) as row_count, + avg(total) as avg_total, + max(total) as max_total + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from orders +), +daily as ( + select + date_trunc('day', ordered_at) as day, + customer_id, + sum(total) as daily_total + from source + group by 1, 2 +), +final as ( + select + day, + customer_id, + daily_total, + sum(daily_total) over (partition by customer_id order by day) as cumulative_total + from daily +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from orders +), +monthly as ( + select + date_trunc(month, ordered_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from payments +), +aggregated as ( + select + method, + count(*) as row_count, + sum(amount) as total_amount + from source + group by method +) +select * from aggregated +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from payments +), +filtered as ( + select * from source + where amount is not null +), +aggregated as ( + select + method, + count(*) as row_count, + avg(amount) as avg_amount, + max(amount) as max_amount + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from payments +), +daily as ( + select + date_trunc('day', paid_at) as day, + method, + sum(amount) as daily_amount + from source + group by 1, 2 +), +final as ( + select + day, + method, + daily_amount, + sum(daily_amount) over (partition by method order by day) as cumulative_amount + from daily +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from payments +), +monthly as ( + select + date_trunc(month, paid_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_orders +), +aggregated as ( + select + status, + count(*) as row_count, + sum(amount) as total_amount + from source + group by status +) +select * from aggregated +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_orders +), +filtered as ( + select * from source + where amount is not null +), +aggregated as ( + select + status, + count(*) as row_count, + avg(amount) as avg_amount, + max(amount) as max_amount + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_orders +), +daily as ( + select + date_trunc('day', updated_at) as day, + status, + sum(amount) as daily_amount + from source + group by 1, 2 +), +final as ( + select + day, + status, + daily_amount, + sum(daily_amount) over (partition by status order by day) as cumulative_amount + from daily +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from stg_orders +), +monthly as ( + select + date_trunc(month, updated_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_payments +), +aggregated as ( + select + payment_method, + count(*) as row_count, + sum(amount) as total_amount + from source + group by payment_method +) +select * from aggregated +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_payments +), +filtered as ( + select * from source + where amount is not null +), +aggregated as ( + select + payment_method, + count(*) as row_count, + avg(amount) as avg_amount, + max(amount) as max_amount + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from stg_payments +), +daily as ( + select + date_trunc('day', paid_at) as day, + payment_method, + sum(amount) as daily_amount + from source + group by 1, 2 +), +final as ( + select + day, + payment_method, + daily_amount, + sum(daily_amount) over (partition by payment_method order by day) as cumulative_amount + from daily +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from stg_payments +), +monthly as ( + select + date_trunc(month, paid_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from subscriptions +), +aggregated as ( + select + plan, + count(*) as row_count, + sum(mrr) as total_mrr + from source + group by plan +) +select * from aggregated +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from subscriptions +), +filtered as ( + select * from source + where mrr is not null +), +aggregated as ( + select + plan, + count(*) as row_count, + avg(mrr) as avg_mrr, + max(mrr) as max_mrr + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from subscriptions +), +daily as ( + select + date_trunc('day', started_at) as day, + plan, + sum(mrr) as daily_mrr + from source + group by 1, 2 +), +final as ( + select + day, + plan, + daily_mrr, + sum(daily_mrr) over (partition by plan order by day) as cumulative_mrr + from daily +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from subscriptions +), +monthly as ( + select + date_trunc(month, started_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from order_items +), +aggregated as ( + select + product_id, + count(*) as row_count, + sum(quantity) as total_quantity + from source + group by product_id +) +select * from aggregated +''' +expect = "refused SQ0003 fn:SUM" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from order_items +), +filtered as ( + select * from source + where quantity is not null +), +aggregated as ( + select + product_id, + count(*) as row_count, + avg(quantity) as avg_quantity, + max(quantity) as max_quantity + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "refused SQ0003 fn:AVG" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from inventory_snapshots +), +aggregated as ( + select + warehouse, + count(*) as row_count, + sum(on_hand) as total_on_hand + from source + group by warehouse +) +select * from aggregated +''' +expect = "refused SQ0003 fn:SUM" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from inventory_snapshots +), +filtered as ( + select * from source + where on_hand is not null +), +aggregated as ( + select + warehouse, + count(*) as row_count, + avg(on_hand) as avg_on_hand, + max(on_hand) as max_on_hand + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "refused SQ0003 fn:AVG" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from web_sessions +), +aggregated as ( + select + device, + count(*) as row_count, + sum(page_views) as total_page_views + from source + group by device +) +select * from aggregated +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from web_sessions +), +filtered as ( + select * from source + where page_views is not null +), +aggregated as ( + select + device, + count(*) as row_count, + avg(page_views) as avg_page_views, + max(page_views) as max_page_views + from filtered + group by 1 +) +select * from aggregated +order by row_count desc +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with source as ( + select * from web_sessions +), +daily as ( + select + date_trunc('day', started_at) as day, + device, + sum(page_views) as daily_page_views + from source + group by 1, 2 +), +final as ( + select + day, + device, + daily_page_views, + sum(daily_page_views) over (partition by device order by day) as cumulative_page_views + from daily +) +select * from final +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with base as ( + select * from web_sessions +), +monthly as ( + select + date_trunc(month, started_at)::date as month, + count(*) as row_count + from base + group by 1 +), +with_growth as ( + select + month, + row_count, + lag(row_count) over (order by month) as previous_count + from monthly +), +final as ( + select + month, + row_count, + previous_count, + row_count - previous_count as change + from with_growth +) +select * from final +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from orders +), +items as ( + select * from order_items +), +products as ( + select * from products +), +order_lines as ( + select + items.order_id, + products.category, + items.quantity * items.unit_price as line_total + from items + inner join products on items.product_id = products.product_id +), +category_revenue as ( + select + orders.channel, + order_lines.category, + sum(order_lines.line_total) as revenue + from order_lines + inner join orders on order_lines.order_id = orders.order_id + group by 1, 2 +) +select * from category_revenue''' +expect = "refused SQ0003 node:mul" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from orders where status != 'cancelled' +), +customers as ( + select * from customers where is_active +), +joined as ( + select + customers.customer_id, + customers.country, + orders.order_id, + orders.total + from customers + join orders on orders.customer_id = customers.customer_id +), +by_country as ( + select country, count(distinct customer_id) as customers, sum(total) as revenue + from joined + group by country +) +select * from by_country''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with recursive numbers as ( + select 1 as n + union all + select n + 1 from numbers where n < 10 +) +select n from numbers''' +expect = "refused SQ0002 node:with" +expect_repository = "refused SQ0002 node:with" + +[[query]] +category = "dbt_cte_chain" +sql = '''with +a as (select customer_id, total from orders), +b as (select customer_id, sum(total) as total from a group by customer_id), +c as (select customer_id, total, rank() over (order by total desc) as rnk from b) +select customer_id, total from c where rnk <= 10''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +), +order_status_counts as ( + select + customer_id, + count_if(status = 'completed') as completed_orders, + count_if(status = 'returned') as returned_orders + from orders + group by customer_id +) +select * from order_status_counts''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +), +payments as ( + select * from stg_payments +), +unpaid as ( + select orders.order_id + from orders + left join payments on payments.order_id = orders.order_id + where payments.payment_id is null +) +select count(*) as unpaid_orders from unpaid''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +) +select * +from orders +where order_id not in (select order_id from stg_payments)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +) +select * +from orders o +where exists (select 1 from stg_payments p where p.order_id = o.order_id)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with tickets as ( + select * from tickets +), +resolution as ( + select + ticket_id, + priority, + datediff('hour', opened_at, closed_at) as hours_to_close + from tickets + where closed_at is not null +) +select + priority, + avg(hours_to_close) as avg_hours_to_close, + median(hours_to_close) as median_hours_to_close +from resolution +group by priority''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with sessions as ( + select * from web_sessions +), +first_touch as ( + select + customer_id, + min_by(attributes:utm_source::varchar, started_at) as first_source + from sessions + group by customer_id +) +select * from first_touch''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_cte_chain" +sql = '''with orders as ( + select * from stg_orders +), +union_all as ( + select order_id, amount, 'order' as source from orders + union all + select payment_id, amount, 'payment' as source from stg_payments +) +select source, sum(amount) as amount from union_all group by source''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select max(updated_at) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at >= (select max(updated_at) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select coalesce(max(updated_at), '1900-01-01') from fct_orders) +''' +expect = "refused SQ0003 fn:COALESCE" +expect_repository = "refused SQ0003 fn:COALESCE" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select coalesce(max(updated_at), '1900-01-01'::timestamp_ntz) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select coalesce(max(updated_at), to_timestamp_ntz('1900-01-01')) from fct_orders) +''' +expect = "refused SQ0008 fn:TO_TIMESTAMP_NTZ" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select dateadd(hour, -3, max(updated_at)) from fct_orders) +''' +expect = "refused SQ0008 fn:DATEADD" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at >= (select dateadd(day, -1, max(updated_at)) from fct_orders) +''' +expect = "refused SQ0008 fn:DATEADD" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select max(updated_at) - interval '3 hours' from fct_orders) +''' +expect = "refused SQ0008 node:sub" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where order_date >= (select max(order_date) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where order_date > (select dateadd('day', -7, max(order_date)) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where order_date >= (select coalesce(max(order_date), '2000-01-01'::date) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where order_id > (select coalesce(max(order_id), 0) from fct_orders) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select max(this.updated_at) from fct_orders as this) +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + order_id, + customer_id, + order_date, + status, + amount, + updated_at +from stg_orders +where updated_at > (select max(updated_at) from fct_orders) or updated_at is null +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''with new_orders as ( + select * from stg_orders + where updated_at > (select max(updated_at) from fct_orders) +) +select * from new_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''with source as ( + select * from stg_orders +), +max_loaded as ( + select max(updated_at) as max_updated_at from fct_orders +) +select source.* +from source +cross join max_loaded +where source.updated_at > max_loaded.max_updated_at''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select s.* +from stg_orders s +left join fct_orders t on s.order_id = t.order_id +where t.order_id is null or s.updated_at > t.updated_at''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from stg_orders +where updated_at > (select max(updated_at) from fct_orders) +qualify row_number() over (partition by order_id order by updated_at desc) = 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from raw_orders +where _loaded_at > (select coalesce(max(updated_at), '1970-01-01') from fct_orders)''' +expect = "refused SQ0003 fn:COALESCE" +expect_repository = "refused SQ0003 fn:COALESCE" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from events +where occurred_at > (select max(occurred_at) from events)''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from events +where occurred_at >= dateadd(day, -3, current_date())''' +expect = "refused SQ0008 node:column" +expect_repository = "refused SQ0003 node:gte" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from events +where occurred_at::date >= current_date() - 3''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from events +where occurred_at >= dateadd(day, -3, (select max(occurred_at) from events))''' +expect = "refused SQ0008 node:column" +expect_repository = "refused SQ0002 node:statement" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from payments +where paid_at between dateadd(day, -7, current_date()) and current_date()''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "refused SQ0003 node:between" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from inventory_snapshots +where snapshot_date = (select max(snapshot_date) from inventory_snapshots)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select * +from inventory_snapshots +where snapshot_date > (select max(snapshot_date) from inventory_snapshots where warehouse = 'east')''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select + customer_id, + max(updated_at) as last_seen_at +from stg_orders +where updated_at > (select max(updated_at) from fct_orders) +group by customer_id''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select order_id, amount +from stg_orders +where updated_at > ( + select max(updated_at) + from fct_orders + where order_date >= dateadd(month, -1, current_date()) +)''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_incremental" +sql = '''select order_id, amount, updated_at +from stg_orders +where updated_at > ifnull((select max(updated_at) from fct_orders), '1900-01-01')''' +expect = "refused SQ0003 fn:IFNULL" +expect_repository = "refused SQ0003 fn:IFNULL" + +[[query]] +category = "dbt_incremental" +sql = '''select order_id, amount, updated_at +from stg_orders +where updated_at > nvl((select max(updated_at) from fct_orders), '1900-01-01'::timestamp_ntz)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from raw_orders +qualify row_number() over (partition by id order by updated_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from raw_orders +qualify row_number() over (partition by id order by _loaded_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from raw_customers +qualify row_number() over (partition by id order by updated_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from raw_customers +qualify row_number() over (partition by email order by _loaded_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from raw_payments +qualify row_number() over (partition by id order by _batched_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from stg_orders +qualify row_number() over (partition by order_id order by updated_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from stg_customers +qualify row_number() over (partition by customer_id order by updated_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from subscriptions +qualify row_number() over (partition by subscription_id order by updated_at desc) = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from events +qualify row_number() over (partition by event_id order by occurred_at desc) = 1 +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * +from web_sessions +qualify row_number() over (partition by session_id order by started_at desc) = 1 +''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by id order by updated_at desc) as row_num + from raw_orders +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by id order by _loaded_at desc) as row_num + from raw_orders +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by id order by updated_at desc) as row_num + from raw_customers +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by email order by _loaded_at desc) as row_num + from raw_customers +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by id order by _batched_at desc) as row_num + from raw_payments +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''with ranked as ( + select + *, + row_number() over (partition by order_id order by updated_at desc) as row_num + from stg_orders +) +select * from ranked where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * exclude (row_num) +from ( + select *, row_number() over (partition by id order by updated_at desc) as row_num + from raw_orders +) +where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * exclude (row_num) +from ( + select *, row_number() over (partition by id order by _loaded_at desc) as row_num + from raw_orders +) +where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * exclude (row_num) +from ( + select *, row_number() over (partition by id order by updated_at desc) as row_num + from raw_customers +) +where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select * exclude (row_num) +from ( + select *, row_number() over (partition by email order by _loaded_at desc) as row_num + from raw_customers +) +where row_num = 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, user_id, status, updated_at +from raw_orders +qualify rank() over (partition by id order by updated_at desc) = 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, user_id, status, updated_at +from raw_orders +qualify updated_at = max(updated_at) over (partition by id)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, user_id, status, updated_at +from raw_orders +qualify row_number() over (partition by id order by updated_at desc, _loaded_at desc) = 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, user_id, status, updated_at +from raw_orders +qualify row_number() over (partition by id order by updated_at desc nulls last) = 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select distinct id, user_id, status from raw_orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, any_value(user_id) as user_id, max(updated_at) as updated_at +from raw_orders +group by id''' +expect = "refused SQ0008 fn:ANY_VALUE" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, max_by(status, updated_at) as status, max(updated_at) as updated_at +from raw_orders +group by id''' +expect = "refused SQ0008 fn:MAX_BY" +expect_repository = "translated" + +[[query]] +category = "dbt_dedup" +sql = '''select id, status +from raw_orders +qualify count(*) over (partition by id) > 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + events.event_id, + item.value:sku::varchar as sku, + item.value:quantity::number as quantity +from events, + lateral flatten(input => events.payload:items) as item''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.value:sku::string as sku, + f.value:price::number(10,2) as price +from events e, + lateral flatten(input => e.payload:items) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.index as position, + f.value::varchar as tag +from events e, + lateral flatten(input => e.payload:tags) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.value::varchar as tag +from events e, + lateral flatten(input => e.payload:tags, outer => true) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + t.ticket_id, + tag.value::varchar as tag +from tickets t, + lateral flatten(input => t.tags) tag''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + t.ticket_id, + tag.value::varchar as tag +from tickets t, + table(flatten(input => t.tags)) tag''' +expect = "refused SQ0008 node:column" +expect_repository = "refused SQ0002 fn:FLATTEN" + +[[query]] +category = "dbt_flatten" +sql = '''select + tag.value::varchar as tag, + count(*) as tickets +from tickets t, + lateral flatten(input => t.tags) tag +group by 1 +order by 2 desc''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + attr.key as attribute, + attr.value::varchar as value +from events e, + lateral flatten(input => e.payload:attributes) attr''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + s.session_id, + a.key as attribute, + a.value::varchar as value +from web_sessions s, + lateral flatten(input => s.attributes) a''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + item.value:sku::varchar as sku, + option.value::varchar as option +from events e, + lateral flatten(input => e.payload:items) item, + lateral flatten(input => item.value:options) option''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + r.raw:id::varchar as event_id, + f.value:name::varchar as product_name +from raw_events r, + lateral flatten(input => r.raw:products) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + r.raw:id::varchar as event_id, + f.path, + f.value +from raw_events r, + lateral flatten(input => r.raw, recursive => true) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_flatten" +sql = '''select + f.value:sku::varchar as sku, + sum(f.value:quantity::number) as units +from events e, + lateral flatten(input => e.payload:items) f +where e.event_type = 'purchase' +group by 1''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + array_size(e.payload:items) as item_count +from events e''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + e.payload:items[0]:sku::varchar as first_sku +from events e''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + array_agg(f.value:sku::varchar) within group (order by f.index) as skus +from events e, + lateral flatten(input => e.payload:items) f +group by e.event_id''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.customer_id, + object_agg(e.event_type, e.payload) as last_payload_by_type +from events e +group by e.customer_id''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.value:sku::varchar as sku +from events e +join lateral flatten(input => e.payload:items) f''' +expect = "refused SQ0008 node:column" +expect_repository = "refused SQ0002 node:lateral" +snowflake = "compiles" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.value:sku::varchar as sku +from events e +inner join lateral flatten(input => e.payload:items) f on true''' +expect = "check SQ0209" +expect_repository = "check SQ0209" +snowflake = "rejects 2" + +[[query]] +category = "dbt_flatten" +sql = '''select + f.value::varchar as tag +from table(flatten(input => parse_json('["a", "b", "c"]'))) f''' +expect = "refused SQ0002 fn:FLATTEN" +expect_repository = "refused SQ0002 fn:FLATTEN" + +[[query]] +category = "dbt_flatten" +sql = '''select + f.value::number as n +from table(flatten(input => array_construct(1, 2, 3))) f''' +expect = "refused SQ0002 fn:FLATTEN" +expect_repository = "refused SQ0002 fn:FLATTEN" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.value:sku::varchar as sku, + f.value:quantity::number * f.value:price::number(10,2) as line_total +from events e, + lateral flatten(input => e.payload:items) f +where f.value:quantity::number > 0''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.this +from events e, + lateral flatten(input => e.payload:items) f''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + e.event_id, + f.seq, + f.key, + f.index, + f.value +from events e, + lateral flatten(input => e.payload) f''' +expect = "refused SQ0008 node:column" +expect_repository = "refused SQ0003 node:column" + +[[query]] +category = "dbt_flatten" +sql = '''select + t.ticket_id, + s.value::varchar as word +from tickets t, + lateral split_to_table(t.subject, ' ') s''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_flatten" +sql = '''select + t.ticket_id, + array_to_string(t.tags, ', ') as tag_list +from tickets t''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "ddl_dml" +sql = '''merge into fct_orders as target +using ( + select * from stg_orders + where updated_at > (select max(updated_at) from fct_orders) +) as source +on target.order_id = source.order_id +when matched then update set + target.status = source.status, + target.amount = source.amount, + target.updated_at = source.updated_at +when not matched then insert (order_id, customer_id, order_date, status, amount, updated_at) + values (source.order_id, source.customer_id, source.order_date, source.status, source.amount, source.updated_at)''' +expect = "refused SQ0002 node:merge" +expect_repository = "refused SQ0002 node:merge" + +[[query]] +category = "ddl_dml" +sql = '''merge into fct_orders t +using stg_orders s +on t.order_id = s.order_id +when matched and s.status = 'deleted' then delete +when matched then update set status = s.status, amount = s.amount, updated_at = s.updated_at +when not matched then insert values (s.order_id, s.customer_id, s.order_date, s.status, s.amount, s.updated_at)''' +expect = "refused SQ0002 node:merge" +expect_repository = "refused SQ0002 node:merge" + +[[query]] +category = "ddl_dml" +sql = '''merge into fct_orders t +using (select * from stg_orders qualify row_number() over (partition by order_id order by updated_at desc) = 1) s +on t.order_id = s.order_id +when matched then update set t.amount = s.amount +when not matched then insert (order_id, amount) values (s.order_id, s.amount)''' +expect = "refused SQ0002 node:merge" +expect_repository = "refused SQ0002 node:merge" + +[[query]] +category = "ddl_dml" +sql = '''delete from fct_orders +where order_id in (select order_id from stg_orders where updated_at > (select max(updated_at) from fct_orders))''' +expect = "refused SQ0002 node:delete" +expect_repository = "refused SQ0002 node:delete" + +[[query]] +category = "ddl_dml" +sql = '''insert into fct_orders (order_id, customer_id, order_date, status, amount, updated_at) +select order_id, customer_id, order_date, status, amount, updated_at +from stg_orders +where updated_at > (select max(updated_at) from fct_orders)''' +expect = "refused SQ0002 node:insert" +expect_repository = "refused SQ0002 node:insert" + +[[query]] +category = "ddl_dml" +sql = '''insert overwrite into fct_orders +select * from stg_orders''' +expect = "refused SQ0002 node:insert" +expect_repository = "refused SQ0002 node:insert" + +[[query]] +category = "ddl_dml" +sql = '''create or replace table fct_orders as ( + select * from stg_orders +)''' +expect = "refused SQ0002 node:create_table" +expect_repository = "refused SQ0002 node:create_table" + +[[query]] +category = "ddl_dml" +sql = '''create or replace transient table fct_orders as +select * from stg_orders''' +expect = "refused SQ0002 node:create_table" +expect_repository = "refused SQ0002 node:create_table" + +[[query]] +category = "ddl_dml" +sql = '''create or replace view stg_orders_v as +select order_id, status from stg_orders''' +expect = "refused SQ0002 node:create_view" +expect_repository = "refused SQ0002 node:create_view" + +[[query]] +category = "ddl_dml" +sql = '''create or replace temporary table fct_orders__dbt_tmp as +select * from stg_orders where updated_at > (select max(updated_at) from fct_orders)''' +expect = "refused SQ0002 node:create_table" +expect_repository = "refused SQ0002 node:create_table" + +[[query]] +category = "ddl_dml" +sql = '''update fct_orders +set status = 'archived' +where order_date < dateadd(year, -2, current_date())''' +expect = "refused SQ0002 node:update" +expect_repository = "refused SQ0002 node:update" + +[[query]] +category = "ddl_dml" +sql = '''update fct_orders t +set amount = s.amount +from stg_orders s +where t.order_id = s.order_id''' +expect = "refused SQ0002 node:update" +expect_repository = "refused SQ0002 node:update" + +[[query]] +category = "ddl_dml" +sql = '''alter table fct_orders add column if not exists channel varchar''' +expect = "refused SQ0002 node:alter_table" +expect_repository = "refused SQ0002 node:alter_table" + +[[query]] +category = "ddl_dml" +sql = '''truncate table fct_orders''' +expect = "refused SQ0002 node:truncate" +expect_repository = "refused SQ0002 node:truncate" + +[[query]] +category = "ddl_dml" +sql = '''begin; +delete from fct_orders where order_date = current_date(); +insert into fct_orders select * from stg_orders where order_date = current_date(); +commit;''' +expect = "refused SQ0002 node:transaction" +expect_repository = "refused SQ0002 node:transaction" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, seq4(), '2024-01-01'::date) as date_day +from table(generator(rowcount => 366))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, row_number() over (order by seq4()) - 1, '2024-01-01'::date) as date_day +from table(generator(rowcount => 366))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd('day', seq4(), to_date('2023-01-01')) as date_day +from table(generator(rowcount => 730))''' +expect = "refused SQ0008 fn:TO_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(month, seq4(), '2020-01-01'::date) as month_start +from table(generator(rowcount => 72))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + seq4() as n +from table(generator(rowcount => 10))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + uniform(1, 100, random()) as roll +from table(generator(rowcount => 5))''' +expect = "refused SQ0002 fn:UNIFORM" +expect_repository = "refused SQ0002 fn:UNIFORM" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select date_day +from spine +where date_day <= current_date()''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +), +daily_orders as ( + select ordered_at::date as order_day, sum(total) as revenue + from orders + group by 1 +) +select + spine.date_day, + coalesce(daily_orders.revenue, 0) as revenue +from spine +left join daily_orders on daily_orders.order_day = spine.date_day''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select + date_day, + year(date_day) as year, + quarter(date_day) as quarter, + month(date_day) as month, + dayofweek(date_day) as day_of_week, + dayname(date_day) as day_name, + monthname(date_day) as month_name, + weekofyear(date_day) as week_of_year, + dayofweek(date_day) in (0, 6) as is_weekend +from spine''' +expect = "refused SQ0002 fn:DAYOFWEEK" +expect_repository = "refused SQ0002 fn:DAYOFWEEK" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select + date_day, + date_trunc('week', date_day) as week_start, + last_day(date_day) as month_end, + date_day = last_day(date_day) as is_month_end +from spine''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +), +customers_by_day as ( + select spine.date_day, count(c.customer_id) as active_customers + from spine + left join customers c on c.signup_date <= spine.date_day + group by 1 +) +select * from customers_by_day''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select + spine.date_day, + count(s.subscription_id) as active_subscriptions, + sum(s.mrr) as mrr +from spine +left join subscriptions s + on s.started_at::date <= spine.date_day + and (s.cancelled_at is null or s.cancelled_at::date > spine.date_day) +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +), +products_by_day as ( + select spine.date_day, p.product_id + from spine + cross join products p +) +select + products_by_day.date_day, + products_by_day.product_id, + coalesce(i.on_hand, 0) as on_hand +from products_by_day +left join inventory_snapshots i + on i.product_id = products_by_day.product_id + and i.snapshot_date = products_by_day.date_day''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select + spine.date_day, + last_value(f.rate_to_usd ignore nulls) over (order by spine.date_day rows between unbounded preceding and current row) as rate_to_usd +from spine +left join fx_rates f on f.rate_date = spine.date_day and f.currency = 'EUR'''' +expect = "translated" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(hour, seq4(), '2024-01-01'::timestamp_ntz) as hour_start +from table(generator(rowcount => 24 * 7))''' +expect = "refused SQ0003 fn:GENERATOR" +expect_repository = "refused SQ0003 fn:GENERATOR" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, seq4(), current_date() - 30) as date_day +from table(generator(rowcount => 31))''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + row_number() over (order by null) as id +from table(generator(rowcount => 100))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + seq8() as id +from table(generator(rowcount => 100))''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, value::int, '2024-01-01'::date) as date_day +from table(flatten(input => array_generate_range(0, 366)))''' +expect = "refused SQ0002 fn:FLATTEN" +expect_repository = "refused SQ0002 fn:FLATTEN" +snowflake = "compiles" + +[[query]] +category = "dbt_date_spine" +sql = '''with recursive spine as ( + select '2024-01-01'::date as date_day + union all + select dateadd(day, 1, date_day) from spine where date_day < '2024-12-31' +) +select date_day from spine''' +expect = "refused SQ0002 node:with" +expect_repository = "refused SQ0002 node:with" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(week, seq4(), '2024-01-01'::date) as week_start + from table(generator(rowcount => 52)) +) +select + spine.week_start, + count(o.order_id) as orders +from spine +left join orders o + on o.ordered_at >= spine.week_start + and o.ordered_at < dateadd(week, 1, spine.week_start) +group by 1 +order by 1''' +expect = "refused SQ0003 node:gte" +expect_repository = "refused SQ0003 node:gte" + +[[query]] +category = "dbt_date_spine" +sql = '''with spine as ( + select dateadd(day, seq4(), '2024-01-01'::date) as date_day + from table(generator(rowcount => 366)) +) +select + date_day, + to_char(date_day, 'YYYY-MM') as year_month, + to_char(date_day, 'DY') as day_short +from spine''' +expect = "refused SQ0003 fn:TO_CHAR" +expect_repository = "refused SQ0003 fn:TO_CHAR" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(minute, 15 * seq4(), '2024-01-01'::timestamp_ntz) as slot_start +from table(generator(rowcount => 96))''' +expect = "refused SQ0008 fn:DATEADD" +expect_repository = "translated" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, seq4(), '2024-01-01') as date_day +from table(generator(rowcount => 10))''' +expect = "refused SQ0008 fn:DATEADD" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_date_spine" +sql = '''select + dateadd(day, seq4(), '2024-01-01'::date) as date_day +from table(generator(timelimit => 1))''' +expect = "refused SQ0003 fn:GENERATOR" +expect_repository = "refused SQ0003 fn:GENERATOR" +snowflake = "compiles" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id order by ordered_at rows between unbounded preceding and current row) as running_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + avg(total) over (partition by customer_id order by ordered_at rows between 2 preceding and current row) as moving_avg_3 +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + avg(total) over (order by ordered_at rows between 6 preceding and current row) as moving_avg_7 +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id order by ordered_at rows between current row and unbounded following) as remaining_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id order by ordered_at rows between 1 preceding and 1 following) as neighbour_sum +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + min(total) over (partition by customer_id order by ordered_at rows between unbounded preceding and unbounded following) as min_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + count(*) over (partition by customer_id order by ordered_at rows between unbounded preceding and 1 preceding) as prior_orders +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id order by ordered_at range between unbounded preceding and current row) as running_total_range +from orders +''' +expect = "refused SQ0003 fn:SUM" +expect_repository = "refused SQ0003 fn:SUM" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id order by ordered_at) as running_total_default +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(total) over (partition by customer_id) as customer_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + total / sum(total) over (partition by customer_id) as share_of_customer +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + ratio_to_report(total) over (partition by customer_id) as ratio +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + lag(total) over (partition by customer_id order by ordered_at) as previous_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + lag(total, 1, 0) over (partition by customer_id order by ordered_at) as previous_total_or_zero +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + lead(ordered_at) over (partition by customer_id order by ordered_at) as next_ordered_at +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + datediff('day', lag(ordered_at) over (partition by customer_id order by ordered_at), ordered_at) as days_since_previous +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + first_value(total) over (partition by customer_id order by ordered_at) as first_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + last_value(total) over (partition by customer_id order by ordered_at rows between unbounded preceding and unbounded following) as last_total +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + nth_value(total, 2) over (partition by customer_id order by ordered_at) as second_total +from orders +''' +expect = "refused SQ0002 fn:NTH_VALUE" +expect_repository = "refused SQ0002 fn:NTH_VALUE" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + row_number() over (partition by customer_id order by ordered_at) as order_sequence +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + rank() over (order by total desc) as total_rank +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + dense_rank() over (partition by channel order by total desc) as channel_rank +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + percent_rank() over (order by total) as total_percentile +from orders +''' +expect = "refused SQ0002 fn:PERCENT_RANK" +expect_repository = "refused SQ0002 fn:PERCENT_RANK" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + cume_dist() over (order by total) as total_cume_dist +from orders +''' +expect = "refused SQ0002 fn:CUME_DIST" +expect_repository = "refused SQ0002 fn:CUME_DIST" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + ntile(4) over (order by total) as total_quartile +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + count(distinct status) over (partition by customer_id) as distinct_statuses +from orders +''' +expect = "translated" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + max(total) over (partition by customer_id order by ordered_at rows between 3 preceding and 1 preceding) as max_prior_three +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + sum(iff(status = 'returned', 1, 0)) over (partition by customer_id order by ordered_at) as returns_so_far +from orders +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + conditional_true_event(datediff('minute', lag(ordered_at) over (partition by customer_id order by ordered_at), ordered_at) > 30) over (partition by customer_id order by ordered_at) as session_number +from orders +''' +expect = "refused SQ0002 fn:CONDITIONAL_TRUE_EVENT" +expect_repository = "refused SQ0002 fn:CONDITIONAL_TRUE_EVENT" +snowflake = "compiles" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + avg(total) over (partition by customer_id order by ordered_at range between interval '7 days' preceding and current row) as avg_7_days +from orders +''' +expect = "refused SQ0003 fn:AVG" +expect_repository = "refused SQ0003 fn:AVG" +snowflake = "compiles" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + lag(total) ignore nulls over (partition by customer_id order by ordered_at) as previous_non_null +from orders +''' +expect = "translated" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + customer_id, + ordered_at, + total, + median(total) over (partition by customer_id) as median_total +from orders +''' +expect = "refused SQ0002 fn:MEDIAN" +expect_repository = "refused SQ0002 fn:MEDIAN" + +[[query]] +category = "dbt_window_frames" +sql = '''select + order_id, + sum(total) over w as running_total, + avg(total) over w as running_avg +from orders +window w as (partition by customer_id order by ordered_at)''' +expect = "parse SQ0001" +expect_repository = "parse SQ0001" +snowflake = "rejects 1003" + +[[query]] +category = "dbt_window_frames" +sql = '''with sessions as ( + select + customer_id, + started_at, + case + when datediff('minute', lag(started_at) over (partition by customer_id order by started_at), started_at) > 30 + or lag(started_at) over (partition by customer_id order by started_at) is null + then 1 else 0 + end as is_new_session + from web_sessions +) +select + customer_id, + started_at, + sum(is_new_session) over (partition by customer_id order by started_at rows between unbounded preceding and current row) as session_index +from sessions''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_window_frames" +sql = '''with daily as ( + select ordered_at::date as day, sum(total) as revenue + from orders + group by 1 +) +select + day, + revenue, + avg(revenue) over (order by day rows between 6 preceding and current row) as revenue_7d_avg, + sum(revenue) over (order by day rows between 27 preceding and current row) as revenue_28d +from daily''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select order_id, payment_method, amount from stg_payments) +pivot (sum(amount) for payment_method in ('credit_card', 'coupon', 'bank_transfer', 'gift_card'))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select order_id, payment_method, amount from stg_payments) +pivot (sum(amount) for payment_method in ('credit_card', 'coupon', 'bank_transfer', 'gift_card')) + as p (order_id, credit_card_amount, coupon_amount, bank_transfer_amount, gift_card_amount)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select customer_id, channel, total from orders) +pivot (count(total) for channel in ('web', 'store', 'phone'))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select customer_id, channel, total from orders) +pivot (sum(total) for channel in (any order by channel))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" +snowflake = "compiles" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select customer_id, channel, total from orders) +pivot (sum(total) for channel in (select distinct channel from orders))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" +snowflake = "compiles" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select customer_id, channel, total from orders) +pivot (sum(total) for channel in ('web', 'store') default on null (0))''' +expect = "refused SQ0002 node:pivot" +expect_repository = "refused SQ0002 node:pivot" +snowflake = "compiles" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from (select warehouse, product_id, on_hand from inventory_snapshots where snapshot_date = '2024-06-30') +pivot (sum(on_hand) for warehouse in ('east', 'west'))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" + +[[query]] +category = "dbt_pivot" +sql = '''select + order_id, + sum(case when payment_method = 'credit_card' then amount end) as credit_card_amount, + sum(case when payment_method = 'coupon' then amount end) as coupon_amount +from stg_payments +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select + customer_id, + count_if(channel = 'web') as web_orders, + count_if(channel = 'store') as store_orders +from orders +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select + customer_id, + sum(iff(channel = 'web', total, 0)) as web_total, + sum(iff(channel = 'store', total, 0)) as store_total +from orders +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select + date_trunc('month', ordered_at)::date as month, + sum(case when status = 'completed' then 1 else 0 end) as completed, + sum(case when status = 'returned' then 1 else 0 end) as returned, + sum(case when status = 'cancelled' then 1 else 0 end) as cancelled +from orders +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select order_id, metric, value +from (select order_id, total, discount from orders) +unpivot (value for metric in (total, discount))''' +expect = "refused SQ0002 node:unpivot" +expect_repository = "refused SQ0002 node:unpivot" + +[[query]] +category = "dbt_pivot" +sql = '''select customer_id, metric, value +from ( + select customer_id, sum(total) as revenue, sum(discount) as discounts + from orders + group by 1 +) +unpivot (value for metric in (revenue, discounts))''' +expect = "refused SQ0002 node:unpivot" +expect_repository = "refused SQ0002 node:unpivot" + +[[query]] +category = "dbt_pivot" +sql = '''select order_id, metric, value +from (select order_id, total, discount from orders) +unpivot include nulls (value for metric in (total, discount))''' +expect = "refused SQ0002 node:unpivot" +expect_repository = "refused SQ0002 node:unpivot" +snowflake = "compiles" + +[[query]] +category = "dbt_pivot" +sql = '''select order_id, 'total' as metric, total as value from orders +union all +select order_id, 'discount' as metric, discount as value from orders''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_pivot" +sql = '''select o.order_id, m.metric, m.value +from orders o, + lateral ( + select 'total' as metric, o.total as value + union all + select 'discount', o.discount + ) m''' +expect = "translated" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_pivot" +sql = '''select customer_id, channel, total_web, total_store +from (select customer_id, channel, total from orders) +pivot (sum(total) for channel in ('web' as total_web, 'store' as total_store))''' +expect = "check SQ0208" +expect_repository = "check SQ0208" +snowflake = "rejects 904" + +[[query]] +category = "dbt_pivot" +sql = '''select * +from orders +pivot (sum(total) for channel in ('web', 'store'))''' +expect = "refused SQ0003 node:pivot" +expect_repository = "refused SQ0003 node:pivot" + +[[query]] +category = "dbt_marts" +sql = '''with orders as ( + select * from fct_orders +), +customers as ( + select * from stg_customers +), +cohorts as ( + select + customer_id, + date_trunc('month', min(order_date)) as cohort_month + from orders + group by 1 +), +activity as ( + select + o.customer_id, + c.cohort_month, + datediff('month', c.cohort_month, date_trunc('month', o.order_date)) as months_since_first + from orders o + join cohorts c using (customer_id) +) +select + cohort_month, + months_since_first, + count(distinct customer_id) as active_customers +from activity +group by 1, 2 +order by 1, 2''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + c.customer_id, + c.email, + count(o.order_id) as lifetime_orders, + sum(o.amount) as lifetime_value, + min(o.order_date) as first_order_date, + max(o.order_date) as last_order_date, + datediff('day', max(o.order_date), current_date()) as days_since_last_order +from stg_customers c +left join fct_orders o on o.customer_id = c.customer_id +group by 1, 2''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + c.customer_id, + case + when count(o.order_id) = 0 then 'prospect' + when count(o.order_id) = 1 then 'new' + when max(o.order_date) < dateadd(day, -90, current_date()) then 'lapsed' + else 'repeat' + end as lifecycle_stage +from stg_customers c +left join fct_orders o on o.customer_id = c.customer_id +group by 1''' +expect = "refused SQ0008 fn:CURRENT_DATE" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.order_id, + o.customer_id, + o.order_date, + o.amount, + o.amount * fx.rate_to_usd as amount_usd +from fct_orders o +left join fx_rates fx + on fx.rate_date = o.order_date + and fx.currency = 'EUR'''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + p.category, + date_trunc('month', o.ordered_at) as month, + sum(i.quantity) as units, + sum(i.quantity * i.unit_price) as revenue, + div0(sum(i.quantity * i.unit_price), sum(i.quantity)) as avg_unit_price +from order_items i +join orders o on o.order_id = i.order_id +join products p on p.product_id = i.product_id +group by 1, 2''' +expect = "refused SQ0003 fn:SUM" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + p.category, + sum(i.quantity * i.unit_price) as revenue, + sum(i.quantity * i.unit_price) / nullif(sum(sum(i.quantity * i.unit_price)) over (), 0) as share_of_revenue +from order_items i +join products p on p.product_id = i.product_id +group by 1''' +expect = "refused SQ0003 node:mul" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + plan, + date_trunc('month', started_at)::date as start_month, + count(*) as new_subscriptions, + sum(mrr) as new_mrr +from subscriptions +group by 1, 2''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + date_trunc('month', cancelled_at)::date as churn_month, + count(*) as churned, + sum(mrr) as churned_mrr +from subscriptions +where cancelled_at is not null +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + s.customer_id, + s.plan, + s.mrr, + s.mrr * 12 as arr, + datediff('month', s.started_at, coalesce(s.cancelled_at, current_timestamp())) as tenure_months +from subscriptions s''' +expect = "refused SQ0008 fn:CURRENT_TIMESTAMP" +expect_repository = "refused SQ0003 fn:DATEDIFF" + +[[query]] +category = "dbt_marts" +sql = '''select + o.channel, + count(distinct o.customer_id) as customers, + count(*) as orders, + sum(o.total) as revenue, + round(sum(o.total) / count(*), 2) as avg_order_value +from orders o +where o.status not in ('cancelled', 'returned') +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.order_id, + o.total, + coalesce(sum(p.amount), 0) as paid, + o.total - coalesce(sum(p.amount), 0) as outstanding +from orders o +left join payments p on p.order_id = o.order_id +group by o.order_id, o.total +having outstanding <> 0''' +expect = "translated" +expect_repository = "translated" +snowflake = "compiles" + +[[query]] +category = "dbt_marts" +sql = '''select + t.priority, + count(*) as tickets, + count_if(t.closed_at is null) as open_tickets, + avg(datediff('hour', t.opened_at, t.closed_at)) as avg_hours_to_close +from tickets t +group by 1''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + c.country, + count(distinct t.ticket_id) as tickets, + count(distinct o.order_id) as orders, + div0(count(distinct t.ticket_id), count(distinct o.order_id)) as tickets_per_order +from customers c +left join tickets t on t.customer_id = c.customer_id +left join orders o on o.customer_id = c.customer_id +group by 1''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + s.device, + count(*) as sessions, + sum(s.page_views) as page_views, + avg(s.page_views) as avg_page_views +from web_sessions s +group by 1''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + s.attributes:utm_source::varchar as source, + count(distinct s.customer_id) as visitors, + count(distinct o.customer_id) as buyers +from web_sessions s +left join orders o + on o.customer_id = s.customer_id + and o.ordered_at between s.started_at and dateadd(day, 7, s.started_at) +group by 1''' +expect = "refused SQ0008 node:column" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + i.warehouse, + i.snapshot_date, + sum(i.on_hand) as units_on_hand, + sum(i.on_hand * p.price) as stock_value +from inventory_snapshots i +join products p on p.product_id = i.product_id +group by 1, 2''' +expect = "refused SQ0003 fn:SUM" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + product_id, + snapshot_date, + on_hand, + on_hand - lag(on_hand) over (partition by product_id, warehouse order by snapshot_date) as change +from inventory_snapshots''' +expect = "refused SQ0003 node:sub" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + date_trunc('week', ordered_at)::date as week, + approx_count_distinct(customer_id) as approx_customers, + count(distinct customer_id) as customers +from orders +group by 1''' +expect = "refused SQ0002 fn:APPROX_COUNT_DISTINCT" +expect_repository = "refused SQ0002 fn:APPROX_COUNT_DISTINCT" + +[[query]] +category = "dbt_marts" +sql = '''select + customer_id, + listagg(distinct channel, ', ') within group (order by channel) as channels +from orders +group by 1''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + customer_id, + percentile_cont(0.5) within group (order by total) as median_order +from orders +group by 1''' +expect = "refused SQ0008 fn:PERCENTILE_CONT" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.order_id, + o.customer_id, + o.total, + c.email, + c.country +from orders o +left join customers c on c.customer_id = o.customer_id +where c.customer_id is null or c.is_active''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.order_id, + datediff('hour', o.ordered_at, o.shipped_at) as hours_to_ship, + iff(datediff('hour', o.ordered_at, o.shipped_at) <= 48, 'on_time', 'late') as shipping_sla +from orders o +where o.shipped_at is not null''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.customer_id, + o.order_id, + o.total, + sum(o.total) over (partition by o.customer_id) as customer_total, + o.total / nullif(sum(o.total) over (partition by o.customer_id), 0) as share +from orders o''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + year(ordered_at) as year, + month(ordered_at) as month, + sum(total) as revenue, + sum(total) - lag(sum(total)) over (order by year(ordered_at), month(ordered_at)) as revenue_change +from orders +group by 1, 2''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + customer_id, + array_agg(distinct channel) as channels, + array_size(array_agg(distinct channel)) as channel_count +from orders +group by 1''' +expect = "refused SQ0008 fn:ARRAY_AGG" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + c.customer_id, + object_construct('email', c.email, 'country', c.country) as profile +from customers c''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + c.customer_id, + to_json(object_construct('email', c.email)) as profile_json +from customers c''' +expect = "refused SQ0008 fn:TO_JSON" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.customer_id, + sum(o.total) as revenue +from orders o +group by all''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_marts" +sql = '''select + o.channel, + o.status, + count(*) as orders +from orders o +group by rollup (o.channel, o.status)''' +expect = "refused SQ0002 fn:ROLLUP" +expect_repository = "refused SQ0002 fn:ROLLUP" + +[[query]] +category = "dbt_marts" +sql = '''select + o.channel, + o.status, + count(*) as orders, + grouping(o.channel) as channel_rollup +from orders o +group by grouping sets ((o.channel), (o.status), ())''' +expect = "refused SQ0002 fn:GROUPING SETS" +expect_repository = "refused SQ0002 fn:GROUPING SETS" + +[[query]] +category = "dbt_snapshot" +sql = '''select + customer_id, + email, + country, + updated_at as dbt_valid_from, + lead(updated_at) over (partition by customer_id order by updated_at) as dbt_valid_to +from stg_customers''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select + customer_id, + email, + updated_at as valid_from, + coalesce(lead(updated_at) over (partition by customer_id order by updated_at), '9999-12-31'::timestamp_ntz) as valid_to, + lead(updated_at) over (partition by customer_id order by updated_at) is null as is_current +from stg_customers''' +expect = "refused SQ0003 node:cast" +expect_repository = "refused SQ0003 node:cast" + +[[query]] +category = "dbt_snapshot" +sql = '''select + md5(coalesce(cast(customer_id as varchar), '') || '|' || coalesce(cast(updated_at as varchar), '')) as dbt_scd_id, + customer_id, + updated_at as dbt_updated_at +from stg_customers''' +expect = "refused SQ0003 node:cast" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''with history as ( + select + customer_id, + email, + updated_at as valid_from, + lead(updated_at) over (partition by customer_id order by updated_at) as valid_to + from stg_customers +) +select * +from history +where '2024-06-30'::timestamp_ntz >= valid_from + and ('2024-06-30'::timestamp_ntz < valid_to or valid_to is null)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select + o.order_id, + h.email as email_at_order_time +from fct_orders o +left join ( + select + customer_id, + email, + updated_at as valid_from, + lead(updated_at) over (partition by customer_id order by updated_at) as valid_to + from stg_customers +) h + on h.customer_id = o.customer_id + and o.updated_at >= h.valid_from + and (o.updated_at < h.valid_to or h.valid_to is null)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select + customer_id, + email, + lag(email) over (partition by customer_id order by updated_at) as previous_email +from stg_customers +qualify email is distinct from previous_email''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select + customer_id, + email, + updated_at +from stg_customers +qualify email <> lag(email) over (partition by customer_id order by updated_at) + or lag(email) over (partition by customer_id order by updated_at) is null''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select + s.customer_id, + s.email, + s.updated_at +from stg_customers s +where not exists ( + select 1 + from stg_customers newer + where newer.customer_id = s.customer_id + and newer.updated_at > s.updated_at +)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_snapshot" +sql = '''select * +from stg_customers +at (timestamp => '2024-06-30 00:00:00'::timestamp_ltz)''' +expect = "refused SQ0002 node:table" +expect_repository = "refused SQ0002 node:table" +snowflake = "compiles" + +[[query]] +category = "dbt_snapshot" +sql = '''select * +from stg_customers +changes (information => default) +at (offset => -3600)''' +expect = "refused SQ0002 node:table" +expect_repository = "refused SQ0002 node:table" +snowflake = "rejects 91930" + +[[query]] +category = "dbt_tests" +sql = '''select order_id +from stg_orders +where order_id is null +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select + order_id as unique_field, + count(*) as n_records +from stg_orders +where order_id is not null +group by order_id +having count(*) > 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select customer_id +from stg_customers +where customer_id is null +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select + customer_id as unique_field, + count(*) as n_records +from stg_customers +where customer_id is not null +group by customer_id +having count(*) > 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select payment_id +from stg_payments +where payment_id is null +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select + payment_id as unique_field, + count(*) as n_records +from stg_payments +where payment_id is not null +group by payment_id +having count(*) > 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select order_id +from fct_orders +where order_id is null +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select + order_id as unique_field, + count(*) as n_records +from fct_orders +where order_id is not null +group by order_id +having count(*) > 1 +''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''with all_values as ( + select status as value_field, count(*) as n_records + from stg_orders + group by status +) +select * +from all_values +where value_field not in ('placed', 'shipped', 'completed', 'return_pending', 'returned')''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''with child as ( + select customer_id as from_field + from stg_orders + where customer_id is not null +), +parent as ( + select customer_id as to_field + from stg_customers +) +select from_field +from child +left join parent on child.from_field = parent.to_field +where parent.to_field is null''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select count(*) as failures, count(*) != 0 as should_warn, count(*) != 0 as should_error +from ( + select order_id from stg_orders where amount < 0 +) dbt_internal_test''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select * +from stg_orders +where not (amount >= 0)''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select * +from ( + select order_id, sum(amount) as payments + from stg_payments + group by 1 +) +where payments < 0''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select max(updated_at) as max_updated_at +from stg_orders +having max(updated_at) < dateadd(hour, -24, current_timestamp())''' +expect = "refused SQ0008 fn:CURRENT_TIMESTAMP" +expect_repository = "refused SQ0003 fn:DATEADD" + +[[query]] +category = "dbt_tests" +sql = '''select + count(*) as row_count +from stg_orders +having count(*) = 0''' +expect = "translated" +expect_repository = "translated" + +[[query]] +category = "dbt_tests" +sql = '''select order_id, count(*) +from stg_payments +group by order_id +having count(*) > 1 and sum(amount) = 0''' +expect = "refused SQ0003 node:aggregate_function" +expect_repository = "refused SQ0003 node:aggregate_function" diff --git a/corpora/everyday-models/schema.json b/corpora/everyday-models/schema.json new file mode 100644 index 000000000..19a5e9f8a --- /dev/null +++ b/corpora/everyday-models/schema.json @@ -0,0 +1,480 @@ +{ + "CUSTOMERS": [ + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "FIRST_NAME", + "VARCHAR" + ], + [ + "LAST_NAME", + "VARCHAR" + ], + [ + "EMAIL", + "VARCHAR" + ], + [ + "COUNTRY", + "VARCHAR" + ], + [ + "SIGNUP_DATE", + "DATE" + ], + [ + "IS_ACTIVE", + "BOOLEAN" + ], + [ + "CREATED_AT", + "TIMESTAMP_NTZ" + ] + ], + "ORDERS": [ + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "STATUS", + "VARCHAR" + ], + [ + "TOTAL", + "NUMBER(12,2)" + ], + [ + "DISCOUNT", + "NUMBER(12,2)" + ], + [ + "ORDERED_AT", + "TIMESTAMP_NTZ" + ], + [ + "SHIPPED_AT", + "TIMESTAMP_NTZ" + ], + [ + "CHANNEL", + "VARCHAR" + ] + ], + "ORDER_ITEMS": [ + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "LINE_NUMBER", + "NUMBER(38,0)" + ], + [ + "PRODUCT_ID", + "NUMBER(38,0)" + ], + [ + "QUANTITY", + "NUMBER(38,0)" + ], + [ + "UNIT_PRICE", + "NUMBER(10,2)" + ] + ], + "PRODUCTS": [ + [ + "PRODUCT_ID", + "NUMBER(38,0)" + ], + [ + "SKU", + "VARCHAR" + ], + [ + "NAME", + "VARCHAR" + ], + [ + "CATEGORY", + "VARCHAR" + ], + [ + "PRICE", + "NUMBER(10,2)" + ], + [ + "WEIGHT_KG", + "FLOAT" + ], + [ + "LAUNCHED_ON", + "DATE" + ] + ], + "PAYMENTS": [ + [ + "PAYMENT_ID", + "NUMBER(38,0)" + ], + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "METHOD", + "VARCHAR" + ], + [ + "AMOUNT", + "NUMBER(12,2)" + ], + [ + "PAID_AT", + "TIMESTAMP_NTZ" + ] + ], + "EVENTS": [ + [ + "EVENT_ID", + "VARCHAR" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "EVENT_TYPE", + "VARCHAR" + ], + [ + "PAYLOAD", + "VARIANT" + ], + [ + "OCCURRED_AT", + "TIMESTAMP_NTZ" + ] + ], + "TICKETS": [ + [ + "TICKET_ID", + "NUMBER(38,0)" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "PRIORITY", + "VARCHAR" + ], + [ + "SUBJECT", + "VARCHAR" + ], + [ + "OPENED_AT", + "TIMESTAMP_NTZ" + ], + [ + "CLOSED_AT", + "TIMESTAMP_NTZ" + ], + [ + "TAGS", + "ARRAY" + ] + ], + "RAW_ORDERS": [ + [ + "ID", + "NUMBER(38,0)" + ], + [ + "USER_ID", + "NUMBER(38,0)" + ], + [ + "ORDER_DATE", + "DATE" + ], + [ + "STATUS", + "VARCHAR" + ], + [ + "AMOUNT_CENTS", + "NUMBER(38,0)" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ], + [ + "_LOADED_AT", + "TIMESTAMP_NTZ" + ] + ], + "RAW_CUSTOMERS": [ + [ + "ID", + "NUMBER(38,0)" + ], + [ + "FIRST_NAME", + "VARCHAR" + ], + [ + "LAST_NAME", + "VARCHAR" + ], + [ + "EMAIL", + "VARCHAR" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ], + [ + "_LOADED_AT", + "TIMESTAMP_NTZ" + ] + ], + "RAW_PAYMENTS": [ + [ + "ID", + "NUMBER(38,0)" + ], + [ + "ORDERID", + "NUMBER(38,0)" + ], + [ + "PAYMENTMETHOD", + "VARCHAR" + ], + [ + "STATUS", + "VARCHAR" + ], + [ + "AMOUNT", + "NUMBER(38,0)" + ], + [ + "CREATED", + "DATE" + ], + [ + "_BATCHED_AT", + "TIMESTAMP_NTZ" + ] + ], + "RAW_EVENTS": [ + [ + "RAW", + "VARIANT" + ], + [ + "_LOADED_AT", + "TIMESTAMP_NTZ" + ] + ], + "STG_ORDERS": [ + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "ORDER_DATE", + "DATE" + ], + [ + "STATUS", + "VARCHAR" + ], + [ + "AMOUNT", + "NUMBER(12,2)" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ] + ], + "STG_CUSTOMERS": [ + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "FIRST_NAME", + "VARCHAR" + ], + [ + "LAST_NAME", + "VARCHAR" + ], + [ + "EMAIL", + "VARCHAR" + ], + [ + "COUNTRY", + "VARCHAR" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ] + ], + "STG_PAYMENTS": [ + [ + "PAYMENT_ID", + "NUMBER(38,0)" + ], + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "PAYMENT_METHOD", + "VARCHAR" + ], + [ + "AMOUNT", + "NUMBER(12,2)" + ], + [ + "PAID_AT", + "TIMESTAMP_NTZ" + ] + ], + "FCT_ORDERS": [ + [ + "ORDER_ID", + "NUMBER(38,0)" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "ORDER_DATE", + "DATE" + ], + [ + "STATUS", + "VARCHAR" + ], + [ + "AMOUNT", + "NUMBER(12,2)" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ] + ], + "SUBSCRIPTIONS": [ + [ + "SUBSCRIPTION_ID", + "NUMBER(38,0)" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "PLAN", + "VARCHAR" + ], + [ + "MRR", + "NUMBER(10,2)" + ], + [ + "STARTED_AT", + "TIMESTAMP_NTZ" + ], + [ + "CANCELLED_AT", + "TIMESTAMP_NTZ" + ], + [ + "UPDATED_AT", + "TIMESTAMP_NTZ" + ] + ], + "INVENTORY_SNAPSHOTS": [ + [ + "SNAPSHOT_DATE", + "DATE" + ], + [ + "PRODUCT_ID", + "NUMBER(38,0)" + ], + [ + "WAREHOUSE", + "VARCHAR" + ], + [ + "ON_HAND", + "NUMBER(38,0)" + ] + ], + "WEB_SESSIONS": [ + [ + "SESSION_ID", + "VARCHAR" + ], + [ + "CUSTOMER_ID", + "NUMBER(38,0)" + ], + [ + "STARTED_AT", + "TIMESTAMP_NTZ" + ], + [ + "PAGE_VIEWS", + "NUMBER(38,0)" + ], + [ + "DEVICE", + "VARCHAR" + ], + [ + "ATTRIBUTES", + "OBJECT" + ] + ], + "FX_RATES": [ + [ + "RATE_DATE", + "DATE" + ], + [ + "CURRENCY", + "VARCHAR" + ], + [ + "RATE_TO_USD", + "NUMBER(18,6)" + ] + ] +} diff --git a/corpora/everyday-models/snowflake-receipts.json b/corpora/everyday-models/snowflake-receipts.json new file mode 100644 index 000000000..819f83b62 --- /dev/null +++ b/corpora/everyday-models/snowflake-receipts.json @@ -0,0 +1,563 @@ +{ + "schema": "the 19 relations of d150-models-capture-packet.json as empty CTEs (CAST(NULL AS type) ... WHERE FALSE) ahead of each query; for queries with their own WITH, the schema CTEs go first in the same WITH and skip names the query defines", + "method": "EXPLAIN of each query (compile only); queries that compiled were then executed once on the empty inputs and their result columns taken from the connector metadata", + "session": "TIMEZONE UTC, TIMESTAMP_TYPE_MAPPING TIMESTAMP_NTZ, QUOTED_IDENTIFIERS_IGNORE_CASE false, USE_CACHED_RESULT false", + "results": { + "U1": { + "sql": "select * exclude (_loaded_at) rename (id as customer_id)\nfrom raw_customers", + "compiles": null, + "captured": false, + "note": "not submitted: the capture guard's offline read-only screen holds any statement containing the word RENAME" + }, + "U2": { + "sql": "with customers as (\n select * from stg_customers\n),\norders as (\n select * from stg_orders\n),\npayments as (\n select * from stg_payments\n),\ncustomer_orders as (\n select\n customer_id,\n min(order_date) as first_order,\n max(order_date) as most_recent_order,\n count(order_id) as number_of_orders\n from orders\n group by customer_id\n),\ncustomer_payments as (\n select\n orders.customer_id,\n sum(amount) as total_amount\n from payments\n left join orders on payments.order_id = orders.order_id\n group by orders.customer_id\n),\nfinal as (\n select\n customers.customer_id,\n customers.first_name,\n customers.last_name,\n customer_orders.first_order,\n customer_orders.most_recent_order,\n customer_orders.number_of_orders,\n customer_payments.total_amount as customer_lifetime_value\n from customers\n left join customer_orders on customers.customer_id = customer_orders.customer_id\n left join customer_payments on customers.customer_id = customer_payments.customer_id\n)\nselect * from final", + "compiles": false, + "errno": 2028, + "sqlstate": "42601", + "message": "SQL compilation error: ambiguous column name 'AMOUNT'" + }, + "U3": { + "sql": "select\n r.raw:id::varchar as event_id,\n f.path,\n f.value\nfrom raw_events r,\n lateral flatten(input => r.raw, recursive => true) f", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "EVENT_ID", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "PATH", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "VALUE", + "type_name": "VARIANT", + "precision": null, + "scale": null, + "nullable": true + } + ] + }, + "U4": { + "sql": "select\n e.customer_id,\n object_agg(e.event_type, e.payload) as last_payload_by_type\nfrom events e\ngroup by e.customer_id", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "LAST_PAYLOAD_BY_TYPE", + "type_name": "OBJECT", + "precision": null, + "scale": null, + "nullable": false + } + ] + }, + "U5": { + "sql": "select\n e.event_id,\n f.value:sku::varchar as sku\nfrom events e\njoin lateral flatten(input => e.payload:items) f", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "EVENT_ID", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "SKU", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + } + ] + }, + "U6": { + "sql": "select\n e.event_id,\n f.value:sku::varchar as sku\nfrom events e\ninner join lateral flatten(input => e.payload:items) f on true", + "compiles": false, + "errno": 2, + "sqlstate": "0A000", + "message": "Unsupported feature 'lateral table function called with OUTER JOIN syntax or a join predicate (ON clause)'." + }, + "U7": { + "sql": "with spine as (\n select dateadd(day, seq4(), '2024-01-01'::date) as date_day\n from table(generator(rowcount => 366))\n)\nselect\n spine.date_day,\n last_value(f.rate_to_usd ignore nulls) over (order by spine.date_day rows between unbounded preceding and current row) as rate_to_usd\nfrom spine\nleft join fx_rates f on f.rate_date = spine.date_day and f.currency = 'EUR'", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "DATE_DAY", + "type_name": "DATE", + "precision": null, + "scale": null, + "nullable": false + }, + { + "name": "RATE_TO_USD", + "type_name": "FIXED", + "precision": 18, + "scale": 6, + "nullable": true + } + ] + }, + "U8": { + "sql": "select\n dateadd(day, value::int, '2024-01-01'::date) as date_day\nfrom table(flatten(input => array_generate_range(0, 366)))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "DATE_DAY", + "type_name": "DATE", + "precision": null, + "scale": null, + "nullable": true + } + ] + }, + "U9": { + "sql": "select\n dateadd(day, seq4(), '2024-01-01') as date_day\nfrom table(generator(rowcount => 10))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "DATE_DAY", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": false + } + ] + }, + "U10": { + "sql": "select\n dateadd(day, seq4(), '2024-01-01'::date) as date_day\nfrom table(generator(timelimit => 1))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "DATE_DAY", + "type_name": "DATE", + "precision": null, + "scale": null, + "nullable": false + } + ] + }, + "U11": { + "sql": "select\n order_id,\n customer_id,\n ordered_at,\n total,\n count(distinct status) over (partition by customer_id) as distinct_statuses\nfrom orders\n", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "ORDERED_AT", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": true + }, + { + "name": "TOTAL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + }, + { + "name": "DISTINCT_STATUSES", + "type_name": "FIXED", + "precision": 18, + "scale": 0, + "nullable": false + } + ] + }, + "U12": { + "sql": "select\n order_id,\n customer_id,\n ordered_at,\n total,\n conditional_true_event(datediff('minute', lag(ordered_at) over (partition by customer_id order by ordered_at), ordered_at) > 30) over (partition by customer_id order by ordered_at) as session_number\nfrom orders\n", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "ORDERED_AT", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": true + }, + { + "name": "TOTAL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + }, + { + "name": "SESSION_NUMBER", + "type_name": "FIXED", + "precision": 18, + "scale": 0, + "nullable": true + } + ] + }, + "U13": { + "sql": "select\n order_id,\n customer_id,\n ordered_at,\n total,\n avg(total) over (partition by customer_id order by ordered_at range between interval '7 days' preceding and current row) as avg_7_days\nfrom orders\n", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "ORDERED_AT", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": true + }, + { + "name": "TOTAL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + }, + { + "name": "AVG_7_DAYS", + "type_name": "FIXED", + "precision": 27, + "scale": 5, + "nullable": true + } + ] + }, + "U14": { + "sql": "select\n order_id,\n customer_id,\n ordered_at,\n total,\n lag(total) ignore nulls over (partition by customer_id order by ordered_at) as previous_non_null\nfrom orders\n", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "ORDERED_AT", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": true + }, + { + "name": "TOTAL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + }, + { + "name": "PREVIOUS_NON_NULL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + } + ] + }, + "U15": { + "sql": "select\n order_id,\n sum(total) over w as running_total,\n avg(total) over w as running_avg\nfrom orders\nwindow w as (partition by customer_id order by ordered_at)", + "compiles": false, + "errno": 1003, + "sqlstate": "42000", + "message": "SQL compilation error: syntax error line 3 at position 20 unexpected 'w'. syntax error line 3 at position 22 unexpected 'as'." + }, + "U16": { + "sql": "select *\nfrom (select customer_id, channel, total from orders)\npivot (sum(total) for channel in (any order by channel))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + } + ] + }, + "U17": { + "sql": "select *\nfrom (select customer_id, channel, total from orders)\npivot (sum(total) for channel in (select distinct channel from orders))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + } + ] + }, + "U18": { + "sql": "select *\nfrom (select customer_id, channel, total from orders)\npivot (sum(total) for channel in ('web', 'store') default on null (0))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "'web'", + "type_name": "FIXED", + "precision": 24, + "scale": 2, + "nullable": false + }, + { + "name": "'store'", + "type_name": "FIXED", + "precision": 24, + "scale": 2, + "nullable": false + } + ] + }, + "U19": { + "sql": "select order_id, metric, value\nfrom (select order_id, total, discount from orders)\nunpivot include nulls (value for metric in (total, discount))", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "METRIC", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "VALUE", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + } + ] + }, + "U20": { + "sql": "select o.order_id, m.metric, m.value\nfrom orders o,\n lateral (\n select 'total' as metric, o.total as value\n union all\n select 'discount', o.discount\n ) m", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "METRIC", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": false + }, + { + "name": "VALUE", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + } + ] + }, + "U21": { + "sql": "select customer_id, channel, total_web, total_store\nfrom (select customer_id, channel, total from orders)\npivot (sum(total) for channel in ('web' as total_web, 'store' as total_store))", + "compiles": false, + "errno": 904, + "sqlstate": "42000", + "message": "SQL compilation error: error line 1 at position 4,966 invalid identifier 'CHANNEL'" + }, + "U22": { + "sql": "select\n o.order_id,\n o.total,\n coalesce(sum(p.amount), 0) as paid,\n o.total - coalesce(sum(p.amount), 0) as outstanding\nfrom orders o\nleft join payments p on p.order_id = o.order_id\ngroup by o.order_id, o.total\nhaving outstanding <> 0", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "ORDER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "TOTAL", + "type_name": "FIXED", + "precision": 12, + "scale": 2, + "nullable": true + }, + { + "name": "PAID", + "type_name": "FIXED", + "precision": 24, + "scale": 2, + "nullable": false + }, + { + "name": "OUTSTANDING", + "type_name": "FIXED", + "precision": 25, + "scale": 2, + "nullable": true + } + ] + }, + "U23": { + "sql": "select *\nfrom stg_customers\nat (timestamp => '2024-06-30 00:00:00'::timestamp_ltz)", + "compiles": true, + "outcome": "rows", + "error": null, + "columns": [ + { + "name": "CUSTOMER_ID", + "type_name": "FIXED", + "precision": 38, + "scale": 0, + "nullable": true + }, + { + "name": "FIRST_NAME", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "LAST_NAME", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "EMAIL", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "COUNTRY", + "type_name": "TEXT", + "precision": null, + "scale": null, + "nullable": true + }, + { + "name": "UPDATED_AT", + "type_name": "TIMESTAMP_NTZ", + "precision": 0, + "scale": 9, + "nullable": true + } + ] + }, + "U24": { + "sql": "select *\nfrom stg_customers\nchanges (information => default)\nat (offset => -3600)", + "compiles": false, + "errno": 91930, + "sqlstate": "2F003", + "message": "SQL compilation error: Change tracking is not enabled or has been missing for the time range requested on table 'SYS$_VIEW_914'." + } + } +} diff --git a/corpora/everyday/queries.toml b/corpora/everyday/queries.toml index f4d49e172..c08ef4645 100644 --- a/corpora/everyday/queries.toml +++ b/corpora/everyday/queries.toml @@ -3176,8 +3176,8 @@ expect_repository = "refused SQ0002 node:statement" [[query]] category = "invalid_semantics" sql = '''SELECT customer_id FROM orders o JOIN customers c ON o.customer_id = c.customer_id''' -expect = "refused SQ0004 statement" -expect_repository = "refused SQ0004 statement" +expect = "check SQ0004" +expect_repository = "check SQ0004" [[query]] category = "invalid_semantics" diff --git a/crates/sqlquack-core/src/diagnostics/codes.lock b/crates/sqlquack-core/src/diagnostics/codes.lock index cad5a3c43..8ecedb34d 100644 --- a/crates/sqlquack-core/src/diagnostics/codes.lock +++ b/crates/sqlquack-core/src/diagnostics/codes.lock @@ -23,3 +23,4 @@ SQ0205 unknown_function SQ0206 duplicate_alias SQ0207 too_many_arguments SQ0208 pivot_consumed_column +SQ0209 lateral_function_join_condition diff --git a/crates/sqlquack-core/src/diagnostics/constants.rs b/crates/sqlquack-core/src/diagnostics/constants.rs index c0829aeb2..23b87ff26 100644 --- a/crates/sqlquack-core/src/diagnostics/constants.rs +++ b/crates/sqlquack-core/src/diagnostics/constants.rs @@ -94,6 +94,8 @@ pub const SET_OPERATION_CHAIN_HELP: &str = "split the chain: put at most 250 bra pub const TOO_MANY_ARGUMENTS: &str = "too_many_arguments"; /// The name of the code of a read of a column a PIVOT consumed. pub const PIVOT_CONSUMED_COLUMN: &str = "pivot_consumed_column"; +/// The name of the code of a lateral table function joined with an ON condition. +pub const LATERAL_FUNCTION_JOIN_CONDITION: &str = "lateral_function_join_condition"; /// A relation or column name does not resolve. pub const UNRESOLVED_NAME: &str = "SQ0004"; /// DuckDB cannot express the construct's meaning. @@ -400,4 +402,17 @@ pub const CODES: &[CodeEntry] = &[ is the only item in FROM, for names the SELECT list does not define as aliases.", example: r#"echo "SELECT order_id, total FROM orders PIVOT (SUM(total) FOR status IN ('shipped', 'pending')) AS p" | sqlquack check"#, }, + CodeEntry { + code: "SQ0209", + name: LATERAL_FUNCTION_JOIN_CONDITION, + alias: None, + title: "A lateral table function is joined with an ON condition", + help: Some( + "remove the ON clause: a lateral table function already reads the rows before it", + ), + explanation: "Snowflake joins a LATERAL table function such as FLATTEN to the rows before it \ + by itself, and rejects the join when it also has an ON condition, even ON TRUE \ + (Snowflake error 2, unsupported feature). Write the join without ON, or with a comma.", + example: r#"echo "SELECT e.event_id, f.value FROM events e JOIN LATERAL FLATTEN(input => e.payload) f ON TRUE" | sqlquack check"#, + }, ]; diff --git a/crates/sqlquack-core/src/refusals/_helpers/clause_location.rs b/crates/sqlquack-core/src/refusals/_helpers/clause_location.rs index b74d6f355..26d86829d 100644 --- a/crates/sqlquack-core/src/refusals/_helpers/clause_location.rs +++ b/crates/sqlquack-core/src/refusals/_helpers/clause_location.rs @@ -2,10 +2,14 @@ use sqlquack_syntax::Expression; -use crate::diagnostics::constants::{DUPLICATE_ALIAS, PIVOT_CONSUMED_COLUMN}; +use crate::diagnostics::constants::{ + DUPLICATE_ALIAS, LATERAL_FUNCTION_JOIN_CONDITION, PIVOT_CONSUMED_COLUMN, +}; use crate::parsing::main::node_location::node_location; use crate::parsing::models::{SourceLines, SourceLocation}; -use crate::refusals::_helpers::rejections::{duplicate_alias, pivot_consumed_column}; +use crate::refusals::_helpers::rejections::{ + duplicate_alias, lateral_join_condition, pivot_consumed_column, +}; /// The keyword that starts a QUALIFY clause. const QUALIFY_KEYWORD: &str = "QUALIFY"; @@ -24,6 +28,9 @@ pub(crate) fn clause_location( Expression::Select(select) if name == DUPLICATE_ALIAS => duplicate_alias(select)? .span .map(|span| lines.location(span.start, span.end)), + Expression::Select(select) if name == LATERAL_FUNCTION_JOIN_CONDITION => { + node_location(lateral_join_condition(select)?, lines) + } Expression::Select(select) if name == PIVOT_CONSUMED_COLUMN => { pivot_consumed_column(select)? .span diff --git a/crates/sqlquack-core/src/refusals/_helpers/rejections.rs b/crates/sqlquack-core/src/refusals/_helpers/rejections.rs index d1703de64..0647010dc 100644 --- a/crates/sqlquack-core/src/refusals/_helpers/rejections.rs +++ b/crates/sqlquack-core/src/refusals/_helpers/rejections.rs @@ -7,7 +7,9 @@ use sqlquack_syntax::expressions::names::models::{Column, Identifier, Star}; use sqlquack_syntax::expressions::queries::models::Select; use sqlquack_syntax::{Expression, ExpressionWalk}; -use crate::diagnostics::constants::{DUPLICATE_ALIAS, PIVOT_CONSUMED_COLUMN, TOO_MANY_ARGUMENTS}; +use crate::diagnostics::constants::{ + DUPLICATE_ALIAS, LATERAL_FUNCTION_JOIN_CONDITION, PIVOT_CONSUMED_COLUMN, TOO_MANY_ARGUMENTS, +}; use crate::refusals::constants::SNOWFLAKE_FUNCTION_ARITIES; /// The registry name of the rejection a node causes, with the reason, if it causes one. @@ -41,6 +43,14 @@ pub(crate) fn rejection(node: &Expression) -> Option<(&'static str, String)> { format!("the alias `{}` names two FROM items", alias.name), ) }) + .or_else(|| { + lateral_join_condition(select).map(|_| { + ( + LATERAL_FUNCTION_JOIN_CONDITION, + "a lateral table function is joined with an ON condition".to_owned(), + ) + }) + }) .or_else(|| { pivot_consumed_column(select).map(|column| { ( @@ -89,6 +99,15 @@ pub(crate) fn duplicate_alias(select: &Select) -> Option<&Identifier> { .find(|alias| !seen.insert(stored_name(alias))) } +/// The ON condition of the first join of a LATERAL table function, which Snowflake joins only without one. +pub(crate) fn lateral_join_condition(select: &Select) -> Option<&Expression> { + select + .joins + .iter() + .filter(|join| matches!(join.this, Expression::Lateral(_))) + .find_map(|join| join.on.as_ref()) +} + /// The first column a lone PIVOT aggregates or pivots on that its query still reads by that name. pub(crate) fn pivot_consumed_column(select: &Select) -> Option<&Column> { let [Expression::Pivot(pivot)] = select.from.as_ref()?.expressions.as_slice() else { diff --git a/crates/sqlquack-core/src/refusals/tests/test_refusals.rs b/crates/sqlquack-core/src/refusals/tests/test_refusals.rs index 8002c2f92..294fb75f9 100644 --- a/crates/sqlquack-core/src/refusals/tests/test_refusals.rs +++ b/crates/sqlquack-core/src/refusals/tests/test_refusals.rs @@ -163,6 +163,24 @@ fn given_sql_snowflake_parses_then_rejects_when_refusing_then_each_rejection_is_ expected_rejections: vec![], expected_codes: vec![], }, + RejectionTestCase { + description: "a lateral table function joined with an ON condition is rejected", + sql: "SELECT 1 FROM events e INNER JOIN LATERAL FLATTEN(input => e.payload) f ON TRUE; \ + SELECT 1 FROM events e LEFT JOIN LATERAL FLATTEN(input => e.payload) f ON f.index = 0", + expected_rejections: vec![ + "lateral_function_join_condition", + "lateral_function_join_condition", + ], + expected_codes: vec!["SQ0209", "SQ0209"], + }, + RejectionTestCase { + description: "a lateral table function joined without ON, or a lateral subquery with ON, is not", + sql: "SELECT 1 FROM events e JOIN LATERAL FLATTEN(input => e.payload) f; \ + SELECT 1 FROM events e, LATERAL FLATTEN(input => e.payload) f; \ + SELECT 1 FROM events e JOIN LATERAL (SELECT 1 AS a) s ON TRUE", + expected_rejections: vec![], + expected_codes: vec![], + }, RejectionTestCase { description: "a star without ILIKE, or ILIKE without REPLACE or RENAME, is left to conversion", sql: "SELECT * REPLACE (price * 2 AS price) FROM orders; \ diff --git a/crates/sqlquack-syntax/src/parser/_helpers/statements/analyze.rs b/crates/sqlquack-syntax/src/parser/_helpers/statements/analyze.rs index 6a1f3c86b..99c15690d 100644 --- a/crates/sqlquack-syntax/src/parser/_helpers/statements/analyze.rs +++ b/crates/sqlquack-syntax/src/parser/_helpers/statements/analyze.rs @@ -264,6 +264,7 @@ impl Parser { || self.check(TokenType::Unpivot) && !self.is_unpivot_clause_start() || self.check(TokenType::Partition) || self.limit_as_snowflake_alias() + || self.check(TokenType::Window) && self.applies_snowflake_context_rules() || self.check(TokenType::Window) && { let next_pos = self.current + 1; next_pos >= self.tokens.len() diff --git a/crates/sqlquack-syntax/src/parser/_helpers/statements/entry.rs b/crates/sqlquack-syntax/src/parser/_helpers/statements/entry.rs index d00b724d5..c3153b297 100644 --- a/crates/sqlquack-syntax/src/parser/_helpers/statements/entry.rs +++ b/crates/sqlquack-syntax/src/parser/_helpers/statements/entry.rs @@ -1610,7 +1610,7 @@ impl Parser { fn parse_select_windows(&mut self) -> Result>> { Ok( - if self.check(TokenType::Window) && { + if !self.applies_snowflake_context_rules() && self.check(TokenType::Window) && { let next_pos = self.current + 1; next_pos < self.tokens.len() && (self.tokens[next_pos].token_type == TokenType::Var diff --git a/crates/sqlquack-syntax/tests/parser/test_snowflake_fixes.rs b/crates/sqlquack-syntax/tests/parser/test_snowflake_fixes.rs index 4f125e039..a21471439 100644 --- a/crates/sqlquack-syntax/tests/parser/test_snowflake_fixes.rs +++ b/crates/sqlquack-syntax/tests/parser/test_snowflake_fixes.rs @@ -1298,6 +1298,16 @@ fn given_an_operator_or_clause_snowflake_does_not_have_when_parsing_then_it_is_r sql: "SELECT FILTER ( [ 1 , 2 ] , x -> x > 1 ) ;", expected_kind: Some("select"), }, + SnowflakeVerdictTestCase { + description: "a named WINDOW clause (recorded errno 1003 at the window's name)", + sql: "SELECT SUM ( 1 ) OVER w FROM t WINDOW w AS ( PARTITION BY 1 ) ;", + expected_kind: None, + }, + SnowflakeVerdictTestCase { + description: "keeps a window function with its window written inline", + sql: "SELECT SUM ( 1 ) OVER ( PARTITION BY 1 ) FROM t ;", + expected_kind: Some("select"), + }, SnowflakeVerdictTestCase { description: "keeps a single = and IN or NOT IN before a list or a query", sql: "SELECT 1 FROM t WHERE a = 1 AND a IN ( 1 , 2 ) AND a NOT IN ( SELECT 1 ) ;", diff --git a/crates/sqlquack/src/analysis/constants.rs b/crates/sqlquack/src/analysis/constants.rs index 585735e1a..1c49bf1c9 100644 --- a/crates/sqlquack/src/analysis/constants.rs +++ b/crates/sqlquack/src/analysis/constants.rs @@ -7,6 +7,8 @@ pub const STATEMENT_REFUSAL: &str = "statement"; pub const UNKNOWN_TABLE_FINDING: &str = "E200"; /// The name check's code for a call to a function neither Snowflake nor the schema declares. pub const UNKNOWN_FUNCTION_FINDING: &str = "E202"; +/// The name check's code for an unqualified column more than one relation in scope has. +pub const AMBIGUOUS_COLUMN_FINDING: &str = "E221"; /// The column a scalar SQL function body is converted as. pub const SCALAR_RESULT_COLUMN: &str = "RESULT"; diff --git a/crates/sqlquack/src/analysis/main/check_source.rs b/crates/sqlquack/src/analysis/main/check_source.rs index 23b29c55e..32b2b8e5d 100644 --- a/crates/sqlquack/src/analysis/main/check_source.rs +++ b/crates/sqlquack/src/analysis/main/check_source.rs @@ -15,7 +15,9 @@ use sqlquack_semantics::query_name_findings; use sqlquack_semantics::types::models::Schema; use sqlquack_syntax::{Dialect, ParseOptions}; -use crate::analysis::constants::{UNKNOWN_FUNCTION_FINDING, UNKNOWN_TABLE_FINDING}; +use crate::analysis::constants::{ + AMBIGUOUS_COLUMN_FINDING, UNKNOWN_FUNCTION_FINDING, UNKNOWN_TABLE_FINDING, +}; /// The parse failure, what Snowflake would reject and, with `schema`, the names it lacks. pub fn check_source(sql: &str, schema: Option<&Schema>) -> Result, String> { @@ -54,7 +56,7 @@ fn undeclared_names(sql: &str, schema: &Schema, lines: &SourceLines) -> Vec Option { let location = finding .span @@ -73,6 +75,14 @@ fn undeclared(finding: NameFinding, lines: &SourceLines) -> Option { category: RefusalCategory::SnowflakeRejects, reason: format!("no Snowflake or schema function is named `{written}`"), }), + AMBIGUOUS_COLUMN_FINDING => Some(Refusal { + construct: None, + location, + category: RefusalCategory::UnresolvedName, + reason: format!( + "column `{written}` is ambiguous: more than one relation in scope has it; qualify it" + ), + }), _ => None, } } diff --git a/crates/sqlquack/src/analysis/tests/helpers.rs b/crates/sqlquack/src/analysis/tests/helpers.rs index 4c906b469..ec8036338 100644 --- a/crates/sqlquack/src/analysis/tests/helpers.rs +++ b/crates/sqlquack/src/analysis/tests/helpers.rs @@ -785,8 +785,8 @@ const PARSED_TREE: &str = r#"{"error":false"#; const LOCATION_FIELD: &str = r#""query_location"#; /// Each everyday corpus query whose outcome under either policy differs from the recorded one. -pub(crate) fn everyday_outcome_mismatches() -> Vec { - let (root, corpus) = everyday_corpus(); +pub(crate) fn everyday_outcome_mismatches(directory: &str) -> Vec { + let (root, corpus) = everyday_corpus(directory); let strict = everyday_arguments(&root, true); let repository = everyday_arguments(&root, false); corpus @@ -819,8 +819,8 @@ pub(crate) fn everyday_outcome_mismatches() -> Vec { } /// How many valid queries, not DDL, are recorded as translated: strict, and repository policy. -pub(crate) fn everyday_translation_rates() -> String { - let (_, corpus) = everyday_corpus(); +pub(crate) fn everyday_translation_rates(directory: &str) -> String { + let (_, corpus) = everyday_corpus(directory); let valid: Vec<&EverydayQuery> = corpus .query .iter() @@ -848,15 +848,26 @@ pub(crate) fn everyday_translation_rates() -> String { } /// Queries Snowflake rejects that pass SQLQuack's parse and checks, once every receipt matches. -pub(crate) fn everyday_snowflake_rejections_not_caught() -> Vec { - let (root, corpus) = everyday_corpus(); +pub(crate) fn everyday_snowflake_rejections_not_caught(directory: &str) -> Vec { + let (root, corpus) = everyday_corpus(directory); let text = std::fs::read_to_string(root.join("snowflake-receipts.json")).expect("receipts read"); let receipts: serde_json::Value = serde_json::from_str(&text).expect("receipts parse"); - let results = receipts["results"] - .as_array() - .expect("receipts hold results"); - for receipt in results { + // Results are a list, or an object keyed by query id; a receipt without a verdict is skipped. + let results: Vec<&serde_json::Value> = receipts["results"].as_array().map_or_else( + || { + receipts["results"] + .as_object() + .into_iter() + .flat_map(|by_id| by_id.values()) + .collect() + }, + |list| list.iter().collect(), + ); + for receipt in results + .into_iter() + .filter(|receipt| receipt["compiles"].is_boolean()) + { let sql = receipt["sql"] .as_str() .expect("a receipt names its SQL") @@ -894,11 +905,11 @@ pub(crate) fn everyday_snowflake_rejections_not_caught() -> Vec { } /// The everyday corpus directory and its queries. -fn everyday_corpus() -> (std::path::PathBuf, EverydayCorpus) { - let root = repository_root().join(EVERYDAY_CORPUS); +fn everyday_corpus(directory: &str) -> (std::path::PathBuf, EverydayCorpus) { + let root = repository_root().join(directory); let text = std::fs::read_to_string(root.join("queries.toml")).expect("the corpus reads"); let corpus: EverydayCorpus = toml::from_str(&text).expect("the corpus parses"); - assert!(corpus.query.len() > 400, "{} queries", corpus.query.len()); + assert!(corpus.query.len() > 300, "{} queries", corpus.query.len()); (root, corpus) } @@ -952,8 +963,11 @@ fn everyday_outcome(sql: &str, arguments: &ConvertArguments) -> String { ) } -/// The corpus directory, under the repository root. -const EVERYDAY_CORPUS: &str = "corpora/everyday"; +/// The everyday corpus directory, under the repository root. +pub(crate) const EVERYDAY_CORPUS: &str = "corpora/everyday"; + +/// The dbt-style model corpus directory, laid out as the everyday corpus. +pub(crate) const EVERYDAY_MODELS_CORPUS: &str = "corpora/everyday-models"; /// Corpus categories of statements no rule translates: DDL and DML. const EVERYDAY_NOT_TRANSLATABLE: [&str; 1] = ["ddl_dml"]; diff --git a/crates/sqlquack/src/analysis/tests/test_everyday_corpus.rs b/crates/sqlquack/src/analysis/tests/test_everyday_corpus.rs index 92dd8884e..25dd309e4 100644 --- a/crates/sqlquack/src/analysis/tests/test_everyday_corpus.rs +++ b/crates/sqlquack/src/analysis/tests/test_everyday_corpus.rs @@ -1,6 +1,6 @@ use crate::analysis::tests::helpers::{ - everyday_outcome_mismatches, everyday_snowflake_rejections_not_caught, - everyday_translation_rates, + EVERYDAY_CORPUS, EVERYDAY_MODELS_CORPUS, everyday_outcome_mismatches, + everyday_snowflake_rejections_not_caught, everyday_translation_rates, }; use crate::analysis::tests::test_types::EverydayCorpusTestCase; @@ -8,6 +8,7 @@ use crate::analysis::tests::test_types::EverydayCorpusTestCase; fn given_everyday_snowflake_queries_when_checked_and_converted_then_each_keeps_its_outcome() { let test_cases = [EverydayCorpusTestCase { description: "every corpora/everyday query parses, checks, converts or refuses as recorded, under the strict built-in policy and the repository policy", + directory: EVERYDAY_CORPUS, expected_mismatches: Vec::new(), expected_rates: "288 of 436 strict, 357 of 436 repository", // SQL Snowflake rejects (snowflake-receipts.json) that SQLQuack neither fails to parse nor @@ -17,19 +18,55 @@ fn given_everyday_snowflake_queries_when_checked_and_converted_then_each_keeps_i for test_case in test_cases { assert_eq!( - everyday_outcome_mismatches(), + everyday_outcome_mismatches(test_case.directory), test_case.expected_mismatches, "{}", test_case.description ); assert_eq!( - everyday_translation_rates(), + everyday_translation_rates(test_case.directory), test_case.expected_rates, "{}", test_case.description ); assert_eq!( - everyday_snowflake_rejections_not_caught(), + everyday_snowflake_rejections_not_caught(test_case.directory), + test_case.expected_rejections_not_caught, + "{}", + test_case.description + ); + } +} + +#[test] +fn given_dbt_style_snowflake_models_when_checked_and_converted_then_each_keeps_its_outcome() { + let test_cases = [EverydayCorpusTestCase { + description: "every corpora/everyday-models query parses, checks, converts or refuses as recorded, under the strict built-in policy and the repository policy", + directory: EVERYDAY_MODELS_CORPUS, + expected_mismatches: Vec::new(), + expected_rates: "175 of 295 strict, 247 of 295 repository", + // SQL Snowflake rejects that SQLQuack neither fails to parse nor flags in its checks: + // CHANGES without change tracking. + expected_rejections_not_caught: vec![ + "select *\nfrom stg_customers\nchanges (information => default)\nat (offset => -3600)", + ], + }]; + + for test_case in test_cases { + assert_eq!( + everyday_outcome_mismatches(test_case.directory), + test_case.expected_mismatches, + "{}", + test_case.description + ); + assert_eq!( + everyday_translation_rates(test_case.directory), + test_case.expected_rates, + "{}", + test_case.description + ); + assert_eq!( + everyday_snowflake_rejections_not_caught(test_case.directory), test_case.expected_rejections_not_caught, "{}", test_case.description diff --git a/crates/sqlquack/src/analysis/tests/test_types.rs b/crates/sqlquack/src/analysis/tests/test_types.rs index 89b58c10e..7868b8418 100644 --- a/crates/sqlquack/src/analysis/tests/test_types.rs +++ b/crates/sqlquack/src/analysis/tests/test_types.rs @@ -213,6 +213,7 @@ pub(crate) struct OverrideCallsTestCase { pub(crate) struct EverydayCorpusTestCase { pub(crate) description: &'static str, + pub(crate) directory: &'static str, pub(crate) expected_mismatches: Vec, pub(crate) expected_rates: &'static str, pub(crate) expected_rejections_not_caught: Vec<&'static str>, diff --git a/website/src/content/docs/docs/diagnostics/reference.mdx b/website/src/content/docs/docs/diagnostics/reference.mdx index f314360d6..76cf0b36b 100644 --- a/website/src/content/docs/docs/diagnostics/reference.mdx +++ b/website/src/content/docs/docs/diagnostics/reference.mdx @@ -36,6 +36,7 @@ renumbered. See [Reading diagnostics](/docs/diagnostics/overview/) for the outpu | [`SQ0206`](#sq0206) | `duplicate_alias` | Two FROM items share one alias | | [`SQ0207`](#sq0207) | `too_many_arguments` | The call passes more arguments than the function takes | | [`SQ0208`](#sq0208) | `pivot_consumed_column` | The query reads a column its PIVOT consumed | +| [`SQ0209`](#sq0209) | `lateral_function_join_condition` | A lateral table function is joined with an ON condition | ## Conversion @@ -366,3 +367,17 @@ PIVOT replaces the column it aggregates and the column after FOR with one column ```sh echo "SELECT order_id, total FROM orders PIVOT (SUM(total) FOR status IN ('shipped', 'pending')) AS p" | sqlquack check ``` + +### SQ0209 + +**A lateral table function is joined with an ON condition** (`lateral_function_join_condition`) + +Snowflake joins a LATERAL table function such as FLATTEN to the rows before it by itself, and rejects the join when it also has an ON condition, even ON TRUE (Snowflake error 2, unsupported feature). Write the join without ON, or with a comma. + +**Help:** remove the ON clause: a lateral table function already reads the rows before it + +**Example:** + +```sh +echo "SELECT e.event_id, f.value FROM events e JOIN LATERAL FLATTEN(input => e.payload) f ON TRUE" | sqlquack check +```