sqlmesh 0.191.1.dev1__py3-none-any.whl → 0.191.1.dev2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
sqlmesh/_version.py CHANGED
@@ -17,5 +17,5 @@ __version__: str
17
17
  __version_tuple__: VERSION_TUPLE
18
18
  version_tuple: VERSION_TUPLE
19
19
 
20
- __version__ = version = '0.191.1.dev1'
21
- __version_tuple__ = version_tuple = (0, 191, 1, 'dev1')
20
+ __version__ = version = '0.191.1.dev2'
21
+ __version_tuple__ = version_tuple = (0, 191, 1, 'dev2')
@@ -55,11 +55,10 @@ MOTHERDUCK_TOKEN_REGEX = re.compile(r"(\?|\&)(motherduck_token=)(\S*)")
55
55
 
56
56
 
57
57
  def _get_engine_import_validator(
58
- import_name: str, engine_type: str, extra_name: t.Optional[str] = None
58
+ import_name: str, engine_type: str, extra_name: t.Optional[str] = None, decorate: bool = True
59
59
  ) -> t.Callable:
60
60
  extra_name = extra_name or engine_type
61
61
 
62
- @model_validator(mode="before")
63
62
  def validate(cls: t.Any, data: t.Any) -> t.Any:
64
63
  check_import = (
65
64
  str_to_bool(str(data.pop("check_import", True))) if isinstance(data, dict) else True
@@ -83,7 +82,7 @@ def _get_engine_import_validator(
83
82
 
84
83
  return data
85
84
 
86
- return validate
85
+ return model_validator(mode="before")(validate) if decorate else validate
87
86
 
88
87
 
89
88
  class ConnectionConfig(abc.ABC, BaseConfig):
@@ -1422,17 +1421,50 @@ class MSSQLConnectionConfig(ConnectionConfig):
1422
1421
  autocommit: t.Optional[bool] = False
1423
1422
  tds_version: t.Optional[str] = None
1424
1423
 
1424
+ # Driver options
1425
+ driver: t.Literal["pymssql", "pyodbc"] = "pymssql"
1426
+ # PyODBC specific options
1427
+ driver_name: t.Optional[str] = None # e.g. "ODBC Driver 18 for SQL Server"
1428
+ trust_server_certificate: t.Optional[bool] = None
1429
+ encrypt: t.Optional[bool] = None
1430
+ # Dictionary of arbitrary ODBC connection properties
1431
+ # See: https://learn.microsoft.com/en-us/sql/connect/odbc/dsn-connection-string-attribute
1432
+ odbc_properties: t.Optional[t.Dict[str, t.Any]] = None
1433
+
1425
1434
  concurrent_tasks: int = 4
1426
1435
  register_comments: bool = True
1427
1436
  pre_ping: bool = True
1428
1437
 
1429
1438
  type_: t.Literal["mssql"] = Field(alias="type", default="mssql")
1430
1439
 
1431
- _engine_import_validator = _get_engine_import_validator("pymssql", "mssql")
1440
+ @model_validator(mode="before")
1441
+ @classmethod
1442
+ def _mssql_engine_import_validator(cls, data: t.Any) -> t.Any:
1443
+ if not isinstance(data, dict):
1444
+ return data
1445
+
1446
+ driver = data.get("driver", "pymssql")
1447
+
1448
+ # Define the mapping of driver to import module and extra name
1449
+ driver_configs = {"pymssql": ("pymssql", "mssql"), "pyodbc": ("pyodbc", "mssql-odbc")}
1450
+
1451
+ if driver not in driver_configs:
1452
+ raise ValueError(f"Unsupported driver: {driver}")
1453
+
1454
+ import_module, extra_name = driver_configs[driver]
1455
+
1456
+ # Use _get_engine_import_validator with decorate=False to get the raw validation function
1457
+ # This avoids the __wrapped__ issue in Python 3.9
1458
+ validator_func = _get_engine_import_validator(
1459
+ import_module, driver, extra_name, decorate=False
1460
+ )
1461
+
1462
+ # Call the raw validation function directly
1463
+ return validator_func(cls, data)
1432
1464
 
1433
1465
  @property
1434
1466
  def _connection_kwargs_keys(self) -> t.Set[str]:
1435
- return {
1467
+ base_keys = {
1436
1468
  "host",
1437
1469
  "user",
1438
1470
  "password",
@@ -1447,15 +1479,96 @@ class MSSQLConnectionConfig(ConnectionConfig):
1447
1479
  "tds_version",
1448
1480
  }
1449
1481
 
1482
+ if self.driver == "pyodbc":
1483
+ base_keys.update(
1484
+ {
1485
+ "driver_name",
1486
+ "trust_server_certificate",
1487
+ "encrypt",
1488
+ "odbc_properties",
1489
+ }
1490
+ )
1491
+ # Remove pymssql-specific parameters
1492
+ base_keys.discard("tds_version")
1493
+ base_keys.discard("conn_properties")
1494
+
1495
+ return base_keys
1496
+
1450
1497
  @property
1451
1498
  def _engine_adapter(self) -> t.Type[EngineAdapter]:
1452
1499
  return engine_adapter.MSSQLEngineAdapter
1453
1500
 
1454
1501
  @property
1455
1502
  def _connection_factory(self) -> t.Callable:
1456
- import pymssql
1503
+ if self.driver == "pymssql":
1504
+ import pymssql
1505
+
1506
+ return pymssql.connect
1507
+
1508
+ import pyodbc
1509
+
1510
+ def connect(**kwargs: t.Any) -> t.Callable:
1511
+ # Extract parameters for connection string
1512
+ host = kwargs.pop("host")
1513
+ port = kwargs.pop("port", 1433)
1514
+ database = kwargs.pop("database", "")
1515
+ user = kwargs.pop("user", None)
1516
+ password = kwargs.pop("password", None)
1517
+ driver_name = kwargs.pop("driver_name", "ODBC Driver 18 for SQL Server")
1518
+ trust_server_certificate = kwargs.pop("trust_server_certificate", False)
1519
+ encrypt = kwargs.pop("encrypt", True)
1520
+ login_timeout = kwargs.pop("login_timeout", 60)
1521
+
1522
+ # Build connection string
1523
+ conn_str_parts = [
1524
+ f"DRIVER={{{driver_name}}}",
1525
+ f"SERVER={host},{port}",
1526
+ ]
1527
+
1528
+ if database:
1529
+ conn_str_parts.append(f"DATABASE={database}")
1530
+
1531
+ # Add security options
1532
+ conn_str_parts.append(f"Encrypt={'YES' if encrypt else 'NO'}")
1533
+ if trust_server_certificate:
1534
+ conn_str_parts.append("TrustServerCertificate=YES")
1535
+
1536
+ conn_str_parts.append(f"Connection Timeout={login_timeout}")
1537
+
1538
+ # Standard SQL Server authentication
1539
+ if user:
1540
+ conn_str_parts.append(f"UID={user}")
1541
+ if password:
1542
+ conn_str_parts.append(f"PWD={password}")
1543
+
1544
+ # Add any additional ODBC properties from the odbc_properties dictionary
1545
+ if self.odbc_properties:
1546
+ for key, value in self.odbc_properties.items():
1547
+ # Skip properties that we've already set above
1548
+ if key.lower() in (
1549
+ "driver",
1550
+ "server",
1551
+ "database",
1552
+ "uid",
1553
+ "pwd",
1554
+ "encrypt",
1555
+ "trustservercertificate",
1556
+ "connection timeout",
1557
+ ):
1558
+ continue
1457
1559
 
1458
- return pymssql.connect
1560
+ # Handle boolean values properly
1561
+ if isinstance(value, bool):
1562
+ conn_str_parts.append(f"{key}={'YES' if value else 'NO'}")
1563
+ else:
1564
+ conn_str_parts.append(f"{key}={value}")
1565
+
1566
+ # Create the connection string
1567
+ conn_str = ";".join(conn_str_parts)
1568
+
1569
+ return pyodbc.connect(conn_str, autocommit=kwargs.get("autocommit", False))
1570
+
1571
+ return connect
1459
1572
 
1460
1573
  @property
1461
1574
  def _extra_engine_config(self) -> t.Dict[str, t.Any]:
@@ -219,6 +219,10 @@ class MSSQLEngineAdapter(
219
219
  assert isinstance(df, pd.DataFrame)
220
220
  temp_table = self._get_temp_table(target_table or "pandas")
221
221
 
222
+ # Return the superclass implementation if the connection pool doesn't support bulk_copy
223
+ if not hasattr(self._connection_pool.get(), "bulk_copy"):
224
+ return super()._df_to_source_queries(df, columns_to_types, batch_size, target_table)
225
+
222
226
  def query_factory() -> Query:
223
227
  # It is possible for the factory to be called multiple times and if so then the temp table will already
224
228
  # be created so we skip creating again. This means we are assuming the first call is the same result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlmesh
3
- Version: 0.191.1.dev1
3
+ Version: 0.191.1.dev2
4
4
  Summary: Next-generation data transformation framework
5
5
  Author-email: "TobikoData Inc." <engineering@tobikodata.com>
6
6
  License: Apache License
@@ -241,6 +241,8 @@ Provides-Extra: athena
241
241
  Requires-Dist: PyAthena[Pandas]; extra == "athena"
242
242
  Provides-Extra: azuresql
243
243
  Requires-Dist: pymssql; extra == "azuresql"
244
+ Provides-Extra: azuresql-odbc
245
+ Requires-Dist: pyodbc; extra == "azuresql-odbc"
244
246
  Provides-Extra: bigquery
245
247
  Requires-Dist: google-cloud-bigquery[pandas]; extra == "bigquery"
246
248
  Requires-Dist: google-cloud-bigquery-storage; extra == "bigquery"
@@ -311,6 +313,8 @@ Requires-Dist: langchain; extra == "llm"
311
313
  Requires-Dist: openai; extra == "llm"
312
314
  Provides-Extra: mssql
313
315
  Requires-Dist: pymssql; extra == "mssql"
316
+ Provides-Extra: mssql-odbc
317
+ Requires-Dist: pyodbc; extra == "mssql-odbc"
314
318
  Provides-Extra: mysql
315
319
  Requires-Dist: pymysql; extra == "mysql"
316
320
  Provides-Extra: mwaa
@@ -1,5 +1,5 @@
1
1
  sqlmesh/__init__.py,sha256=QjwaamJIdW7voXoS880yAx7tHm-Im4_mqGA-XJgafos,7631
2
- sqlmesh/_version.py,sha256=7hh-fhvpsFAPwOfArTlM6CZ9gYvfxD3DI1WDSOp7590,528
2
+ sqlmesh/_version.py,sha256=3VcMADjN_hztomDeuXOjOIf8lrB7o5RCIewqH-yhrjE,528
3
3
  sqlmesh/magics.py,sha256=gjzoxREb1XT8Yho_PQMlMqhhkLq_sEfBM8S3PTgxvWE,40337
4
4
  sqlmesh/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
5
  sqlmesh/cicd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -41,7 +41,7 @@ sqlmesh/core/config/__init__.py,sha256=b4gFb04tpeZpowqJD-7h3U3TLgxrpQhZahVEAo6VW
41
41
  sqlmesh/core/config/base.py,sha256=t8NQmsgQoZSc-k0dlDiCb8t1jj0AMYdGZ-6se9q_Pks,4898
42
42
  sqlmesh/core/config/categorizer.py,sha256=6vzUoNLjR6GOEb_2mYVz2TwmMv2BfldgHX2u-Le5HZs,1975
43
43
  sqlmesh/core/config/common.py,sha256=-o_HoRFtR4u84frg_TLno7z7vS9II6ASiOayb59MKHo,2806
44
- sqlmesh/core/config/connection.py,sha256=HWplZfaZx1VSsBEs6B6EPg1QrdYCgXNot8d5B-AezMw,77697
44
+ sqlmesh/core/config/connection.py,sha256=AV7Bx64YCPQCv6J17bsgt-1SyIOAZSEqHIF8U3Nrfh0,82103
45
45
  sqlmesh/core/config/feature_flag.py,sha256=JRcD31OG_t2cYc24meKbHDGB0iVID0P07V2oOeB_mgA,203
46
46
  sqlmesh/core/config/format.py,sha256=6CXFbvnor56xbldKE-Vrm9k_ABRoY4v6vgIb3mCihiQ,1355
47
47
  sqlmesh/core/config/gateway.py,sha256=tYngyqwd_4Qr9lhcv2hlvLvb_2pgYYtKu6hdGsTr-4I,1931
@@ -66,7 +66,7 @@ sqlmesh/core/engine_adapter/clickhouse.py,sha256=LoPdm_s_s-ZxS1XJPA3Et0iOHeJ1Lgq
66
66
  sqlmesh/core/engine_adapter/databricks.py,sha256=8ZExPIeT3dDh9MTJW8udvmh9LiY-f9HkRAN1Q5Qebhs,14541
67
67
  sqlmesh/core/engine_adapter/duckdb.py,sha256=rFv27sF1KXhJcrIuM9Zdfl2cyZNRXEF9qQczgODdK0M,4614
68
68
  sqlmesh/core/engine_adapter/mixins.py,sha256=HMB0Z9O3p5jaTZMolKiWKqpVDZokf5GoI99YXwEJ8bc,22470
69
- sqlmesh/core/engine_adapter/mssql.py,sha256=nsBM9U6N6CmKCY8gr8PTNeQAMD8UeSM51uM9eCjlP3A,13880
69
+ sqlmesh/core/engine_adapter/mssql.py,sha256=e-PElz8RAUwD-fGCx-2oleg3UGxFH-f0AzXODR3hpm0,14140
70
70
  sqlmesh/core/engine_adapter/mysql.py,sha256=lVC-siXZK3iSfqCmBjZNw8uy09Z8la8v8_oUZ-E2OwI,6966
71
71
  sqlmesh/core/engine_adapter/postgres.py,sha256=fdQrsBodfGlNf8lZYkS99U_gjoDyaYKEVKLgScduv3I,4601
72
72
  sqlmesh/core/engine_adapter/redshift.py,sha256=B4GsPggz-xyvBpx5qubvVwEeKF3vk7n5SMExL-Q5Ysw,16754
@@ -275,7 +275,7 @@ sqlmesh/utils/pydantic.py,sha256=I898jAFqa3qU6lG2uPpRebVFncOUo8ZrLWM_R3K3DHU,116
275
275
  sqlmesh/utils/rich.py,sha256=xNZyPCqEc4TAlJlsEthg-5eYUWpaizz0QjBzO7a1Yv4,1509
276
276
  sqlmesh/utils/windows.py,sha256=r7Yin4WEwbvH7sKA3hOJtRsclmscNuBeCklCfVPvtQs,611
277
277
  sqlmesh/utils/yaml.py,sha256=np-WVcwmwsKghCgAj1OBBMsK1SXSRn4wR-H37cGiFr8,2282
278
- sqlmesh-0.191.1.dev1.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
278
+ sqlmesh-0.191.1.dev2.dist-info/licenses/LICENSE,sha256=OlMefUjgWJdULtf84BLW0AZZcY8DwdgQqb_1j2862j8,11346
279
279
  web/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
280
280
  web/client/dist/index.html,sha256=k5bAEoPDJ0BhYQ4hBcKWObITCxIgqOUC28nGXvEDrt0,5472
281
281
  web/client/dist/assets/Audits-Pgs8N6IE.js,sha256=BQuc9kiznZSqTnzNrJSDNqAAdOTGjNQJt32M1sHZFok,1041
@@ -393,8 +393,8 @@ web/server/api/endpoints/models.py,sha256=ud-lRwriyI_OYg9tZchrBgeiXELiRTLNEodraf
393
393
  web/server/api/endpoints/modules.py,sha256=8hqqgonGay_mJmpCw0IdbjsPhWlQH2VLdKAqha-myac,468
394
394
  web/server/api/endpoints/plan.py,sha256=OYuQ_4dgbhG9ibTcX1a8mvYgH3ANxw9Fhbb4wg6QWqc,9222
395
395
  web/server/api/endpoints/table_diff.py,sha256=SCy3-pr5x6bEEiOaZGCDbiHQhpgxwmnz3Acqa2Ie6jI,2090
396
- sqlmesh-0.191.1.dev1.dist-info/METADATA,sha256=Yc9GVN9IIzZxnLpB6NEFetXNdSHxdfCwK5dzFeQtJV8,26540
397
- sqlmesh-0.191.1.dev1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
398
- sqlmesh-0.191.1.dev1.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
399
- sqlmesh-0.191.1.dev1.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
400
- sqlmesh-0.191.1.dev1.dist-info/RECORD,,
396
+ sqlmesh-0.191.1.dev2.dist-info/METADATA,sha256=4VXVPffRoDSY07wCfBSu04NbrRdUV7FELjwuCNKSufQ,26690
397
+ sqlmesh-0.191.1.dev2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
398
+ sqlmesh-0.191.1.dev2.dist-info/entry_points.txt,sha256=pIYKtg-u-uO7BK_Y0qrdmfyj0bYrcMc3Xiqr5WE3VFk,121
399
+ sqlmesh-0.191.1.dev2.dist-info/top_level.txt,sha256=bspjo5vjELQkuN0qKuTFIbsrUt3Zeo8D2mlKeHG1LdE,12
400
+ sqlmesh-0.191.1.dev2.dist-info/RECORD,,