-- ============================================================================ -- Snowflake LATERAL FLATTEN validation queries -- -- Run this file against a real Snowflake instance to verify that the -- Unparser-generated SQL is syntactically and semantically correct. -- -- Each section shows: -- 1. The DataFusion input (SQL parsed by the planner) -- 2. The Snowflake SQL produced by the Unparser -- -- NOTE: The Unparser emits array literals as [1, 2, 3] (DataFusion syntax). -- Snowflake requires ARRAY_CONSTRUCT(1, 2, 3). The queries below use -- ARRAY_CONSTRUCT so they can run directly on Snowflake. The exact Unparser -- output is shown in the "Unparser output:" comment above each query. -- ============================================================================ -- ---------------------------------------------------------------------------- -- Setup: create and seed test tables -- ---------------------------------------------------------------------------- CREATE OR REPLACE TABLE source ( items ARRAY ); INSERT INTO source SELECT PARSE_JSON('[1, 2, 3]'); INSERT INTO source SELECT PARSE_JSON('["a", "b"]'); INSERT INTO source SELECT NULL; CREATE OR REPLACE TABLE unnest_table ( array_col ARRAY ); INSERT INTO unnest_table SELECT PARSE_JSON('[10, 20, 30]'); INSERT INTO unnest_table SELECT PARSE_JSON('[40, 50]'); INSERT INTO unnest_table SELECT NULL; CREATE OR REPLACE TABLE multi_array_table ( column_a ARRAY, column_b ARRAY ); INSERT INTO multi_array_table SELECT PARSE_JSON('[1, 2, 3]'), PARSE_JSON('["x", "y"]'); INSERT INTO multi_array_table SELECT PARSE_JSON('[4]'), PARSE_JSON('["z"]'); -- ============================================================================ -- Roundtrip tests: SQL parsed → plan → Snowflake SQL -- ============================================================================ -- -------------------------------------------------------------------------- -- Test: snowflake_unnest_to_lateral_flatten_simple -- DataFusion input: SELECT * FROM UNNEST([1,2,3]) -- Unparser output: SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => ARRAY_CONSTRUCT(1, 2, 3)) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_implicit_from -- DataFusion input: SELECT UNNEST([1,2,3]) -- Unparser output: SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => ARRAY_CONSTRUCT(1, 2, 3)) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_string_array -- DataFusion input: SELECT * FROM UNNEST(['a','b','c']) -- Unparser output: SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => ['a', 'b', 'c']) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" FROM LATERAL FLATTEN(INPUT => ARRAY_CONSTRUCT('a', 'b', 'c')) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_select_unnest_with_alias -- DataFusion input: SELECT UNNEST([1,2,3]) as c1 -- Unparser output: SELECT "_unnest_1"."VALUE" AS "c1" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" AS "c1" FROM LATERAL FLATTEN(INPUT => ARRAY_CONSTRUCT(1, 2, 3)) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_from_unnest_with_table_alias -- DataFusion input: SELECT * FROM UNNEST([1,2,3]) AS t1 (c1) -- Unparser output: SELECT "t1"."VALUE" FROM LATERAL FLATTEN(INPUT => [1, 2, 3]) AS "t1" -- -------------------------------------------------------------------------- SELECT "t1"."VALUE" FROM LATERAL FLATTEN(INPUT => ARRAY_CONSTRUCT(1, 2, 3)) AS "t1"; -- ============================================================================ -- Plan-built tests: LogicalPlan → Snowflake SQL -- These use a table called "source" with an ARRAY column "items". -- ============================================================================ -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_limit_between_projection_and_unnest -- Plan: Projection → Limit → Unnest → Projection → TableScan -- Unparser output: SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 5 -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 5; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_sort_between_projection_and_unnest -- Plan: Projection → Sort → Unnest → Projection → TableScan -- Unparser output: SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" ORDER BY "_unnest_1"."VALUE" ASC NULLS FIRST -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" ORDER BY "_unnest_1"."VALUE" ASC NULLS FIRST; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_limit_between_projection_and_unnest_with_subquery_alias -- Plan: Projection → Limit → Unnest → SubqueryAlias → Projection → TableScan -- Unparser output: SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 10 -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 10; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_composed_expression_wrapping_unnest -- Plan: Projection(CAST(placeholder AS Int64)) → Unnest → Projection → TableScan -- Unparser output: SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "item_id" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "item_id" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_composed_expression_with_limit -- Plan: Projection(CAST) → Limit → Unnest → Projection → TableScan -- Unparser output: SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "item_id" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 5 -- -------------------------------------------------------------------------- SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "item_id" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 5; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_multi_expression_projection -- Plan: Projection([CAST AS Int64, CAST AS Utf8]) → Unnest → Projection → TableScan -- Unparser output: SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "a", CAST("_unnest_1"."VALUE" AS VARCHAR) AS "b" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "a", CAST("_unnest_1"."VALUE" AS VARCHAR) AS "b" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_multi_expression_with_limit -- Plan: Projection([CAST, CAST]) → Limit → Unnest → Projection → TableScan -- Unparser output: SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "a", CAST("_unnest_1"."VALUE" AS VARCHAR) AS "b" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 10 -- -------------------------------------------------------------------------- SELECT CAST("_unnest_1"."VALUE" AS BIGINT) AS "a", CAST("_unnest_1"."VALUE" AS VARCHAR) AS "b" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" LIMIT 10; -- -------------------------------------------------------------------------- -- Test: snowflake_unnest_through_subquery_alias -- Plan: Projection → Unnest → SubqueryAlias → Projection → TableScan -- Unparser output: SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" AS "item" FROM "source" CROSS JOIN LATERAL FLATTEN(INPUT => "source"."items", OUTER => true) AS "_unnest_1"; -- ============================================================================ -- Roundtrip tests with table columns -- ============================================================================ -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_unnest_from_subselect -- DataFusion input: SELECT UNNEST(array_col) FROM (SELECT array_col FROM unnest_table WHERE array_col IS NOT NULL LIMIT 3) -- Unparser output: SELECT "_unnest_1"."VALUE" FROM (SELECT "unnest_table"."array_col" FROM "unnest_table" WHERE "unnest_table"."array_col" IS NOT NULL LIMIT 3) CROSS JOIN LATERAL FLATTEN(INPUT => "unnest_table"."array_col") AS "_unnest_1" -- -------------------------------------------------------------------------- SELECT "_unnest_1"."VALUE" FROM ( SELECT "unnest_table"."array_col" FROM "unnest_table" WHERE "unnest_table"."array_col" IS NOT NULL LIMIT 3 ) CROSS JOIN LATERAL FLATTEN(INPUT => "unnest_table"."array_col") AS "_unnest_1"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_cross_join_unnest_table_column -- DataFusion input: SELECT * FROM multi_array_table CROSS JOIN UNNEST(column_a) AS a (a) -- Unparser output: SELECT "multi_array_table"."column_a", "multi_array_table"."column_b", "a"."VALUE" FROM "multi_array_table" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_a") AS "a" -- -------------------------------------------------------------------------- SELECT "multi_array_table"."column_a", "multi_array_table"."column_b", "a"."VALUE" FROM "multi_array_table" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_a") AS "a"; -- -------------------------------------------------------------------------- -- Test: snowflake_flatten_multiple_unnest_cross_join -- DataFusion input: SELECT a.a, b.b FROM multi_array_table -- CROSS JOIN UNNEST(column_a) AS a (a) -- CROSS JOIN UNNEST(column_b) AS b (b) -- Unparser output: SELECT "a"."VALUE", "b"."VALUE" FROM "multi_array_table" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_a") AS "a" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_b") AS "b" -- -------------------------------------------------------------------------- SELECT "a"."VALUE", "b"."VALUE" FROM "multi_array_table" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_a") AS "a" CROSS JOIN LATERAL FLATTEN(INPUT => "multi_array_table"."column_b") AS "b"; -- ============================================================================ -- Cleanup -- ============================================================================ -- DROP TABLE IF EXISTS source; -- DROP TABLE IF EXISTS unnest_table; -- DROP TABLE IF EXISTS multi_array_table;