From 4cc7d5a6dcda8f275b4156a9a23bbe5380be1b53 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 10 Aug 2023 11:23:50 +0200 Subject: Merging upstream version 17.11.0. Signed-off-by: Daniel Baumann --- tests/dialects/test_bigquery.py | 21 +++++++++-- tests/dialects/test_duckdb.py | 32 ++++++++++++++++ tests/dialects/test_hive.py | 1 + tests/dialects/test_presto.py | 31 ++++++++-------- tests/dialects/test_redshift.py | 43 +++++++++++++++------- tests/dialects/test_snowflake.py | 17 +++++---- tests/dialects/test_starrocks.py | 16 ++++++-- tests/dialects/test_tsql.py | 8 ++++ tests/fixtures/identity.sql | 2 + tests/fixtures/optimizer/optimizer.sql | 37 +++++++++++++++++++ tests/fixtures/optimizer/pushdown_projections.sql | 9 +++++ tests/fixtures/optimizer/qualify_columns.sql | 23 +++++++++++- .../optimizer/qualify_columns__invalid.sql | 1 + tests/test_expressions.py | 4 ++ tests/test_optimizer.py | 2 +- tests/test_parser.py | 14 +++++++ 16 files changed, 215 insertions(+), 46 deletions(-) (limited to 'tests') diff --git a/tests/dialects/test_bigquery.py b/tests/dialects/test_bigquery.py index 8d01ebe..b5f91cf 100644 --- a/tests/dialects/test_bigquery.py +++ b/tests/dialects/test_bigquery.py @@ -200,8 +200,15 @@ class TestBigQuery(Validator): ) self.validate_all( "REGEXP_CONTAINS('foo', '.*')", - read={"bigquery": "REGEXP_CONTAINS('foo', '.*')"}, - write={"mysql": "REGEXP_LIKE('foo', '.*')"}, + read={ + "bigquery": "REGEXP_CONTAINS('foo', '.*')", + "mysql": "REGEXP_LIKE('foo', '.*')", + "starrocks": "REGEXP('foo', '.*')", + }, + write={ + "mysql": "REGEXP_LIKE('foo', '.*')", + "starrocks": "REGEXP('foo', '.*')", + }, ), self.validate_all( '"""x"""', @@ -481,13 +488,21 @@ class TestBigQuery(Validator): write={ "bigquery": "DATE_DIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE), DAY)", "mysql": "DATEDIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", + "starrocks": "DATE_DIFF('DAY', CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", + }, + ) + self.validate_all( + "DATE_DIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE), DAY)", + read={ + "mysql": "DATEDIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", + "starrocks": "DATEDIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", }, ) self.validate_all( "DATE_DIFF(DATE '2010-07-07', DATE '2008-12-25', MINUTE)", write={ "bigquery": "DATE_DIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE), MINUTE)", - "mysql": "DATEDIFF(CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", + "starrocks": "DATE_DIFF('MINUTE', CAST('2010-07-07' AS DATE), CAST('2008-12-25' AS DATE))", }, ) self.validate_all( diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py index 5284700..5c35d8f 100644 --- a/tests/dialects/test_duckdb.py +++ b/tests/dialects/test_duckdb.py @@ -583,6 +583,38 @@ class TestDuckDB(Validator): write={"duckdb": "SELECT a, BOOL_OR(b) FROM table GROUP BY a"}, ) + def test_encode_decode(self): + self.validate_all( + "ENCODE(x)", + read={ + "spark": "ENCODE(x, 'utf-8')", + "presto": "TO_UTF8(x)", + }, + write={ + "duckdb": "ENCODE(x)", + "spark": "ENCODE(x, 'utf-8')", + "presto": "TO_UTF8(x)", + }, + ) + self.validate_all( + "DECODE(x)", + read={ + "spark": "DECODE(x, 'utf-8')", + "presto": "FROM_UTF8(x)", + }, + write={ + "duckdb": "DECODE(x)", + "spark": "DECODE(x, 'utf-8')", + "presto": "FROM_UTF8(x)", + }, + ) + self.validate_all( + "DECODE(x)", + read={ + "presto": "FROM_UTF8(x, y)", + }, + ) + def test_rename_table(self): self.validate_all( "ALTER TABLE db.t1 RENAME TO db.t2", diff --git a/tests/dialects/test_hive.py b/tests/dialects/test_hive.py index 6dd484f..4c463f7 100644 --- a/tests/dialects/test_hive.py +++ b/tests/dialects/test_hive.py @@ -390,6 +390,7 @@ class TestHive(Validator): ) def test_hive(self): + self.validate_identity("SELECT transform") self.validate_identity("SELECT * FROM test DISTRIBUTE BY y SORT BY x DESC ORDER BY l") self.validate_identity( "SELECT * FROM test WHERE RAND() <= 0.1 DISTRIBUTE BY RAND() SORT BY RAND()" diff --git a/tests/dialects/test_presto.py b/tests/dialects/test_presto.py index a2800bd..ec1ad30 100644 --- a/tests/dialects/test_presto.py +++ b/tests/dialects/test_presto.py @@ -466,6 +466,7 @@ class TestPresto(Validator): read={"spark": "STARTSWITH('abc', 'a')"}, write={ "presto": "STARTS_WITH('abc', 'a')", + "snowflake": "STARTSWITH('abc', 'a')", "spark": "STARTSWITH('abc', 'a')", }, ) @@ -740,46 +741,44 @@ class TestPresto(Validator): ) def test_encode_decode(self): + self.validate_identity("FROM_UTF8(x, y)") + self.validate_all( "TO_UTF8(x)", + read={ + "duckdb": "ENCODE(x)", + "spark": "ENCODE(x, 'utf-8')", + }, write={ + "duckdb": "ENCODE(x)", + "presto": "TO_UTF8(x)", "spark": "ENCODE(x, 'utf-8')", }, ) self.validate_all( "FROM_UTF8(x)", - write={ + read={ + "duckdb": "DECODE(x)", "spark": "DECODE(x, 'utf-8')", }, - ) - self.validate_all( - "FROM_UTF8(x, y)", - write={ - "presto": "FROM_UTF8(x, y)", - }, - ) - self.validate_all( - "ENCODE(x, 'utf-8')", - write={ - "presto": "TO_UTF8(x)", - }, - ) - self.validate_all( - "DECODE(x, 'utf-8')", write={ + "duckdb": "DECODE(x)", "presto": "FROM_UTF8(x)", + "spark": "DECODE(x, 'utf-8')", }, ) self.validate_all( "ENCODE(x, 'invalid')", write={ "presto": UnsupportedError, + "duckdb": UnsupportedError, }, ) self.validate_all( "DECODE(x, 'invalid')", write={ "presto": UnsupportedError, + "duckdb": UnsupportedError, }, ) diff --git a/tests/dialects/test_redshift.py b/tests/dialects/test_redshift.py index 90c953f..96e9e20 100644 --- a/tests/dialects/test_redshift.py +++ b/tests/dialects/test_redshift.py @@ -5,15 +5,16 @@ class TestRedshift(Validator): dialect = "redshift" def test_redshift(self): - self.validate_identity("SELECT * FROM #x") - self.validate_identity("SELECT INTERVAL '5 day'") - self.validate_identity("foo$") - self.validate_identity("$foo") - + self.validate_all( + "SELECT INTERVAL '5 days'", + read={ + "": "SELECT INTERVAL '5' days", + }, + ) self.validate_all( "SELECT ADD_MONTHS('2008-03-31', 1)", write={ - "redshift": "SELECT DATEADD(month, 1, '2008-03-31')", + "redshift": "SELECT DATEADD(month, 1, CAST('2008-03-31' AS DATE))", "trino": "SELECT DATE_ADD('month', 1, CAST(CAST('2008-03-31' AS TIMESTAMP) AS DATE))", }, ) @@ -88,11 +89,6 @@ class TestRedshift(Validator): "snowflake": "SELECT DATE_PART(month, CAST('20220502' AS DATE))", }, ) - self.validate_all("SELECT INTERVAL '5 days'", read={"": "SELECT INTERVAL '5' days"}) - self.validate_all("CONVERT(INT, x)", write={"redshift": "CAST(x AS INTEGER)"}) - self.validate_all( - "DATEADD('day', ndays, caldate)", write={"redshift": "DATEADD(day, ndays, caldate)"} - ) self.validate_all( 'create table "group" ("col" char(10))', write={ @@ -186,7 +182,7 @@ class TestRedshift(Validator): self.validate_all( "DATEDIFF('day', a, b)", write={ - "redshift": "DATEDIFF(day, a, b)", + "redshift": "DATEDIFF(day, CAST(a AS DATE), CAST(b AS DATE))", "presto": "DATE_DIFF('day', CAST(CAST(a AS TIMESTAMP) AS DATE), CAST(CAST(b AS TIMESTAMP) AS DATE))", }, ) @@ -198,15 +194,18 @@ class TestRedshift(Validator): ) def test_identity(self): + self.validate_identity("SELECT * FROM #x") + self.validate_identity("SELECT INTERVAL '5 day'") + self.validate_identity("foo$") + self.validate_identity("$foo") self.validate_identity("CAST('bla' AS SUPER)") self.validate_identity("CREATE TABLE real1 (realcol REAL)") self.validate_identity("CAST('foo' AS HLLSKETCH)") - self.validate_identity("SELECT DATEADD(day, 1, 'today')") self.validate_identity("'abc' SIMILAR TO '(b|c)%'") + self.validate_identity("CREATE TABLE datetable (start_date DATE, end_date DATE)") self.validate_identity( "SELECT caldate + INTERVAL '1 second' AS dateplus FROM date WHERE caldate = '12-31-2008'" ) - self.validate_identity("CREATE TABLE datetable (start_date DATE, end_date DATE)") self.validate_identity( "SELECT COUNT(*) FROM event WHERE eventname LIKE '%Ring%' OR eventname LIKE '%Die%'" ) @@ -225,6 +224,22 @@ class TestRedshift(Validator): self.validate_identity( "CREATE TABLE SOUP (SOUP1 VARCHAR(50) NOT NULL ENCODE ZSTD, SOUP2 VARCHAR(70) NULL ENCODE DELTA)" ) + self.validate_identity( + "SELECT DATEADD(day, 1, 'today')", + "SELECT DATEADD(day, 1, CAST('today' AS DATE))", + ) + self.validate_identity( + "SELECT DATEADD('day', ndays, caldate)", + "SELECT DATEADD(day, ndays, CAST(caldate AS DATE))", + ) + self.validate_identity( + "CONVERT(INT, x)", + "CAST(x AS INTEGER)", + ) + self.validate_identity( + "SELECT DATE_ADD('day', 1, DATE('2023-01-01'))", + "SELECT DATEADD(day, 1, CAST(DATE('2023-01-01') AS DATE))", + ) def test_values(self): self.validate_all( diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py index a889e1d..3053d47 100644 --- a/tests/dialects/test_snowflake.py +++ b/tests/dialects/test_snowflake.py @@ -8,6 +8,7 @@ class TestSnowflake(Validator): dialect = "snowflake" def test_snowflake(self): + self.validate_identity("WEEKOFYEAR(tstamp)") self.validate_identity("SELECT SUM(amount) FROM mytable GROUP BY ALL") self.validate_identity("WITH x AS (SELECT 1 AS foo) SELECT foo FROM IDENTIFIER('x')") self.validate_identity("WITH x AS (SELECT 1 AS foo) SELECT IDENTIFIER('foo') FROM x") @@ -33,12 +34,21 @@ class TestSnowflake(Validator): self.validate_identity("ALTER TABLE foo UNSET DATA_RETENTION_TIME_IN_DAYS, CHANGE_TRACKING") self.validate_identity("COMMENT IF EXISTS ON TABLE foo IS 'bar'") self.validate_identity("SELECT CONVERT_TIMEZONE('UTC', 'America/Los_Angeles', col)") + self.validate_identity("REGEXP_REPLACE('target', 'pattern', '\n')") self.validate_identity( 'COPY INTO NEW_TABLE ("foo", "bar") FROM (SELECT $1, $2, $3, $4 FROM @%old_table)' ) self.validate_identity( "SELECT state, city, SUM(retail_price * quantity) AS gross_revenue FROM sales GROUP BY ALL" ) + self.validate_identity( + r"SELECT RLIKE(a, $$regular expression with \ characters: \d{2}-\d{3}-\d{4}$$, 'i') FROM log_source", + r"SELECT REGEXP_LIKE(a, 'regular expression with \\ characters: \\d{2}-\\d{3}-\\d{4}', 'i') FROM log_source", + ) + self.validate_identity( + r"SELECT $$a ' \ \t \x21 z $ $$", + r"SELECT 'a \' \\ \\t \\x21 z $ '", + ) self.validate_all("CAST(x AS BYTEINT)", write={"snowflake": "CAST(x AS INT)"}) self.validate_all("CAST(x AS CHAR VARYING)", write={"snowflake": "CAST(x AS VARCHAR)"}) @@ -384,13 +394,6 @@ class TestSnowflake(Validator): "snowflake": "SELECT 'a'", }, ) - self.validate_all( - r"SELECT $$a ' \ \t \x21 z $ $$", - write={ - "snowflake": r"SELECT 'a \' \ \t \x21 z $ '", - }, - ) - self.validate_identity("REGEXP_REPLACE('target', 'pattern', '\n')") self.validate_all( "SELECT RLIKE(a, b)", write={ diff --git a/tests/dialects/test_starrocks.py b/tests/dialects/test_starrocks.py index 96e20da..21a89d7 100644 --- a/tests/dialects/test_starrocks.py +++ b/tests/dialects/test_starrocks.py @@ -1,7 +1,7 @@ from tests.dialects.test_dialect import Validator -class TestMySQL(Validator): +class TestStarrocks(Validator): dialect = "starrocks" def test_identity(self): @@ -10,11 +10,21 @@ class TestMySQL(Validator): def test_time(self): self.validate_identity("TIMESTAMP('2022-01-01')") + self.validate_identity( + "SELECT DATE_DIFF('second', '2010-11-30 23:59:59', '2010-11-30 20:58:59')" + ) + self.validate_identity( + "SELECT DATE_DIFF('minute', '2010-11-30 23:59:59', '2010-11-30 20:58:59')" + ) def test_regex(self): self.validate_all( - "SELECT REGEXP_LIKE(abc, '%foo%')", - write={ + "SELECT REGEXP(abc, '%foo%')", + read={ + "mysql": "SELECT REGEXP_LIKE(abc, '%foo%')", "starrocks": "SELECT REGEXP(abc, '%foo%')", }, + write={ + "mysql": "SELECT REGEXP_LIKE(abc, '%foo%')", + }, ) diff --git a/tests/dialects/test_tsql.py b/tests/dialects/test_tsql.py index 5266bd4..f43b41b 100644 --- a/tests/dialects/test_tsql.py +++ b/tests/dialects/test_tsql.py @@ -6,6 +6,10 @@ class TestTSQL(Validator): dialect = "tsql" def test_tsql(self): + projection = parse_one("SELECT a = 1", read="tsql").selects[0] + projection.assert_is(exp.Alias) + projection.args["alias"].assert_is(exp.Identifier) + self.validate_identity("UPDATE x SET y = 1 OUTPUT x.a, x.b INTO @y FROM y") self.validate_identity("UPDATE x SET y = 1 OUTPUT x.a, x.b FROM y") self.validate_identity("INSERT INTO x (y) OUTPUT x.a, x.b INTO l SELECT * FROM z") @@ -25,6 +29,10 @@ class TestTSQL(Validator): self.validate_identity('SELECT "x"."y" FROM foo') self.validate_identity("SELECT * FROM #foo") self.validate_identity("SELECT * FROM ##foo") + self.validate_identity("SELECT a = 1", "SELECT 1 AS a") + self.validate_identity( + "SELECT a = 1 UNION ALL SELECT a = b", "SELECT 1 AS a UNION ALL SELECT b AS a" + ) self.validate_identity( "SELECT x FROM @MyTableVar AS m JOIN Employee ON m.EmployeeID = Employee.EmployeeID" ) diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql index 10f77ac..0690421 100644 --- a/tests/fixtures/identity.sql +++ b/tests/fixtures/identity.sql @@ -87,6 +87,7 @@ x IN ('a', 'a''a') x IN ((1)) x BETWEEN -1 AND 1 x BETWEEN 'a' || b AND 'c' || d +((a, b) AS c) NOT x IS NULL x IS TRUE x IS FALSE @@ -852,3 +853,4 @@ SELECT * FROM (tbl1 CROSS JOIN (SELECT * FROM tbl2) AS t1) /* comment1 */ INSERT INTO x /* comment2 */ VALUES (1, 2, 3) /* comment1 */ UPDATE tbl /* comment2 */ SET x = 2 WHERE x < 2 /* comment1 */ DELETE FROM x /* comment2 */ WHERE y > 1 +SELECT next, transform, if diff --git a/tests/fixtures/optimizer/optimizer.sql b/tests/fixtures/optimizer/optimizer.sql index 981e052..74572d2 100644 --- a/tests/fixtures/optimizer/optimizer.sql +++ b/tests/fixtures/optimizer/optimizer.sql @@ -907,3 +907,40 @@ JOIN "x" AS "x" ON "y"."b" = "x"."b" GROUP BY "x"."a"; + +# title: select * from a cte, which had one of its two columns aliased +WITH cte(x, y) AS (SELECT 1, 2) SELECT * FROM cte AS cte(a); +WITH "cte" AS ( + SELECT + 1 AS "x", + 2 AS "y" +) +SELECT + "cte"."a" AS "a", + "cte"."y" AS "y" +FROM "cte" AS "cte"("a"); + +# title: select single column from a cte using its alias +WITH cte(x) AS (SELECT 1) SELECT a FROM cte AS cte(a); +WITH "cte" AS ( + SELECT + 1 AS "x" +) +SELECT + "cte"."a" AS "a" +FROM "cte" AS "cte"("a"); + +# title: joined ctes with a "using" clause, one of which has had its column aliased +WITH m(a) AS (SELECT 1), n(b) AS (SELECT 1) SELECT * FROM m JOIN n AS foo(a) USING (a); +WITH "m" AS ( + SELECT + 1 AS "a" +), "n" AS ( + SELECT + 1 AS "b" +) +SELECT + COALESCE("m"."a", "foo"."a") AS "a" +FROM "m" +JOIN "n" AS "foo"("a") + ON "m"."a" = "foo"."a"; diff --git a/tests/fixtures/optimizer/pushdown_projections.sql b/tests/fixtures/optimizer/pushdown_projections.sql index 86dea8c..70fd9b0 100644 --- a/tests/fixtures/optimizer/pushdown_projections.sql +++ b/tests/fixtures/optimizer/pushdown_projections.sql @@ -61,6 +61,15 @@ SELECT i.a AS a FROM x AS i LEFT JOIN (SELECT _q_0.a AS a FROM (SELECT x.a AS a WITH cte AS (SELECT source.a AS a, ROW_NUMBER() OVER (PARTITION BY source.id, source.timestamp ORDER BY source.a DESC) AS index FROM source AS source QUALIFY index) SELECT cte.a AS a FROM cte; WITH cte AS (SELECT source.a AS a FROM source AS source QUALIFY ROW_NUMBER() OVER (PARTITION BY source.id, source.timestamp ORDER BY source.a DESC)) SELECT cte.a AS a FROM cte; +WITH cte AS (SELECT 1 AS x, 2 AS y, 3 AS z) SELECT cte.a FROM cte AS cte(a); +WITH cte AS (SELECT 1 AS x) SELECT cte.a AS a FROM cte AS cte(a); + +WITH cte(x, y, z) AS (SELECT 1, 2, 3) SELECT a, z FROM cte AS cte(a); +WITH cte AS (SELECT 1 AS x, 3 AS z) SELECT cte.a AS a, cte.z AS z FROM cte AS cte(a); + +WITH cte(x, y, z) AS (SELECT 1, 2, 3) SELECT a, z FROM (SELECT * FROM cte AS cte(b)) AS cte(a); +WITH cte AS (SELECT 1 AS x, 3 AS z) SELECT cte.a AS a, cte.z AS z FROM (SELECT cte.b AS a, cte.z AS z FROM cte AS cte(b)) AS cte; + -------------------------------------- -- Unknown Star Expansion -------------------------------------- diff --git a/tests/fixtures/optimizer/qualify_columns.sql b/tests/fixtures/optimizer/qualify_columns.sql index 8a2519e..7ba8e54 100644 --- a/tests/fixtures/optimizer/qualify_columns.sql +++ b/tests/fixtures/optimizer/qualify_columns.sql @@ -125,7 +125,7 @@ SELECT COALESCE(x.a) AS d FROM x JOIN y ON x.b = y.b GROUP BY d; SELECT COALESCE(x.a) AS d FROM x AS x JOIN y AS y ON x.b = y.b GROUP BY COALESCE(x.a); SELECT a + 1 AS d FROM x WHERE d > 1; -SELECT x.a + 1 AS d FROM x AS x WHERE x.a + 1 > 1; +SELECT x.a + 1 AS d FROM x AS x WHERE (x.a + 1) > 1; # execute: false SELECT a + 1 AS d, d + 2 FROM x; @@ -300,6 +300,12 @@ WITH z AS (SELECT x.a AS a, x.b AS b FROM x AS x), q AS (SELECT z.b AS b FROM z) WITH z AS ((SELECT b FROM x UNION ALL SELECT b FROM y) ORDER BY b) SELECT * FROM z; WITH z AS ((SELECT x.b AS b FROM x AS x UNION ALL SELECT y.b AS b FROM y AS y) ORDER BY b) SELECT z.b AS b FROM z; +WITH cte(x) AS (SELECT 1) SELECT * FROM cte AS cte(a); +WITH cte AS (SELECT 1 AS x) SELECT cte.a AS a FROM cte AS cte(a); + +WITH cte(x, y) AS (SELECT 1, 2) SELECT cte.* FROM cte AS cte(a); +WITH cte AS (SELECT 1 AS x, 2 AS y) SELECT cte.a AS a, cte.y AS y FROM cte AS cte(a); + -------------------------------------- -- Except and Replace -------------------------------------- @@ -383,6 +389,9 @@ SELECT x.b AS b FROM t AS t JOIN x AS x ON t.a = x.a; SELECT a FROM t1 JOIN t2 USING(a); SELECT COALESCE(t1.a, t2.a) AS a FROM t1 AS t1 JOIN t2 AS t2 ON t1.a = t2.a; +WITH m(a) AS (SELECT 1), n(b) AS (SELECT 1) SELECT * FROM m JOIN n AS foo(a) USING (a); +WITH m AS (SELECT 1 AS a), n AS (SELECT 1 AS b) SELECT COALESCE(m.a, foo.a) AS a FROM m JOIN n AS foo(a) ON m.a = foo.a; + -------------------------------------- -- Hint with table reference -------------------------------------- @@ -444,7 +453,7 @@ SELECT x.a AS a, x.b AS b FROM x AS x QUALIFY COUNT(x.a) OVER (PARTITION BY x.b) -- Expand laterals -------------------------------------- # execute: false -select 2 AS d, d + 1 FROM x WHERE d = 2 GROUP BY d; +SELECT 2 AS d, d + 1 FROM x WHERE d = 2 GROUP BY d; SELECT 2 AS d, 2 + 1 AS _col_1 FROM x AS x WHERE 2 = 2 GROUP BY 1; # title: expand alias reference @@ -471,6 +480,16 @@ FROM ( ); SELECT _q_0.i AS i, _q_0.j AS j FROM (SELECT x.a + 1 AS i, x.a + 1 + 1 AS j FROM x AS x) AS _q_0; +# title: wrap expanded alias to ensure operator precedence isn't broken +# execute: false +SELECT x.a + x.b AS f, f * x.b FROM x; +SELECT x.a + x.b AS f, (x.a + x.b) * x.b AS _col_1 FROM x AS x; + +# title: no need to wrap expanded alias +# execute: false +SELECT x.a + x.b AS f, f, f + 5 FROM x; +SELECT x.a + x.b AS f, x.a + x.b AS _col_1, x.a + x.b + 5 AS _col_2 FROM x AS x; + -------------------------------------- -- Wrapped tables / join constructs -------------------------------------- diff --git a/tests/fixtures/optimizer/qualify_columns__invalid.sql b/tests/fixtures/optimizer/qualify_columns__invalid.sql index f3d8b6a..09a9ddc 100644 --- a/tests/fixtures/optimizer/qualify_columns__invalid.sql +++ b/tests/fixtures/optimizer/qualify_columns__invalid.sql @@ -11,3 +11,4 @@ SELECT x.a FROM x JOIN y USING (a); SELECT a, SUM(b) FROM x GROUP BY 3; SELECT p FROM (SELECT x from xx) y CROSS JOIN yy CROSS JOIN zz SELECT a FROM (SELECT * FROM x CROSS JOIN y); +SELECT x FROM tbl AS tbl(a); diff --git a/tests/test_expressions.py b/tests/test_expressions.py index 1aab1c0..f68ced2 100644 --- a/tests/test_expressions.py +++ b/tests/test_expressions.py @@ -328,6 +328,10 @@ class TestExpressions(unittest.TestCase): cte = expression.find(exp.CTE) self.assertEqual(cte.alias_column_names, ["a", "b"]) + expression = parse_one("SELECT * FROM tbl AS tbl(a, b)") + table = expression.find(exp.Table) + self.assertEqual(table.alias_column_names, ["a", "b"]) + def test_ctes(self): expression = parse_one("SELECT a FROM x") self.assertEqual(expression.ctes, []) diff --git a/tests/test_optimizer.py b/tests/test_optimizer.py index 64d7db7..3fe53e4 100644 --- a/tests/test_optimizer.py +++ b/tests/test_optimizer.py @@ -291,7 +291,7 @@ class TestOptimizer(unittest.TestCase): # check order of lateral expansion with no schema self.assertEqual( optimizer.optimize("SELECT a + 1 AS d, d + 1 AS e FROM x WHERE e > 1 GROUP BY e").sql(), - 'SELECT "x"."a" + 1 AS "d", "x"."a" + 1 + 1 AS "e" FROM "x" AS "x" WHERE "x"."a" + 2 > 1 GROUP BY "x"."a" + 1 + 1', + 'SELECT "x"."a" + 1 AS "d", "x"."a" + 1 + 1 AS "e" FROM "x" AS "x" WHERE ("x"."a" + 2) > 1 GROUP BY "x"."a" + 1 + 1', ) self.assertEqual( diff --git a/tests/test_parser.py b/tests/test_parser.py index 027a9ca..e7b0ca9 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -219,6 +219,9 @@ class TestParser(unittest.TestCase): with self.assertRaises(ParseError): parse_one("WITH cte AS (SELECT * FROM x)") + with self.assertRaises(ParseError): + parse_one("SELECT foo( FROM bar") + self.assertEqual( parse_one( "CREATE TABLE t (i UInt8) ENGINE = AggregatingMergeTree() ORDER BY tuple()", @@ -694,3 +697,14 @@ class TestParser(unittest.TestCase): def test_parse_floats(self): self.assertTrue(parse_one("1. ").is_number) + + def test_parse_terse_coalesce(self): + self.assertIsNotNone(parse_one("SELECT x ?? y FROM z").find(exp.Coalesce)) + self.assertEqual( + parse_one("SELECT a, b ?? 'No Data' FROM z").sql(), + "SELECT a, COALESCE(b, 'No Data') FROM z", + ) + self.assertEqual( + parse_one("SELECT a, b ?? c ?? 'No Data' FROM z").sql(), + "SELECT a, COALESCE(COALESCE(b, c), 'No Data') FROM z", + ) -- cgit v1.2.3