soda-sqlserver 4.7.0__tar.gz → 4.15.0__tar.gz

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.
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soda-sqlserver
3
- Version: 4.7.0
3
+ Version: 4.15.0
4
4
  Summary: Soda SQL Server V4
5
5
  Author-email: "Soda Data N.V." <info@soda.io>
6
6
  License: Proprietary
7
7
  Requires-Python: >=3.10
8
- Requires-Dist: soda-core==4.7.0
8
+ Requires-Dist: soda-core==4.15.0
9
9
  Requires-Dist: pyodbc
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "soda-sqlserver"
3
- version = "4.7.0"
3
+ version = "4.15.0"
4
4
  description = "Soda SQL Server V4"
5
5
  requires-python = ">=3.10"
6
6
  license = {text = "Proprietary"}
@@ -8,7 +8,7 @@ authors = [
8
8
  {name = "Soda Data N.V.", email = "info@soda.io"}
9
9
  ]
10
10
  dependencies = [
11
- "soda-core==4.7.0",
11
+ "soda-core==4.15.0",
12
12
  "pyodbc",
13
13
  ]
14
14
 
@@ -14,6 +14,7 @@ from soda_core.common.sql_ast import (
14
14
  COUNT,
15
15
  CREATE_TABLE,
16
16
  CREATE_TABLE_AS_SELECT,
17
+ CREATE_TABLE_COLUMN,
17
18
  CREATE_TABLE_IF_NOT_EXISTS,
18
19
  CREATE_VIEW,
19
20
  DISTINCT,
@@ -162,6 +163,11 @@ class SqlServerSqlDialect(SqlDialect, sqlglot_dialect="tsql"):
162
163
  def _build_length_sql(self, length: LENGTH) -> str:
163
164
  return f"LEN({self.build_expression_sql(length.expression)})"
164
165
 
166
+ def _build_count_sql(self, count: COUNT) -> str:
167
+ # T-SQL COUNT returns INT and overflows above 2,147,483,647 rows. COUNT_BIG is the
168
+ # BIGINT-returning equivalent with identical null/distinct semantics.
169
+ return f"COUNT_BIG({self.build_expression_sql(count.expression)})"
170
+
165
171
  def sql_expr_timestamp_literal(self, datetime_in_iso8601: str) -> str:
166
172
  return f"'{datetime_in_iso8601}'"
167
173
 
@@ -324,6 +330,22 @@ class SqlServerSqlDialect(SqlDialect, sqlglot_dialect="tsql"):
324
330
  "datetimeoffset",
325
331
  ]
326
332
 
333
+ # SQL Server's datetime2 / datetimeoffset / time accept a fractional-seconds
334
+ # precision of 0..7. Any higher value is rejected at CREATE TABLE time.
335
+ _MAX_DATETIME_PRECISION = 7
336
+
337
+ def _build_create_table_column_type(self, create_table_column: CREATE_TABLE_COLUMN) -> str:
338
+ # Clamp datetime precision to SQL Server's max. Cross-source flows from sources
339
+ # with higher native precision (e.g. Snowflake's TIMESTAMP_NTZ defaults to 9)
340
+ # would otherwise produce e.g. `datetime2(9)` and fail CREATE TABLE.
341
+ if create_table_column.type.name.lower() in ("datetime2", "datetimeoffset", "time"):
342
+ if (
343
+ create_table_column.type.datetime_precision is not None
344
+ and create_table_column.type.datetime_precision > self._MAX_DATETIME_PRECISION
345
+ ):
346
+ create_table_column.type.datetime_precision = self._MAX_DATETIME_PRECISION
347
+ return super()._build_create_table_column_type(create_table_column)
348
+
327
349
  def default_varchar_length(self) -> Optional[int]:
328
350
  return 255
329
351
 
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import logging
4
4
  import struct
5
5
  from abc import ABC
6
- from datetime import datetime, timedelta, timezone
6
+ from datetime import datetime, timedelta, timezone, tzinfo
7
7
  from typing import Any, Literal, Optional, Union
8
8
 
9
9
  import pyodbc
@@ -196,5 +196,28 @@ class SqlServerDataSourceConnection(DataSourceConnection):
196
196
  def _execute_query_get_result_row_column_name(self, column) -> str:
197
197
  return column[0]
198
198
 
199
+ def _fetch_session_timezone(self) -> tzinfo:
200
+ # Use SYSDATETIMEOFFSET() instead of CURRENT_TIMEZONE_ID() so the same query works
201
+ # across the whole SQL Server family: SQL Server proper, Microsoft Fabric DW, Azure
202
+ # Synapse Dedicated/Serverless. CURRENT_TIMEZONE_ID is unsupported in Fabric and
203
+ # Synapse Dedicated; SYSDATETIMEOFFSET is universally supported.
204
+ # The connection registers ``handle_datetimeoffset`` as a pyodbc output converter for
205
+ # SQL_TYPE -155, so the returned value is a tz-aware ``datetime`` whose tzinfo is the
206
+ # exact offset reported by the server.
207
+ with self.connection.cursor() as cursor:
208
+ cursor.execute("SELECT SYSDATETIMEOFFSET()")
209
+ row = cursor.fetchone()
210
+ if not row or not isinstance(row[0], datetime) or row[0].tzinfo is None:
211
+ return timezone.utc
212
+ offset = row[0].tzinfo.utcoffset(row[0])
213
+ # Normalize zero offset to ``timezone.utc`` (the singleton) so that adapters
214
+ # routed through ``parse_session_timezone`` and SQL Server agree on the UTC
215
+ # representation — adapters that report ``UTC`` / ``Etc/UTC`` resolve to the
216
+ # same ``timezone.utc`` instance, while a raw ``timezone(timedelta(0))`` would
217
+ # be ``==`` but not ``is`` equivalent.
218
+ if offset == timedelta(0):
219
+ return timezone.utc
220
+ return timezone(offset)
221
+
199
222
  def _get_autocommit_setting(self) -> bool:
200
223
  return False # No need to set autocommit, as it is set to False by default.
@@ -15,6 +15,14 @@ class SqlServerDataSourceTestHelper(DataSourceTestHelper):
15
15
  def _create_database_name(self) -> Optional[str]:
16
16
  return os.getenv("SQLSERVER_DATABASE", "master")
17
17
 
18
+ def _string_length_sql_function(self) -> str:
19
+ # SqlServer's LEN() trims trailing spaces, under-reporting any
20
+ # payload that ends with spaces. DATALENGTH() returns the raw
21
+ # byte count, which equals char count for varchar columns and
22
+ # is the right answer for the unbounded-column data-length
23
+ # verification.
24
+ return "datalength"
25
+
18
26
  def _create_data_source_yaml_str(self) -> str:
19
27
  """
20
28
  Called in _create_data_source_impl to initialized self.data_source_impl
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soda-sqlserver
3
- Version: 4.7.0
3
+ Version: 4.15.0
4
4
  Summary: Soda SQL Server V4
5
5
  Author-email: "Soda Data N.V." <info@soda.io>
6
6
  License: Proprietary
7
7
  Requires-Python: >=3.10
8
- Requires-Dist: soda-core==4.7.0
8
+ Requires-Dist: soda-core==4.15.0
9
9
  Requires-Dist: pyodbc
@@ -0,0 +1,2 @@
1
+ soda-core==4.15.0
2
+ pyodbc
@@ -1,2 +0,0 @@
1
- soda-core==4.7.0
2
- pyodbc