|
| 1 | +# type: ignore |
| 2 | + |
| 3 | +import typing as t |
| 4 | + |
| 5 | +import pytest |
| 6 | +from sqlglot import exp, parse_one |
| 7 | + |
| 8 | +from sqlmesh.core.engine_adapter import FabricAdapter |
| 9 | +from tests.core.engine_adapter import to_sql_calls |
| 10 | + |
| 11 | +pytestmark = [pytest.mark.engine, pytest.mark.fabric] |
| 12 | + |
| 13 | + |
| 14 | +@pytest.fixture |
| 15 | +def adapter(make_mocked_engine_adapter: t.Callable) -> FabricAdapter: |
| 16 | + return make_mocked_engine_adapter(FabricAdapter) |
| 17 | + |
| 18 | + |
| 19 | +def test_columns(adapter: FabricAdapter): |
| 20 | + adapter.cursor.fetchall.return_value = [ |
| 21 | + ("decimal_ps", "decimal", None, 5, 4), |
| 22 | + ("decimal", "decimal", None, 18, 0), |
| 23 | + ("float", "float", None, 53, None), |
| 24 | + ("char_n", "char", 10, None, None), |
| 25 | + ("varchar_n", "varchar", 10, None, None), |
| 26 | + ("nvarchar_max", "nvarchar", -1, None, None), |
| 27 | + ] |
| 28 | + |
| 29 | + assert adapter.columns("db.table") == { |
| 30 | + "decimal_ps": exp.DataType.build("decimal(5, 4)", dialect=adapter.dialect), |
| 31 | + "decimal": exp.DataType.build("decimal(18, 0)", dialect=adapter.dialect), |
| 32 | + "float": exp.DataType.build("float(53)", dialect=adapter.dialect), |
| 33 | + "char_n": exp.DataType.build("char(10)", dialect=adapter.dialect), |
| 34 | + "varchar_n": exp.DataType.build("varchar(10)", dialect=adapter.dialect), |
| 35 | + "nvarchar_max": exp.DataType.build("nvarchar(max)", dialect=adapter.dialect), |
| 36 | + } |
| 37 | + |
| 38 | + # Verify that the adapter queries the uppercase INFORMATION_SCHEMA |
| 39 | + adapter.cursor.execute.assert_called_once_with( |
| 40 | + """SELECT [COLUMN_NAME], [DATA_TYPE], [CHARACTER_MAXIMUM_LENGTH], [NUMERIC_PRECISION], [NUMERIC_SCALE] FROM [INFORMATION_SCHEMA].[COLUMNS] WHERE [TABLE_NAME] = 'table' AND [TABLE_SCHEMA] = 'db';""" |
| 41 | + ) |
| 42 | + |
| 43 | + |
| 44 | +def test_table_exists(adapter: FabricAdapter): |
| 45 | + adapter.cursor.fetchone.return_value = (1,) |
| 46 | + assert adapter.table_exists("db.table") |
| 47 | + # Verify that the adapter queries the uppercase INFORMATION_SCHEMA |
| 48 | + adapter.cursor.execute.assert_called_once_with( |
| 49 | + """SELECT 1 FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'table' AND [TABLE_SCHEMA] = 'db';""" |
| 50 | + ) |
| 51 | + |
| 52 | + adapter.cursor.fetchone.return_value = None |
| 53 | + assert not adapter.table_exists("db.table") |
| 54 | + |
| 55 | + |
| 56 | +def test_insert_overwrite_by_time_partition(adapter: FabricAdapter): |
| 57 | + adapter.insert_overwrite_by_time_partition( |
| 58 | + "test_table", |
| 59 | + parse_one("SELECT a, b FROM tbl"), |
| 60 | + start="2022-01-01", |
| 61 | + end="2022-01-02", |
| 62 | + time_column="b", |
| 63 | + time_formatter=lambda x, _: exp.Literal.string(x.strftime("%Y-%m-%d")), |
| 64 | + columns_to_types={"a": exp.DataType.build("INT"), "b": exp.DataType.build("STRING")}, |
| 65 | + ) |
| 66 | + |
| 67 | + # Fabric adapter should use DELETE/INSERT strategy, not MERGE. |
| 68 | + assert to_sql_calls(adapter) == [ |
| 69 | + """DELETE FROM [test_table] WHERE [b] BETWEEN '2022-01-01' AND '2022-01-02';""", |
| 70 | + """INSERT INTO [test_table] ([a], [b]) SELECT [a], [b] FROM (SELECT [a] AS [a], [b] AS [b] FROM [tbl]) AS [_subquery] WHERE [b] BETWEEN '2022-01-01' AND '2022-01-02';""", |
| 71 | + ] |
| 72 | + |
| 73 | + |
| 74 | +def test_replace_query(adapter: FabricAdapter): |
| 75 | + adapter.cursor.fetchone.return_value = (1,) |
| 76 | + adapter.replace_query("test_table", parse_one("SELECT a FROM tbl"), {"a": "int"}) |
| 77 | + |
| 78 | + # This behavior is inherited from MSSQLEngineAdapter and should be TRUNCATE + INSERT |
| 79 | + assert to_sql_calls(adapter) == [ |
| 80 | + """SELECT 1 FROM [INFORMATION_SCHEMA].[TABLES] WHERE [TABLE_NAME] = 'test_table';""", |
| 81 | + "TRUNCATE TABLE [test_table];", |
| 82 | + "INSERT INTO [test_table] ([a]) SELECT [a] FROM [tbl];", |
| 83 | + ] |
0 commit comments