1
0
Fork 0

Adding upstream version 26.12.0.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-03-31 15:54:51 +02:00
parent aa70b5e889
commit 4118582692
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
70 changed files with 1134 additions and 340 deletions

View file

@ -15,6 +15,7 @@ from sqlglot import (
from sqlglot.helper import logger as helper_logger
from sqlglot.parser import logger as parser_logger
from tests.dialects.test_dialect import Validator
from sqlglot.optimizer.annotate_types import annotate_types
class TestBigQuery(Validator):
@ -196,6 +197,9 @@ LANGUAGE js AS
self.validate_identity("CAST(x AS TIMESTAMPTZ)", "CAST(x AS TIMESTAMP)")
self.validate_identity("CAST(x AS RECORD)", "CAST(x AS STRUCT)")
self.validate_identity("SELECT * FROM x WHERE x.y >= (SELECT MAX(a) FROM b-c) - 20")
self.validate_identity(
"SELECT FORMAT_TIMESTAMP('%Y-%m-%d %H:%M:%S', CURRENT_TIMESTAMP(), 'Europe/Berlin') AS ts"
)
self.validate_identity(
"SELECT cars, apples FROM some_table PIVOT(SUM(total_counts) FOR products IN ('general.cars' AS cars, 'food.apples' AS apples))"
)
@ -317,6 +321,13 @@ LANGUAGE js AS
"SELECT CAST(1 AS INT64)",
)
self.validate_all(
"SELECT DATE_SUB(CURRENT_DATE(), INTERVAL 2 DAY)",
write={
"bigquery": "SELECT DATE_SUB(CURRENT_DATE, INTERVAL '2' DAY)",
"databricks": "SELECT DATE_ADD(CURRENT_DATE, -2)",
},
)
self.validate_all(
"SELECT DATE_SUB(DATE '2008-12-25', INTERVAL 5 DAY)",
write={
@ -1309,8 +1320,8 @@ LANGUAGE js AS
"mysql": "DATE_ADD(CURRENT_DATE, INTERVAL '-1' DAY)",
"postgres": "CURRENT_DATE + INTERVAL '-1 DAY'",
"presto": "DATE_ADD('DAY', CAST('-1' AS BIGINT), CURRENT_DATE)",
"hive": "DATE_ADD(CURRENT_DATE, '-1')",
"spark": "DATE_ADD(CURRENT_DATE, '-1')",
"hive": "DATE_ADD(CURRENT_DATE, -1)",
"spark": "DATE_ADD(CURRENT_DATE, -1)",
},
)
self.validate_all(
@ -2356,3 +2367,18 @@ OPTIONS (
"STRING_AGG(DISTINCT a ORDER BY b DESC, c DESC LIMIT 10)",
"STRING_AGG(DISTINCT a, ',' ORDER BY b DESC, c DESC LIMIT 10)",
)
def test_annotate_timestamps(self):
sql = """
SELECT
CURRENT_TIMESTAMP() AS curr_ts,
TIMESTAMP_SECONDS(2) AS ts_seconds,
PARSE_TIMESTAMP('%c', 'Thu Dec 25 07:30:00 2008', 'UTC') AS parsed_ts,
TIMESTAMP_ADD(TIMESTAMP "2008-12-25 15:30:00+00", INTERVAL 10 MINUTE) AS ts_add,
TIMESTAMP_SUB(TIMESTAMP "2008-12-25 15:30:00+00", INTERVAL 10 MINUTE) AS ts_sub,
"""
annotated = annotate_types(self.parse_one(sql), dialect="bigquery")
for select in annotated.selects:
self.assertEqual(select.type.sql("bigquery"), "TIMESTAMP")

View file

@ -50,6 +50,7 @@ class TestDatabricks(Validator):
self.validate_identity(
"COPY INTO target FROM `s3://link` FILEFORMAT = AVRO VALIDATE = ALL FILES = ('file1', 'file2') FORMAT_OPTIONS ('opt1'='true', 'opt2'='test') COPY_OPTIONS ('mergeSchema'='true')"
)
self.validate_identity("SELECT PARSE_JSON('{}')")
self.validate_identity(
"SELECT DATE_FORMAT(CAST(FROM_UTC_TIMESTAMP(foo, 'America/Los_Angeles') AS TIMESTAMP), 'yyyy-MM-dd HH:mm:ss') AS foo FROM t",
"SELECT DATE_FORMAT(CAST(FROM_UTC_TIMESTAMP(CAST(foo AS TIMESTAMP), 'America/Los_Angeles') AS TIMESTAMP), 'yyyy-MM-dd HH:mm:ss') AS foo FROM t",

View file

@ -1569,3 +1569,29 @@ class TestDuckDB(Validator):
""",
"SELECT l_returnflag, l_linestatus, SUM(l_quantity) AS sum_qty, SUM(l_extendedprice) AS sum_base_price, SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, AVG(l_quantity) AS avg_qty, AVG(l_extendedprice) AS avg_price, AVG(l_discount) AS avg_disc, COUNT(*) AS count_order",
)
def test_at_sign_to_abs(self):
self.validate_identity(
"SELECT @col FROM t",
"SELECT ABS(col) FROM t",
)
self.validate_identity(
"SELECT @col + 1 FROM t",
"SELECT ABS(col + 1) FROM t",
)
self.validate_identity(
"SELECT (@col) + 1 FROM t",
"SELECT (ABS(col)) + 1 FROM t",
)
self.validate_identity(
"SELECT @(-1)",
"SELECT ABS((-1))",
)
self.validate_identity(
"SELECT @(-1) + 1",
"SELECT ABS((-1) + 1)",
)
self.validate_identity(
"SELECT (@-1) + 1",
"SELECT (ABS(-1)) + 1",
)

View file

@ -394,7 +394,7 @@ class TestSnowflake(Validator):
"""SELECT PARSE_JSON('{"fruit":"banana"}'):fruit""",
write={
"bigquery": """SELECT JSON_EXTRACT(PARSE_JSON('{"fruit":"banana"}'), '$.fruit')""",
"databricks": """SELECT '{"fruit":"banana"}':fruit""",
"databricks": """SELECT PARSE_JSON('{"fruit":"banana"}'):fruit""",
"duckdb": """SELECT JSON('{"fruit":"banana"}') -> '$.fruit'""",
"mysql": """SELECT JSON_EXTRACT('{"fruit":"banana"}', '$.fruit')""",
"presto": """SELECT JSON_EXTRACT(JSON_PARSE('{"fruit":"banana"}'), '$.fruit')""",
@ -1057,6 +1057,9 @@ class TestSnowflake(Validator):
staged_file.sql(dialect="snowflake"),
)
self.validate_identity('SELECT * FROM @"mystage"')
self.validate_identity('SELECT * FROM @"myschema"."mystage"/file.gz')
self.validate_identity('SELECT * FROM @"my_DB"."schEMA1".mystage/file.gz')
self.validate_identity("SELECT metadata$filename FROM @s1/")
self.validate_identity("SELECT * FROM @~")
self.validate_identity("SELECT * FROM @~/some/path/to/file.csv")
@ -1463,6 +1466,7 @@ class TestSnowflake(Validator):
"CREATE TABLE t (id INT TAG (key1='value_1', key2='value_2'))",
)
self.validate_identity("CREATE OR REPLACE TABLE foo COPY GRANTS USING TEMPLATE (SELECT 1)")
self.validate_identity("USE SECONDARY ROLES ALL")
self.validate_identity("USE SECONDARY ROLES NONE")
self.validate_identity("USE SECONDARY ROLES a, b, c")
@ -2386,11 +2390,14 @@ SINGLE = TRUE""",
)
def test_put_to_stage(self):
self.validate_identity('PUT \'file:///dir/tmp.csv\' @"my_DB"."schEMA1"."MYstage"')
# PUT with file path and stage ref containing spaces (wrapped in single quotes)
ast = parse_one("PUT 'file://my file.txt' '@s1/my folder'", read="snowflake")
self.assertIsInstance(ast, exp.Put)
self.assertEqual(ast.this, exp.Literal(this="file://my file.txt", is_string=True))
self.assertEqual(ast.args["target"], exp.Var(this="@s1/my folder"))
self.assertEqual(ast.args["target"], exp.Var(this="'@s1/my folder'"))
self.assertEqual(ast.sql("snowflake"), "PUT 'file://my file.txt' '@s1/my folder'")
# expression with additional properties
ast = parse_one(

View file

@ -322,6 +322,13 @@ TBLPROPERTIES (
},
)
self.validate_all(
"SELECT id_column, name, age FROM test_table LATERAL VIEW INLINE(struc_column) explode_view AS name, age",
write={
"presto": "SELECT id_column, name, age FROM test_table CROSS JOIN UNNEST(struc_column) AS explode_view(name, age)",
"spark": "SELECT id_column, name, age FROM test_table LATERAL VIEW INLINE(struc_column) explode_view AS name, age",
},
)
self.validate_all(
"SELECT ARRAY_AGG(x) FILTER (WHERE x = 5) FROM (SELECT 1 UNION ALL SELECT NULL) AS t(x)",
write={
@ -843,7 +850,7 @@ TBLPROPERTIES (
},
)
def test_explode_to_unnest(self):
def test_explode_projection_to_unnest(self):
self.validate_all(
"SELECT EXPLODE(x) FROM tbl",
write={
@ -951,3 +958,42 @@ TBLPROPERTIES (
self.validate_identity(
"ANALYZE TABLE ctlg.db.tbl PARTITION(foo = 'foo', bar = 'bar') COMPUTE STATISTICS NOSCAN"
)
def test_transpile_annotated_exploded_column(self):
from sqlglot.optimizer.annotate_types import annotate_types
from sqlglot.optimizer.qualify import qualify
for db_prefix in ("", "explode_view."):
with self.subTest(f"Annotated exploded column with prefix: {db_prefix}."):
sql = f"""
WITH test_table AS (
SELECT
12345 AS id_column,
ARRAY(
STRUCT('John' AS name, 30 AS age),
STRUCT('Mary' AS name, 20 AS age),
STRUCT('Mike' AS name, 80 AS age),
STRUCT('Dan' AS name, 50 AS age)
) AS struct_column
)
SELECT
id_column,
{db_prefix}new_column.name,
{db_prefix}new_column.age
FROM test_table
LATERAL VIEW EXPLODE(struct_column) explode_view AS new_column
"""
expr = self.parse_one(sql)
qualified = qualify(expr, dialect="spark")
annotated = annotate_types(qualified, dialect="spark")
self.assertEqual(
annotated.sql("spark"),
"WITH `test_table` AS (SELECT 12345 AS `id_column`, ARRAY(STRUCT('John' AS `name`, 30 AS `age`), STRUCT('Mary' AS `name`, 20 AS `age`), STRUCT('Mike' AS `name`, 80 AS `age`), STRUCT('Dan' AS `name`, 50 AS `age`)) AS `struct_column`) SELECT `test_table`.`id_column` AS `id_column`, `explode_view`.`new_column`.`name` AS `name`, `explode_view`.`new_column`.`age` AS `age` FROM `test_table` AS `test_table` LATERAL VIEW EXPLODE(`test_table`.`struct_column`) explode_view AS `new_column`",
)
self.assertEqual(
annotated.sql("presto"),
"""WITH "test_table" AS (SELECT 12345 AS "id_column", ARRAY[CAST(ROW('John', 30) AS ROW("name" VARCHAR, "age" INTEGER)), CAST(ROW('Mary', 20) AS ROW("name" VARCHAR, "age" INTEGER)), CAST(ROW('Mike', 80) AS ROW("name" VARCHAR, "age" INTEGER)), CAST(ROW('Dan', 50) AS ROW("name" VARCHAR, "age" INTEGER))] AS "struct_column") SELECT "test_table"."id_column" AS "id_column", "explode_view"."name" AS "name", "explode_view"."age" AS "age" FROM "test_table" AS "test_table" CROSS JOIN UNNEST("test_table"."struct_column") AS "explode_view"("name", "age")""",
)

View file

@ -133,12 +133,25 @@ class TestTSQL(Validator):
},
)
self.validate_all(
"WITH t(c) AS (SELECT 1) SELECT * INTO TEMP UNLOGGED foo FROM (SELECT c AS c FROM t) AS temp",
"WITH t(c) AS (SELECT 1) SELECT * INTO UNLOGGED #foo FROM (SELECT c AS c FROM t) AS temp",
write={
"duckdb": "CREATE TEMPORARY TABLE foo AS WITH t(c) AS (SELECT 1) SELECT * FROM (SELECT c AS c FROM t) AS temp",
"postgres": "WITH t(c) AS (SELECT 1) SELECT * INTO TEMPORARY foo FROM (SELECT c AS c FROM t) AS temp",
},
)
self.validate_all(
"WITH t(c) AS (SELECT 1) SELECT c INTO #foo FROM t",
read={
"tsql": "WITH t(c) AS (SELECT 1) SELECT c INTO #foo FROM t",
"postgres": "WITH t(c) AS (SELECT 1) SELECT c INTO TEMPORARY foo FROM t",
},
write={
"tsql": "WITH t(c) AS (SELECT 1) SELECT c INTO #foo FROM t",
"postgres": "WITH t(c) AS (SELECT 1) SELECT c INTO TEMPORARY foo FROM t",
"duckdb": "CREATE TEMPORARY TABLE foo AS WITH t(c) AS (SELECT 1) SELECT c FROM t",
"snowflake": "CREATE TEMPORARY TABLE foo AS WITH t(c) AS (SELECT 1) SELECT c FROM t",
},
)
self.validate_all(
"WITH t(c) AS (SELECT 1) SELECT * INTO UNLOGGED foo FROM (SELECT c AS c FROM t) AS temp",
write={
@ -151,6 +164,13 @@ class TestTSQL(Validator):
"duckdb": "CREATE TABLE foo AS WITH t(c) AS (SELECT 1) SELECT * FROM (SELECT c AS c FROM t) AS temp",
},
)
self.validate_all(
"WITH y AS (SELECT 2 AS c) INSERT INTO #t SELECT * FROM y",
write={
"duckdb": "WITH y AS (SELECT 2 AS c) INSERT INTO t SELECT * FROM y",
"postgres": "WITH y AS (SELECT 2 AS c) INSERT INTO t SELECT * FROM y",
},
)
self.validate_all(
"WITH y AS (SELECT 2 AS c) INSERT INTO t SELECT * FROM y",
read={
@ -850,6 +870,9 @@ class TestTSQL(Validator):
)
def test_ddl(self):
for colstore in ("NONCLUSTERED COLUMNSTORE", "CLUSTERED COLUMNSTORE"):
self.validate_identity(f"CREATE {colstore} INDEX index_name ON foo.bar")
for view_attr in ("ENCRYPTION", "SCHEMABINDING", "VIEW_METADATA"):
self.validate_identity(f"CREATE VIEW a.b WITH {view_attr} AS SELECT * FROM x")
@ -871,19 +894,19 @@ class TestTSQL(Validator):
self.validate_identity("CREATE SCHEMA testSchema")
self.validate_identity("CREATE VIEW t AS WITH cte AS (SELECT 1 AS c) SELECT c FROM cte")
self.validate_identity("ALTER TABLE tbl SET SYSTEM_VERSIONING=OFF")
self.validate_identity("ALTER TABLE tbl SET FILESTREAM_ON = 'test'")
self.validate_identity("ALTER TABLE tbl SET DATA_DELETION=ON")
self.validate_identity("ALTER TABLE tbl SET DATA_DELETION=OFF")
self.validate_identity(
"ALTER TABLE tbl SET SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, DATA_CONSISTENCY_CHECK=OFF, HISTORY_RETENTION_PERIOD=5 DAYS)"
)
self.validate_identity(
"ALTER TABLE tbl SET SYSTEM_VERSIONING=ON(HISTORY_TABLE=db.tbl, HISTORY_RETENTION_PERIOD=INFINITE)"
)
self.validate_identity("ALTER TABLE tbl SET SYSTEM_VERSIONING=OFF")
self.validate_identity("ALTER TABLE tbl SET FILESTREAM_ON = 'test'")
self.validate_identity(
"ALTER TABLE tbl SET DATA_DELETION=ON(FILTER_COLUMN=col, RETENTION_PERIOD=5 MONTHS)"
)
self.validate_identity("ALTER TABLE tbl SET DATA_DELETION=ON")
self.validate_identity("ALTER TABLE tbl SET DATA_DELETION=OFF")
self.validate_identity("ALTER VIEW v AS SELECT a, b, c, d FROM foo")
self.validate_identity("ALTER VIEW v AS SELECT * FROM foo WHERE c > 100")
@ -899,10 +922,44 @@ class TestTSQL(Validator):
"ALTER VIEW v WITH VIEW_METADATA AS SELECT * FROM foo WHERE c > 100",
check_command_warning=True,
)
self.validate_identity(
"CREATE COLUMNSTORE INDEX index_name ON foo.bar",
"CREATE NONCLUSTERED COLUMNSTORE INDEX index_name ON foo.bar",
)
self.validate_identity(
"CREATE PROCEDURE foo AS BEGIN DELETE FROM bla WHERE foo < CURRENT_TIMESTAMP - 7 END",
"CREATE PROCEDURE foo AS BEGIN DELETE FROM bla WHERE foo < GETDATE() - 7 END",
)
self.validate_identity(
"INSERT INTO Production.UpdatedInventory SELECT ProductID, LocationID, NewQty, PreviousQty FROM (MERGE INTO Production.ProductInventory AS pi USING (SELECT ProductID, SUM(OrderQty) FROM Sales.SalesOrderDetail AS sod INNER JOIN Sales.SalesOrderHeader AS soh ON sod.SalesOrderID = soh.SalesOrderID AND soh.OrderDate BETWEEN '20030701' AND '20030731' GROUP BY ProductID) AS src(ProductID, OrderQty) ON pi.ProductID = src.ProductID WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0 THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 THEN DELETE OUTPUT $action, Inserted.ProductID, Inserted.LocationID, Inserted.Quantity AS NewQty, Deleted.Quantity AS PreviousQty) AS Changes(Action, ProductID, LocationID, NewQty, PreviousQty) WHERE Action = 'UPDATE'",
"""INSERT INTO Production.UpdatedInventory
SELECT
ProductID,
LocationID,
NewQty,
PreviousQty
FROM (
MERGE INTO Production.ProductInventory AS pi
USING (
SELECT
ProductID,
SUM(OrderQty)
FROM Sales.SalesOrderDetail AS sod
INNER JOIN Sales.SalesOrderHeader AS soh
ON sod.SalesOrderID = soh.SalesOrderID
AND soh.OrderDate BETWEEN '20030701' AND '20030731'
GROUP BY
ProductID
) AS src(ProductID, OrderQty)
ON pi.ProductID = src.ProductID
WHEN MATCHED AND pi.Quantity - src.OrderQty >= 0 THEN UPDATE SET pi.Quantity = pi.Quantity - src.OrderQty
WHEN MATCHED AND pi.Quantity - src.OrderQty <= 0 THEN DELETE
OUTPUT $action, Inserted.ProductID, Inserted.LocationID, Inserted.Quantity AS NewQty, Deleted.Quantity AS PreviousQty
) AS Changes(Action, ProductID, LocationID, NewQty, PreviousQty)
WHERE
Action = 'UPDATE'""",
pretty=True,
)
self.validate_all(
"CREATE TABLE [#temptest] (name INTEGER)",
@ -1003,14 +1060,6 @@ class TestTSQL(Validator):
},
)
for colstore in ("NONCLUSTERED COLUMNSTORE", "CLUSTERED COLUMNSTORE"):
self.validate_identity(f"CREATE {colstore} INDEX index_name ON foo.bar")
self.validate_identity(
"CREATE COLUMNSTORE INDEX index_name ON foo.bar",
"CREATE NONCLUSTERED COLUMNSTORE INDEX index_name ON foo.bar",
)
def test_insert_cte(self):
self.validate_all(
"INSERT INTO foo.bar WITH cte AS (SELECT 1 AS one) SELECT * FROM cte",