diff --git a/src/drivers/snowflake/rename_functions.rs b/src/drivers/snowflake/rename_functions.rs index a14ff4f..0ffb875 100644 --- a/src/drivers/snowflake/rename_functions.rs +++ b/src/drivers/snowflake/rename_functions.rs @@ -9,6 +9,7 @@ use crate::ast::{FunctionName, Identifier}; // A `phf_map!` of BigQuery function names to Snowflake function names. Use // this for simple renaming. static FUNCTION_NAMES: phf::Map<&'static str, &'static str> = phf::phf_map! { + "GENERATE_UUID" => "UUID_STRING", "REGEXP_EXTRACT" => "REGEXP_SUBSTR", "SHA256" => "SHA2_BINARY", // Second argument defaults to SHA256. }; @@ -32,6 +33,7 @@ impl Udf { /// A `phf_map!` of BigQuery UDF names to Snowflake UDFs. Use this when we /// actually need to create a UDF as a helper function. static UDFS: phf::Map<&'static str, &'static Udf> = phf::phf_map! { + "RAND" => &Udf { decl: "RAND() RETURNS FLOAT", sql: "UNIFORM(0::float, 1::float, RANDOM())" }, "TO_HEX" => &Udf { decl: "TO_HEX(b BINARY) RETURNS STRING", sql: "HEX_ENCODE(b, 0)" }, }; diff --git a/tests/sql/functions/README.md b/tests/sql/functions/README.md index f0a3b55..9708926 100644 --- a/tests/sql/functions/README.md +++ b/tests/sql/functions/README.md @@ -18,36 +18,41 @@ generate your own version of this list by running `joinery parse - [x] LENGTH(_) - [x] CONCAT(*) - [x] TRIM(_) -- [ ] ARRAY_TO_STRING(_,_) - [x] SUM(_) - [x] FARM_FINGERPRINT(_) -- [ ] ANY_VALUE(_) -- [ ] ROW_NUMBER() OVER(..) -- [ ] COUNTIF(_) +- [x] ANY_VALUE(_) +- [x] ROW_NUMBER() OVER(..) +- [x] COUNTIF(_) - [x] UPPER(_) -- [ ] ARRAY_AGG(_) -- [ ] DATE_TRUNC(_,_) (special) - [x] MIN(_) -- [ ] FORMAT_DATETIME(_,_) -- [ ] RAND() -- [ ] RANK() OVER(..) +- [x] RAND() +- [x] RANK() OVER(..) +- [x] SUM(_) OVER(..) +- [x] EXP(_) +- [x] MAX(_) +- [x] GENERATE_UUID() +- [x] LEAST(*) +- [x] APPROX_QUANTILES(_,_) +- [x] LAG(_) OVER(..) +- [x] FIRST_VALUE(_) OVER(..) + +Arrays: + +- [ ] ARRAY_TO_STRING(_,_) +- [ ] ARRAY_AGG(_) - [ ] ARRAY_LENGTH(_) -- [ ] SUM(_) OVER(..) -- [ ] DATETIME_SUB(_,_) -- [ ] DATE_DIFF(_,_,_) (special) + +Special date functions: + - [ ] CURRENT_DATETIME() -- [ ] DATE_SUB(_,_) -- [ ] EXP(_) -- [x] MAX(_) -- [ ] GENERATE_UUID() - [ ] DATE(_) -- [ ] LEAST(_,_) -- [ ] APPROX_QUANTILES(_,_) -- [ ] GENERATE_DATE_ARRAY(_,_,_) - [ ] DATE_ADD(_,_) -- [ ] LAG(_) OVER(..) +- [ ] DATE_DIFF(_,_,_) (special) +- [ ] DATE_SUB(_,_) +- [ ] DATE_TRUNC(_,_) (special) +- [ ] DATETIME(_) - [ ] DATETIME_DIFF(_,_,_) (special) +- [ ] DATETIME_SUB(_,_) - [ ] DATETIME_TRUNC(_,_) (special) -- [ ] FIRST_VALUE(_) OVER(..) -- [ ] DATETIME(_) -- [ ] LEAST(_) +- [ ] FORMAT_DATETIME(_,_) +- [ ] GENERATE_DATE_ARRAY(_,_,_) diff --git a/tests/sql/functions/aggregate/any_value.sql b/tests/sql/functions/aggregate/any_value.sql new file mode 100644 index 0000000..475d02a --- /dev/null +++ b/tests/sql/functions/aggregate/any_value.sql @@ -0,0 +1,13 @@ +-- pending: sqlite3 ANY_VALUES is not available. + +CREATE TEMP TABLE vals (i INT64); +INSERT INTO vals VALUES (1), (1); + +CREATE OR REPLACE TABLE __result1 AS +SELECT ANY_VALUE(i) AS any_value FROM vals; + +CREATE OR REPLACE TABLE __expected1 ( + any_value INT64, +); +INSERT INTO __expected1 VALUES + (1); \ No newline at end of file diff --git a/tests/sql/functions/aggregate/approx_quantiles.sql b/tests/sql/functions/aggregate/approx_quantiles.sql new file mode 100644 index 0000000..377abea --- /dev/null +++ b/tests/sql/functions/aggregate/approx_quantiles.sql @@ -0,0 +1,15 @@ +-- pending: snowflake Use APPROX_PERCENTILE instead of APPROX_QUANTILES (complicated) +-- pending: sqlite3 No APPROX_QUANTILES function + +CREATE TEMP TABLE quantile_data (x INT64); +INSERT INTO quantile_data VALUES (1), (2), (3), (4), (5); + +CREATE OR REPLACE TABLE __result1 AS +SELECT APPROX_QUANTILES(x, 2) AS approx_quantiles +FROM quantile_data; + +CREATE OR REPLACE TABLE __expected1 ( + approx_quantiles ARRAY, +); +INSERT INTO __expected1 VALUES + ([1, 3, 5]); diff --git a/tests/sql/functions/aggregate/countif.sql b/tests/sql/functions/aggregate/countif.sql new file mode 100644 index 0000000..bbd5485 --- /dev/null +++ b/tests/sql/functions/aggregate/countif.sql @@ -0,0 +1,14 @@ +-- pending: snowflake COUNTIF can be rewritten portably by transpiler +-- pending: sqlite3 COUNTIF Can be rewritten portably by transpiler + +CREATE TEMP TABLE vals (i INT64); +INSERT INTO vals VALUES (1), (2), (2), (3), (NULL); + +CREATE OR REPLACE TABLE __result1 AS +SELECT COUNTIF(i = 2) AS is2 FROM vals; + +CREATE OR REPLACE TABLE __expected1 ( + is2 INT64, +); +INSERT INTO __expected1 VALUES + (2); diff --git a/tests/sql/functions/simple/exp.sql b/tests/sql/functions/simple/exp.sql new file mode 100644 index 0000000..c8281bd --- /dev/null +++ b/tests/sql/functions/simple/exp.sql @@ -0,0 +1,11 @@ +-- pending: sqlite3 EXP is not available + +CREATE OR REPLACE TABLE __result1 AS +SELECT + EXP(0.0) AS exp_zero; + +CREATE OR REPLACE TABLE __expected1 ( + exp_zero FLOAT64, +); +INSERT INTO __expected1 VALUES + (1.0); diff --git a/tests/sql/functions/simple/generate_uuid.sql b/tests/sql/functions/simple/generate_uuid.sql new file mode 100644 index 0000000..3dce9fd --- /dev/null +++ b/tests/sql/functions/simple/generate_uuid.sql @@ -0,0 +1,11 @@ +-- pending: sqlite3 Needs external function. + +CREATE OR REPLACE TABLE __result1 AS +SELECT + LENGTH(generate_uuid()) AS uuid_length; + +CREATE OR REPLACE TABLE __expected1 ( + uuid_length INT64, +); +INSERT INTO __expected1 VALUES + (36); diff --git a/tests/sql/functions/simple/greatest_least.sql b/tests/sql/functions/simple/greatest_least.sql new file mode 100644 index 0000000..eb3b860 --- /dev/null +++ b/tests/sql/functions/simple/greatest_least.sql @@ -0,0 +1,19 @@ +-- pending: sqlite3 GREATEST, LEAST are not available +-- +-- GREATEST, LEAST + +CREATE OR REPLACE TABLE __result1 AS +SELECT + GREATEST(1, 2, 3) AS greatest, + GREATEST(1, NULL, 3) AS greatest_null, + LEAST(1, 2, 3) AS least, + LEAST(1, NULL, 3) AS least_null; + +CREATE OR REPLACE TABLE __expected1 ( + greatest INT64, + greatest_null INT64, + least INT64, + least_null INT64, +); +INSERT INTO __expected1 VALUES + (3, NULL, 1, NULL); diff --git a/tests/sql/functions/simple/rand.sql b/tests/sql/functions/simple/rand.sql new file mode 100644 index 0000000..0737441 --- /dev/null +++ b/tests/sql/functions/simple/rand.sql @@ -0,0 +1,11 @@ +-- pending: sqlite3 BigQuery RAND returns [0.0, 1.0), SQLite3 RANDOM() returns int + +CREATE OR REPLACE TABLE __result1 AS +SELECT + RAND() BETWEEN 0.0 AND 1.0 AS rand_in_range; + +CREATE OR REPLACE TABLE __expected1 ( + rand_in_range BOOL, +); +INSERT INTO __expected1 VALUES + (TRUE); diff --git a/tests/sql/functions/windows/basic_windows.sql b/tests/sql/functions/windows/basic_windows.sql index 19aaa9e..fda01cb 100644 --- a/tests/sql/functions/windows/basic_windows.sql +++ b/tests/sql/functions/windows/basic_windows.sql @@ -25,6 +25,7 @@ SELECT category, price, RANK() OVER (PARTITION BY category ORDER BY price) AS price_rank, + ROW_NUMBER() OVER (PARTITION BY category ORDER BY price) AS price_row_number, SUM(price) OVER (PARTITION BY category ORDER BY price ROWS UNBOUNDED PRECEDING) AS cummulative_price, SUM(price) OVER (PARTITION BY category ORDER BY price ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS cummulative_price2, FROM groceries; @@ -34,14 +35,15 @@ CREATE OR REPLACE TABLE __expected1 ( category STRING, price FLOAT64, price_rank INT64, + price_row_number INT64, cummulative_price FLOAT64, cummulative_price2 FLOAT64, ); INSERT INTO __expected1 VALUES - ('apple', 'fruit', 1.0, 1, 1.0, 1.0), - ('banana', 'fruit', 1.5, 2, 2.5, 2.5), - ('carrot', 'vegetable', 0.75, 1, 0.75, 0.75), - ('eggplant', 'vegetable', 1.25, 2, 2.0, 2.0), - ('flour', 'baking', 0.25, 1, 0.25, 0.25), - ('sugar', 'baking', 0.5, 2, 0.75, 0.75), - ('salt', 'baking', 0.75, 3, 1.5, 1.5); + ('apple', 'fruit', 1.0, 1, 1, 1.0, 1.0), + ('banana', 'fruit', 1.5, 2, 2, 2.5, 2.5), + ('carrot', 'vegetable', 0.75, 1, 1, 0.75, 0.75), + ('eggplant', 'vegetable', 1.25, 2, 2, 2.0, 2.0), + ('flour', 'baking', 0.25, 1, 1, 0.25, 0.25), + ('sugar', 'baking', 0.5, 2, 2, 0.75, 0.75), + ('salt', 'baking', 0.75, 3, 3, 1.5, 1.5); diff --git a/tests/sql/functions/windows/first_last_value.sql b/tests/sql/functions/windows/first_last_value.sql new file mode 100644 index 0000000..5afffe1 --- /dev/null +++ b/tests/sql/functions/windows/first_last_value.sql @@ -0,0 +1,39 @@ +-- pending: sqlite3 FIRST_VALUE, LAST_VALUE seem to require frames to work? + +-- FIRST_VALUE, LAST_VALUE + +-- A fixture table for testing window functions. +CREATE TEMP TABLE groceries ( + item STRING, + category STRING, + price FLOAT64, +); +INSERT INTO groceries VALUES + ('apple', 'fruit', 1.00), + ('banana', 'fruit', 1.50), + ('carrot', 'vegetable', 0.75), + ('eggplant', 'vegetable', 1.25), + ('sugar', 'baking', 0.50), + ('flour', 'baking', 0.25), + ('salt', 'baking', 0.75); + +CREATE OR REPLACE TABLE __result1 AS +SELECT + item, + FIRST_VALUE(item) OVER (PARTITION BY category ORDER BY price) AS cheapest_alternative, + LAST_VALUE(item) OVER (PARTITION BY category ORDER BY price) AS most_expensive_alternative, +FROM groceries; + +CREATE OR REPLACE TABLE __expected1 ( + item STRING, + cheapest_alternative STRING, + most_expensive_alternative STRING, +); +INSERT INTO __expected1 VALUES + ('apple', 'apple', 'banana'), + ('banana', 'apple', 'banana'), + ('carrot', 'carrot', 'eggplant'), + ('eggplant', 'carrot', 'eggplant'), + ('flour', 'flour', 'salt'), + ('sugar', 'flour', 'salt'), + ('salt', 'flour', 'salt'); diff --git a/tests/sql/functions/windows/lag.sql b/tests/sql/functions/windows/lag.sql new file mode 100644 index 0000000..1d4b272 --- /dev/null +++ b/tests/sql/functions/windows/lag.sql @@ -0,0 +1,19 @@ +-- LAG + +CREATE TEMP TABLE fruits (idx INT64, fruit_name STRING); +INSERT INTO fruits VALUES (1, 'apple'), (2, 'banana'), (3, 'cherry'); + +CREATE OR REPLACE TABLE __result1 AS +SELECT + fruit_name, + LAG(fruit_name) OVER (ORDER BY idx) AS previous_fruit +FROM fruits; + +CREATE OR REPLACE TABLE __expected1 ( + fruit_name STRING, + previous_fruit STRING, +); +INSERT INTO __expected1 VALUES + ('apple', NULL), + ('banana', 'apple'), + ('cherry', 'banana');