summaryrefslogtreecommitdiffstats
path: root/tests/dialects/test_tsql.py
diff options
context:
space:
mode:
Diffstat (limited to 'tests/dialects/test_tsql.py')
-rw-r--r--tests/dialects/test_tsql.py191
1 files changed, 160 insertions, 31 deletions
diff --git a/tests/dialects/test_tsql.py b/tests/dialects/test_tsql.py
index 0ac94f2..07179ef 100644
--- a/tests/dialects/test_tsql.py
+++ b/tests/dialects/test_tsql.py
@@ -6,8 +6,22 @@ class TestTSQL(Validator):
dialect = "tsql"
def test_tsql(self):
- self.validate_all(
- "WITH t(c) AS (SELECT 1) SELECT * INTO foo FROM (SELECT c FROM t) AS temp",
+ # https://learn.microsoft.com/en-us/previous-versions/sql/sql-server-2008-r2/ms187879(v=sql.105)?redirectedfrom=MSDN
+ # tsql allows .. which means use the default schema
+ self.validate_identity("SELECT * FROM a..b")
+
+ self.validate_identity("SELECT CONCAT(column1, column2)")
+ self.validate_identity("SELECT TestSpecialChar.Test# FROM TestSpecialChar")
+ self.validate_identity("SELECT TestSpecialChar.Test@ FROM TestSpecialChar")
+ self.validate_identity("SELECT TestSpecialChar.Test$ FROM TestSpecialChar")
+ self.validate_identity("SELECT TestSpecialChar.Test_ FROM TestSpecialChar")
+ self.validate_identity("SELECT TOP (2 + 1) 1")
+ self.validate_identity("SELECT * FROM t WHERE NOT c", "SELECT * FROM t WHERE NOT c <> 0")
+ self.validate_identity("1 AND true", "1 <> 0 AND (1 = 1)")
+ self.validate_identity("CAST(x AS int) OR y", "CAST(x AS INTEGER) <> 0 OR y <> 0")
+
+ self.validate_all(
+ "WITH t(c) AS (SELECT 1) SELECT * INTO foo FROM (SELECT c AS c FROM t) AS temp",
read={
"duckdb": "CREATE TABLE foo AS WITH t(c) AS (SELECT 1) SELECT c FROM t",
},
@@ -25,7 +39,7 @@ class TestTSQL(Validator):
},
)
self.validate_all(
- "WITH t(c) AS (SELECT 1) MERGE INTO x AS z USING (SELECT c FROM t) AS y ON a = b WHEN MATCHED THEN UPDATE SET a = y.b",
+ "WITH t(c) AS (SELECT 1) MERGE INTO x AS z USING (SELECT c AS c FROM t) AS y ON a = b WHEN MATCHED THEN UPDATE SET a = y.b",
read={
"postgres": "MERGE INTO x AS z USING (WITH t(c) AS (SELECT 1) SELECT c FROM t) AS y ON a = b WHEN MATCHED THEN UPDATE SET a = y.b",
},
@@ -167,18 +181,6 @@ class TestTSQL(Validator):
)
self.validate_all(
- "SELECT DATEPART(year, CAST('2017-01-01' AS DATE))",
- read={"postgres": "SELECT DATE_PART('year', '2017-01-01'::DATE)"},
- )
- self.validate_all(
- "SELECT DATEPART(month, CAST('2017-03-01' AS DATE))",
- read={"postgres": "SELECT DATE_PART('month', '2017-03-01'::DATE)"},
- )
- self.validate_all(
- "SELECT DATEPART(day, CAST('2017-01-02' AS DATE))",
- read={"postgres": "SELECT DATE_PART('day', '2017-01-02'::DATE)"},
- )
- self.validate_all(
"SELECT CAST([a].[b] AS SMALLINT) FROM foo",
write={
"tsql": 'SELECT CAST("a"."b" AS SMALLINT) FROM foo',
@@ -229,11 +231,13 @@ class TestTSQL(Validator):
self.validate_all(
"HASHBYTES('SHA1', x)",
read={
+ "snowflake": "SHA1(x)",
"spark": "SHA(x)",
},
write={
- "tsql": "HASHBYTES('SHA1', x)",
+ "snowflake": "SHA1(x)",
"spark": "SHA(x)",
+ "tsql": "HASHBYTES('SHA1', x)",
},
)
self.validate_all(
@@ -561,6 +565,21 @@ class TestTSQL(Validator):
)
def test_ddl(self):
+ expression = parse_one("ALTER TABLE dbo.DocExe DROP CONSTRAINT FK_Column_B", dialect="tsql")
+ self.assertIsInstance(expression, exp.AlterTable)
+ self.assertIsInstance(expression.args["actions"][0], exp.Drop)
+ self.assertEqual(
+ expression.sql(dialect="tsql"), "ALTER TABLE dbo.DocExe DROP CONSTRAINT FK_Column_B"
+ )
+
+ for clusterd_keyword in ("CLUSTERED", "NONCLUSTERED"):
+ self.validate_identity(
+ 'CREATE TABLE "dbo"."benchmark" ('
+ '"name" CHAR(7) NOT NULL, '
+ '"internal_id" VARCHAR(10) NOT NULL, '
+ f'UNIQUE {clusterd_keyword} ("internal_id" ASC))'
+ )
+
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",
@@ -589,6 +608,12 @@ class TestTSQL(Validator):
},
)
self.validate_all(
+ "SELECT * INTO foo.bar.baz FROM (SELECT * FROM a.b.c) AS temp",
+ read={
+ "": "CREATE TABLE foo.bar.baz AS (SELECT * FROM a.b.c)",
+ },
+ )
+ self.validate_all(
"IF NOT EXISTS (SELECT * FROM sys.indexes WHERE object_id = object_id('db.tbl') AND name = 'idx') EXEC('CREATE INDEX idx ON db.tbl')",
read={
"": "CREATE INDEX IF NOT EXISTS idx ON db.tbl",
@@ -622,7 +647,6 @@ class TestTSQL(Validator):
"tsql": "CREATE OR ALTER VIEW a.b AS SELECT 1",
},
)
-
self.validate_all(
"ALTER TABLE a ADD b INTEGER, c INTEGER",
read={
@@ -633,7 +657,6 @@ class TestTSQL(Validator):
"tsql": "ALTER TABLE a ADD b INTEGER, c INTEGER",
},
)
-
self.validate_all(
"CREATE TABLE #mytemp (a INTEGER, b CHAR(2), c TIME(4), d FLOAT(24))",
write={
@@ -833,7 +856,7 @@ WHERE
)
def test_len(self):
- self.validate_all("LEN(x)", write={"spark": "LENGTH(x)"})
+ self.validate_all("LEN(x)", read={"": "LENGTH(x)"}, write={"spark": "LENGTH(x)"})
def test_replicate(self):
self.validate_all("REPLICATE('x', 2)", write={"spark": "REPEAT('x', 2)"})
@@ -870,11 +893,68 @@ WHERE
)
def test_datepart(self):
+ self.validate_identity(
+ "DATEPART(QUARTER, x)",
+ "DATEPART(quarter, CAST(x AS DATETIME2))",
+ )
+ self.validate_identity(
+ "DATEPART(YEAR, x)",
+ "FORMAT(CAST(x AS DATETIME2), 'yyyy')",
+ )
+ self.validate_identity(
+ "DATEPART(HOUR, date_and_time)",
+ "DATEPART(hour, CAST(date_and_time AS DATETIME2))",
+ )
+ self.validate_identity(
+ "DATEPART(WEEKDAY, date_and_time)",
+ "DATEPART(dw, CAST(date_and_time AS DATETIME2))",
+ )
+ self.validate_identity(
+ "DATEPART(DW, date_and_time)",
+ "DATEPART(dw, CAST(date_and_time AS DATETIME2))",
+ )
+
self.validate_all(
"SELECT DATEPART(month,'1970-01-01')",
- write={"spark": "SELECT DATE_FORMAT(CAST('1970-01-01' AS TIMESTAMP), 'MM')"},
+ write={
+ "postgres": "SELECT TO_CHAR(CAST('1970-01-01' AS TIMESTAMP), 'MM')",
+ "spark": "SELECT DATE_FORMAT(CAST('1970-01-01' AS TIMESTAMP), 'MM')",
+ "tsql": "SELECT FORMAT(CAST('1970-01-01' AS DATETIME2), 'MM')",
+ },
+ )
+ self.validate_all(
+ "SELECT DATEPART(year, CAST('2017-01-01' AS DATE))",
+ read={
+ "postgres": "SELECT DATE_PART('year', '2017-01-01'::DATE)",
+ },
+ write={
+ "postgres": "SELECT TO_CHAR(CAST(CAST('2017-01-01' AS DATE) AS TIMESTAMP), 'YYYY')",
+ "spark": "SELECT DATE_FORMAT(CAST(CAST('2017-01-01' AS DATE) AS TIMESTAMP), 'yyyy')",
+ "tsql": "SELECT FORMAT(CAST(CAST('2017-01-01' AS DATE) AS DATETIME2), 'yyyy')",
+ },
+ )
+ self.validate_all(
+ "SELECT DATEPART(month, CAST('2017-03-01' AS DATE))",
+ read={
+ "postgres": "SELECT DATE_PART('month', '2017-03-01'::DATE)",
+ },
+ write={
+ "postgres": "SELECT TO_CHAR(CAST(CAST('2017-03-01' AS DATE) AS TIMESTAMP), 'MM')",
+ "spark": "SELECT DATE_FORMAT(CAST(CAST('2017-03-01' AS DATE) AS TIMESTAMP), 'MM')",
+ "tsql": "SELECT FORMAT(CAST(CAST('2017-03-01' AS DATE) AS DATETIME2), 'MM')",
+ },
+ )
+ self.validate_all(
+ "SELECT DATEPART(day, CAST('2017-01-02' AS DATE))",
+ read={
+ "postgres": "SELECT DATE_PART('day', '2017-01-02'::DATE)",
+ },
+ write={
+ "postgres": "SELECT TO_CHAR(CAST(CAST('2017-01-02' AS DATE) AS TIMESTAMP), 'DD')",
+ "spark": "SELECT DATE_FORMAT(CAST(CAST('2017-01-02' AS DATE) AS TIMESTAMP), 'dd')",
+ "tsql": "SELECT FORMAT(CAST(CAST('2017-01-02' AS DATE) AS DATETIME2), 'dd')",
+ },
)
- self.validate_identity("DATEPART(YEAR, x)", "FORMAT(CAST(x AS DATETIME2), 'yyyy')")
def test_convert_date_format(self):
self.validate_all(
@@ -1073,10 +1153,7 @@ WHERE
def test_date_diff(self):
self.validate_identity("SELECT DATEDIFF(hour, 1.5, '2021-01-01')")
- self.validate_identity(
- "SELECT DATEDIFF(year, '2020-01-01', '2021-01-01')",
- "SELECT DATEDIFF(year, CAST('2020-01-01' AS DATETIME2), CAST('2021-01-01' AS DATETIME2))",
- )
+
self.validate_all(
"SELECT DATEDIFF(quarter, 0, '2021-01-01')",
write={
@@ -1098,7 +1175,7 @@ WHERE
write={
"tsql": "SELECT DATEDIFF(year, CAST('2020-01-01' AS DATETIME2), CAST('2021-01-01' AS DATETIME2))",
"spark": "SELECT DATEDIFF(year, CAST('2020-01-01' AS TIMESTAMP), CAST('2021-01-01' AS TIMESTAMP))",
- "spark2": "SELECT CAST(MONTHS_BETWEEN(CAST('2021-01-01' AS TIMESTAMP), CAST('2020-01-01' AS TIMESTAMP)) AS INT) / 12",
+ "spark2": "SELECT CAST(MONTHS_BETWEEN(CAST('2021-01-01' AS TIMESTAMP), CAST('2020-01-01' AS TIMESTAMP)) / 12 AS INT)",
},
)
self.validate_all(
@@ -1114,16 +1191,18 @@ WHERE
write={
"databricks": "SELECT DATEDIFF(quarter, CAST('start' AS TIMESTAMP), CAST('end' AS TIMESTAMP))",
"spark": "SELECT DATEDIFF(quarter, CAST('start' AS TIMESTAMP), CAST('end' AS TIMESTAMP))",
- "spark2": "SELECT CAST(MONTHS_BETWEEN(CAST('end' AS TIMESTAMP), CAST('start' AS TIMESTAMP)) AS INT) / 3",
+ "spark2": "SELECT CAST(MONTHS_BETWEEN(CAST('end' AS TIMESTAMP), CAST('start' AS TIMESTAMP)) / 3 AS INT)",
"tsql": "SELECT DATEDIFF(quarter, CAST('start' AS DATETIME2), CAST('end' AS DATETIME2))",
},
)
def test_iif(self):
self.validate_identity(
- "SELECT IF(cond, 'True', 'False')", "SELECT IIF(cond, 'True', 'False')"
+ "SELECT IF(cond, 'True', 'False')", "SELECT IIF(cond <> 0, 'True', 'False')"
+ )
+ self.validate_identity(
+ "SELECT IIF(cond, 'True', 'False')", "SELECT IIF(cond <> 0, 'True', 'False')"
)
- self.validate_identity("SELECT IIF(cond, 'True', 'False')")
self.validate_all(
"SELECT IIF(cond, 'True', 'False');",
write={
@@ -1173,9 +1252,14 @@ WHERE
def test_top(self):
self.validate_all(
- "SELECT TOP 3 * FROM A",
+ "SELECT DISTINCT TOP 3 * FROM A",
+ read={
+ "spark": "SELECT DISTINCT * FROM A LIMIT 3",
+ },
write={
- "spark": "SELECT * FROM A LIMIT 3",
+ "spark": "SELECT DISTINCT * FROM A LIMIT 3",
+ "teradata": "SELECT DISTINCT TOP 3 * FROM A",
+ "tsql": "SELECT DISTINCT TOP 3 * FROM A",
},
)
self.validate_all(
@@ -1292,6 +1376,26 @@ WHERE
},
)
+ def test_temporal_table(self):
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON)"""
+ )
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START HIDDEN NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END HIDDEN NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON(HISTORY_TABLE="dbo"."benchmark_history", DATA_CONSISTENCY_CHECK=ON))"""
+ )
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON(HISTORY_TABLE="dbo"."benchmark_history", DATA_CONSISTENCY_CHECK=ON))"""
+ )
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON(HISTORY_TABLE="dbo"."benchmark_history", DATA_CONSISTENCY_CHECK=OFF))"""
+ )
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON(HISTORY_TABLE="dbo"."benchmark_history"))"""
+ )
+ self.validate_identity(
+ """CREATE TABLE test ("data" CHAR(7), "valid_from" DATETIME2(2) GENERATED ALWAYS AS ROW START NOT NULL, "valid_to" DATETIME2(2) GENERATED ALWAYS AS ROW END NOT NULL, PERIOD FOR SYSTEM_TIME ("valid_from", "valid_to")) WITH(SYSTEM_VERSIONING=ON(HISTORY_TABLE="dbo"."benchmark_history"))"""
+ )
+
def test_system_time(self):
self.validate_all(
"SELECT [x] FROM [a].[b] FOR SYSTEM_TIME AS OF 'foo'",
@@ -1433,3 +1537,28 @@ FROM OPENJSON(@json) WITH (
"spark": "SET count = (SELECT COUNT(1) FROM x)",
},
)
+
+ def test_qualify_derived_table_outputs(self):
+ self.validate_identity(
+ "WITH t AS (SELECT 1) SELECT * FROM t",
+ 'WITH t AS (SELECT 1 AS "1") SELECT * FROM t',
+ )
+ self.validate_identity(
+ 'WITH t AS (SELECT "c") SELECT * FROM t',
+ 'WITH t AS (SELECT "c" AS "c") SELECT * FROM t',
+ )
+ self.validate_identity(
+ "SELECT * FROM (SELECT 1) AS subq",
+ 'SELECT * FROM (SELECT 1 AS "1") AS subq',
+ )
+ self.validate_identity(
+ 'SELECT * FROM (SELECT "c") AS subq',
+ 'SELECT * FROM (SELECT "c" AS "c") AS subq',
+ )
+
+ self.validate_all(
+ "WITH t1(c) AS (SELECT 1), t2 AS (SELECT CAST(c AS INTEGER) AS c FROM t1) SELECT * FROM t2",
+ read={
+ "duckdb": "WITH t1(c) AS (SELECT 1), t2 AS (SELECT CAST(c AS INTEGER) FROM t1) SELECT * FROM t2",
+ },
+ )