Skip to content

Commit 1bbe90e

Browse files
committed
docs & tests
1 parent 585fb7e commit 1bbe90e

7 files changed

Lines changed: 120 additions & 2 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Fabric
2+
3+
## Local/Built-in Scheduler
4+
**Engine Adapter Type**: `fabric`
5+
6+
### Installation
7+
#### Microsoft Entra ID / Azure Active Directory Authentication:
8+
```
9+
pip install "sqlmesh[mssql-odbc]"
10+
```
11+
12+
### Connection options
13+
14+
| Option | Description | Type | Required |
15+
| ----------------- | ------------------------------------------------------------ | :----------: | :------: |
16+
| `type` | Engine type name - must be `fabric` | string | Y |
17+
| `host` | The hostname of the Fabric Warehouse server | string | Y |
18+
| `user` | The client id to use for authentication with the Fabric Warehouse server | string | N |
19+
| `password` | The client secret to use for authentication with the Fabric Warehouse server | string | N |
20+
| `port` | The port number of the Fabric Warehouse server | int | N |
21+
| `database` | The target database | string | N |
22+
| `charset` | The character set used for the connection | string | N |
23+
| `timeout` | The query timeout in seconds. Default: no timeout | int | N |
24+
| `login_timeout` | The timeout for connection and login in seconds. Default: 60 | int | N |
25+
| `appname` | The application name to use for the connection | string | N |
26+
| `conn_properties` | The list of connection properties | list[string] | N |
27+
| `autocommit` | Is autocommit mode enabled. Default: false | bool | N |
28+
| `driver` | The driver to use for the connection. Default: pyodbc | string | N |
29+
| `driver_name` | The driver name to use for the connection. E.g., *ODBC Driver 18 for SQL Server* | string | N |
30+
| `odbc_properties` | The dict of ODBC connection properties. E.g., authentication: ActiveDirectoryServicePrincipal. See more [here](https://learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute?view=sql-server-ver16). | dict | N |

docs/integrations/overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ SQLMesh supports the following execution engines for running SQLMesh projects:
1717
* [ClickHouse](./engines/clickhouse.md)
1818
* [Databricks](./engines/databricks.md)
1919
* [DuckDB](./engines/duckdb.md)
20+
* [Fabric](./engines/fabric.md)
2021
* [MotherDuck](./engines/motherduck.md)
2122
* [MSSQL](./engines/mssql.md)
2223
* [MySQL](./engines/mysql.md)

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ nav:
8484
- integrations/engines/clickhouse.md
8585
- integrations/engines/databricks.md
8686
- integrations/engines/duckdb.md
87+
- integrations/engines/fabric.md
8788
- integrations/engines/motherduck.md
8889
- integrations/engines/mssql.md
8990
- integrations/engines/mysql.md

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ markers = [
249249
"clickhouse_cloud: test for Clickhouse (cloud mode)",
250250
"databricks: test for Databricks",
251251
"duckdb: test for DuckDB",
252+
"fabric: test for Fabric",
252253
"motherduck: test for MotherDuck",
253254
"mssql: test for MSSQL",
254255
"mysql: test for MySQL",

sqlmesh/core/config/connection.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@
4949
"mysql",
5050
"mssql",
5151
"azuresql",
52-
"fabric",
5352
}
5453
FORBIDDEN_STATE_SYNC_ENGINES = {
5554
# Do not support row-level operations
@@ -1603,6 +1602,7 @@ class FabricConnectionConfig(MSSQLConnectionConfig):
16031602
"""
16041603

16051604
type_: t.Literal["fabric"] = Field(alias="type", default="fabric") # type: ignore
1605+
driver: t.Literal["pyodbc"] = "pyodbc"
16061606
autocommit: t.Optional[bool] = True
16071607

16081608
@property

sqlmesh/core/engine_adapter/fabric.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,10 @@ def table_exists(self, table_name: TableName) -> bool:
3131
exp.select("1")
3232
.from_("INFORMATION_SCHEMA.TABLES")
3333
.where(f"TABLE_NAME = '{table.alias_or_name}'")
34-
.where(f"TABLE_SCHEMA = '{table.db}'")
3534
)
35+
database_name = table.db
36+
if database_name:
37+
sql = sql.where(f"TABLE_SCHEMA = '{database_name}'")
3638

3739
result = self.fetchone(sql, quote_identifiers=True)
3840

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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], [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

Comments
 (0)