dbt-sqlserver 1.11.0rc1__tar.gz → 1.12.0rc2__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.
Files changed (82) hide show
  1. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/PKG-INFO +34 -10
  2. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/README.md +25 -3
  3. dbt_sqlserver-1.12.0rc2/dbt/adapters/sqlserver/__version__.py +1 -0
  4. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_adapter.py +120 -20
  5. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_auth.py +8 -0
  6. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_backend.py +95 -1
  7. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_connections.py +322 -6
  8. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_constants.py +5 -1
  9. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_credentials.py +2 -0
  10. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_helpers.py +36 -0
  11. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_relation.py +9 -0
  12. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_runtime.py +78 -0
  13. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/apply_grants.sql +3 -3
  14. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/apply_masks.sql +3 -3
  15. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/columns.sql +1 -1
  16. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/indexes.sql +124 -58
  17. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/metadata.sql +3 -3
  18. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/relation.sql +12 -5
  19. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/schema.sql +8 -3
  20. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/hooks.sql +5 -1
  21. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/incremental/incremental.sql +47 -6
  22. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/table/table.sql +5 -3
  23. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/unit_test/unit_test_create_table_as.sql +1 -1
  24. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/tests.sql +4 -3
  25. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/unit_tests.sql +6 -4
  26. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/relations/table/create.sql +84 -26
  27. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/relations/views/create.sql +9 -1
  28. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt_sqlserver.egg-info/PKG-INFO +34 -10
  29. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt_sqlserver.egg-info/requires.txt +6 -5
  30. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/pyproject.toml +17 -7
  31. dbt_sqlserver-1.11.0rc1/dbt/adapters/sqlserver/__version__.py +0 -1
  32. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/LICENSE +0 -0
  33. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/__init__.py +0 -0
  34. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/__init__.py +0 -0
  35. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/py.typed +0 -0
  36. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/relation_configs/__init__.py +0 -0
  37. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/relation_configs/index.py +0 -0
  38. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/relation_configs/policies.py +0 -0
  39. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_column.py +0 -0
  40. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_configs.py +0 -0
  41. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/adapters/sqlserver/sqlserver_mask.py +0 -0
  42. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/__init__.py +0 -0
  43. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/dbt_project.yml +0 -0
  44. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/catalog.sql +0 -0
  45. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/persist_docs.sql +0 -0
  46. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/show.sql +0 -0
  47. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/adapters/validate_sql.sql +0 -0
  48. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/functions/helpers.sql +0 -0
  49. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/functions/scalar.sql +0 -0
  50. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/incremental/incremental_strategies.sql +0 -0
  51. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/incremental/merge.sql +0 -0
  52. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/table/columns_spec_ddl.sql +0 -0
  53. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/table/table_dml_refresh.sql +0 -0
  54. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/unit_test/get_fixture_sql.sql +0 -0
  55. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/view/create_view_as.sql +0 -0
  56. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/models/view/view.sql +0 -0
  57. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/snapshots/helpers.sql +0 -0
  58. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/snapshots/snapshot.sql +0 -0
  59. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/snapshots/snapshot_merge.sql +0 -0
  60. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/materializations/snapshots/strategies.sql +0 -0
  61. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/relations/seeds/helpers.sql +0 -0
  62. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/relations/table/clone.sql +0 -0
  63. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/any_value.sql +0 -0
  64. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/array_construct.sql +0 -0
  65. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/cast_bool_to_text.sql +0 -0
  66. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/concat.sql +0 -0
  67. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/date_trunc.sql +0 -0
  68. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/dateadd.sql +0 -0
  69. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/get_tables_by_pattern.sql +0 -0
  70. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/hash.sql +0 -0
  71. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/last_day.sql +0 -0
  72. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/length.sql +0 -0
  73. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/listagg.sql +0 -0
  74. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/position.sql +0 -0
  75. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/safe_cast.sql +0 -0
  76. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/split_part.sql +0 -0
  77. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/macros/utils/timestamps.sql +0 -0
  78. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt/include/sqlserver/profile_template.yml +0 -0
  79. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt_sqlserver.egg-info/SOURCES.txt +0 -0
  80. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt_sqlserver.egg-info/dependency_links.txt +0 -0
  81. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/dbt_sqlserver.egg-info/top_level.txt +0 -0
  82. {dbt_sqlserver-1.11.0rc1 → dbt_sqlserver-1.12.0rc2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbt-sqlserver
3
- Version: 1.11.0rc1
3
+ Version: 1.12.0rc2
4
4
  Summary: A Microsoft SQL Server adapter plugin for dbt
5
5
  Author: Mikael Ene, Anders Swanson, Sam Debruyn, Cor Zuurmond, Cody Scott
6
6
  License: MIT
@@ -14,32 +14,45 @@ Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Operating System :: Microsoft :: Windows
15
15
  Classifier: Operating System :: MacOS :: MacOS X
16
16
  Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3.10
17
18
  Classifier: Programming Language :: Python :: 3.11
18
19
  Classifier: Programming Language :: Python :: 3.12
19
20
  Classifier: Programming Language :: Python :: 3.13
20
- Requires-Python: >=3.11
21
+ Classifier: Programming Language :: Python :: 3.14
22
+ Requires-Python: >=3.10
21
23
  Description-Content-Type: text/markdown
22
24
  License-File: LICENSE
23
- Requires-Dist: dbt-core<2.0,>=1.11.0
25
+ Requires-Dist: dbt-core<2.0,>=1.12.0
24
26
  Requires-Dist: dbt-common<2.0,>=1.22.0
25
- Requires-Dist: dbt-adapters<2.0,>=1.24.1
26
- Requires-Dist: pyodbc>=5.2.0
27
+ Requires-Dist: dbt-adapters<2.0,>=1.24.5
27
28
  Provides-Extra: azure
28
29
  Requires-Dist: azure-identity>=1.12.0; extra == "azure"
29
30
  Provides-Extra: pyodbc
30
31
  Requires-Dist: pyodbc>=5.2.0; extra == "pyodbc"
31
32
  Provides-Extra: mssql
32
33
  Requires-Dist: mssql-python>=1.7.1; extra == "mssql"
33
- Requires-Dist: azure-identity>=1.12.0; extra == "mssql"
34
- Requires-Dist: azure-core>=1.0.0; extra == "mssql"
34
+ Provides-Extra: adbc
35
+ Requires-Dist: adbc-driver-manager>=1.0.0; extra == "adbc"
36
+ Requires-Dist: pyarrow>=14.0.0; extra == "adbc"
35
37
  Dynamic: license-file
36
38
 
37
39
  # dbt-sqlserver
38
40
 
39
41
  [dbt](https://www.getdbt.com) adapter for Microsoft SQL Server and Azure SQL services.
40
42
 
41
- The adapter supports dbt-core 1.10 or newer and follows the same versioning scheme.
42
- E.g. version 1.10.x of the adapter is compatible with dbt-core 1.10.x.
43
+ The adapter supports dbt-core 1.11 or newer and follows the same versioning scheme.
44
+ E.g. version 1.11.x of the adapter is compatible with dbt-core 1.11.x.
45
+
46
+ ## Supported Python versions
47
+
48
+ The adapter is tested against:
49
+
50
+ | Python version | Status |
51
+ |---|---|
52
+ | 3.10 | Installable (not tested in CI) |
53
+ | 3.11 | Officially supported |
54
+ | 3.12 | Officially supported |
55
+ | 3.13 | Officially supported |
43
56
 
44
57
  ## Supported SQL Server versions
45
58
 
@@ -79,6 +92,7 @@ Latest pre-release: ![GitHub tag (latest SemVer pre-release)](https://img.shield
79
92
  |---|---|---|
80
93
  | `pyodbc` | `dbt-sqlserver[pyodbc]` or `pyodbc` | `unixodbc-dev` plus the Microsoft ODBC Driver for SQL Server |
81
94
  | `mssql-python` | `dbt-sqlserver[mssql]` or `mssql-python` | `libltdl7`, `libkrb5-3`, `libgssapi-krb5-2` |
95
+ | `adbc` *(experimental)* | `dbt-sqlserver[adbc]` | none (driver binary installed separately via the `dbc` CLI) |
82
96
 
83
97
 
84
98
  ### `pyodbc` backend
@@ -143,6 +157,16 @@ your_profile:
143
157
  backend: mssql-python # <-- enables this backend
144
158
  ```
145
159
 
160
+ ### `adbc` backend *(experimental)*
161
+
162
+ An Arrow-native backend built on [ADBC](https://arrow.apache.org/adbc/), avoiding the row-based ODBC/DB-API bridge entirely. SQL Server authentication only (no Azure AD / Windows auth yet).
163
+
164
+ ```shell
165
+ pip install -U "dbt-sqlserver[adbc]"
166
+ ```
167
+
168
+ The driver binary is not on PyPI and must be installed once via the `dbc` CLI. See [docs/adbc_backend.md](docs/adbc_backend.md) for the full setup, configuration, and known-differences guide.
169
+
146
170
  ## Changelog
147
171
 
148
172
  See [the changelog](CHANGELOG.md)
@@ -176,7 +200,7 @@ The same setting is also honoured via `vars:` for backwards compatibility; the b
176
200
 
177
201
  ### `backend`
178
202
 
179
- *(default: `pyodbc`)* Set to `mssql-python` in a profile target to use the `mssql-python` backend instead of `pyodbc`. The adapter fails if the required backend package (Python dependency), such as `pyodbc` or `mssql-python`, is not installed.
203
+ *(default: `pyodbc`)* Set to `mssql-python` or `adbc` (experimental, see [docs/adbc_backend.md](docs/adbc_backend.md)) in a profile target to use that backend instead of `pyodbc`. The adapter fails if the required backend package (Python dependency), such as `pyodbc`, `mssql-python`, or `adbc-driver-manager`, is not installed.
180
204
 
181
205
  ### `dbt_sqlserver_enable_safe_type_expansion`
182
206
 
@@ -2,8 +2,19 @@
2
2
 
3
3
  [dbt](https://www.getdbt.com) adapter for Microsoft SQL Server and Azure SQL services.
4
4
 
5
- The adapter supports dbt-core 1.10 or newer and follows the same versioning scheme.
6
- E.g. version 1.10.x of the adapter is compatible with dbt-core 1.10.x.
5
+ The adapter supports dbt-core 1.11 or newer and follows the same versioning scheme.
6
+ E.g. version 1.11.x of the adapter is compatible with dbt-core 1.11.x.
7
+
8
+ ## Supported Python versions
9
+
10
+ The adapter is tested against:
11
+
12
+ | Python version | Status |
13
+ |---|---|
14
+ | 3.10 | Installable (not tested in CI) |
15
+ | 3.11 | Officially supported |
16
+ | 3.12 | Officially supported |
17
+ | 3.13 | Officially supported |
7
18
 
8
19
  ## Supported SQL Server versions
9
20
 
@@ -43,6 +54,7 @@ Latest pre-release: ![GitHub tag (latest SemVer pre-release)](https://img.shield
43
54
  |---|---|---|
44
55
  | `pyodbc` | `dbt-sqlserver[pyodbc]` or `pyodbc` | `unixodbc-dev` plus the Microsoft ODBC Driver for SQL Server |
45
56
  | `mssql-python` | `dbt-sqlserver[mssql]` or `mssql-python` | `libltdl7`, `libkrb5-3`, `libgssapi-krb5-2` |
57
+ | `adbc` *(experimental)* | `dbt-sqlserver[adbc]` | none (driver binary installed separately via the `dbc` CLI) |
46
58
 
47
59
 
48
60
  ### `pyodbc` backend
@@ -107,6 +119,16 @@ your_profile:
107
119
  backend: mssql-python # <-- enables this backend
108
120
  ```
109
121
 
122
+ ### `adbc` backend *(experimental)*
123
+
124
+ An Arrow-native backend built on [ADBC](https://arrow.apache.org/adbc/), avoiding the row-based ODBC/DB-API bridge entirely. SQL Server authentication only (no Azure AD / Windows auth yet).
125
+
126
+ ```shell
127
+ pip install -U "dbt-sqlserver[adbc]"
128
+ ```
129
+
130
+ The driver binary is not on PyPI and must be installed once via the `dbc` CLI. See [docs/adbc_backend.md](docs/adbc_backend.md) for the full setup, configuration, and known-differences guide.
131
+
110
132
  ## Changelog
111
133
 
112
134
  See [the changelog](CHANGELOG.md)
@@ -140,7 +162,7 @@ The same setting is also honoured via `vars:` for backwards compatibility; the b
140
162
 
141
163
  ### `backend`
142
164
 
143
- *(default: `pyodbc`)* Set to `mssql-python` in a profile target to use the `mssql-python` backend instead of `pyodbc`. The adapter fails if the required backend package (Python dependency), such as `pyodbc` or `mssql-python`, is not installed.
165
+ *(default: `pyodbc`)* Set to `mssql-python` or `adbc` (experimental, see [docs/adbc_backend.md](docs/adbc_backend.md)) in a profile target to use that backend instead of `pyodbc`. The adapter fails if the required backend package (Python dependency), such as `pyodbc`, `mssql-python`, or `adbc-driver-manager`, is not installed.
144
166
 
145
167
  ### `dbt_sqlserver_enable_safe_type_expansion`
146
168
 
@@ -0,0 +1 @@
1
+ version = "1.12.0rc2"
@@ -1,4 +1,5 @@
1
- from typing import Any, Dict, List, Optional
1
+ import datetime as _dt
2
+ from typing import Any, Dict, List, Optional, Tuple, Union
2
3
 
3
4
  import agate
4
5
  import dbt_common.exceptions
@@ -37,6 +38,44 @@ from dbt.adapters.sqlserver.sqlserver_relation import SQLServerRelation
37
38
  logger = AdapterLogger("SQLServer")
38
39
 
39
40
 
41
+ def _normalize_result_datetimes(
42
+ result: Union[Tuple, List[Tuple], None],
43
+ ) -> Union[Tuple, List[Tuple], None]:
44
+ """Strip spurious ``tzinfo=UTC`` that ADBC attaches to SQL Server
45
+ DATETIME2 / DATETIME values.
46
+
47
+ SQL Server does **not** store timezone offsets for these types, so the
48
+ correct Python representation is a naive ``datetime``. ADBC's Arrow
49
+ backend wraps every ``timestamp`` column as ``timestamp[us, tz=UTC]``
50
+ regardless of the source semantics, producing ``datetime(…, tzinfo=UTC)``.
51
+ We revert that to match SQL Server semantics and to stay compatible with
52
+ the existing pyodbc / mssql-python backends.
53
+ """
54
+ if result is None:
55
+ return None
56
+
57
+ if isinstance(result, tuple):
58
+ return tuple(
59
+ (v.replace(tzinfo=None) if isinstance(v, _dt.datetime) and v.tzinfo is not None else v)
60
+ for v in result
61
+ )
62
+
63
+ if isinstance(result, list):
64
+ return [
65
+ tuple(
66
+ (
67
+ v.replace(tzinfo=None)
68
+ if isinstance(v, _dt.datetime) and v.tzinfo is not None
69
+ else v
70
+ )
71
+ for v in row
72
+ )
73
+ for row in result
74
+ ]
75
+
76
+ return result
77
+
78
+
40
79
  class SQLServerAdapter(SQLAdapter):
41
80
  """
42
81
  Controls actual implementation of adapter, and ability to override certain methods.
@@ -77,14 +116,6 @@ class SQLServerAdapter(SQLAdapter):
77
116
  @property
78
117
  def _behavior_flags(self) -> List[BehaviorFlag]:
79
118
  return [
80
- {
81
- "name": "empty",
82
- "default": False,
83
- "description": (
84
- "When enabled, table and view materializations will be created as empty "
85
- "structures (no data)."
86
- ),
87
- },
88
119
  {
89
120
  "name": "dbt_sqlserver_use_default_schema_concat",
90
121
  "default": False,
@@ -108,13 +139,14 @@ class SQLServerAdapter(SQLAdapter):
108
139
  },
109
140
  {
110
141
  "name": "dbt_sqlserver_use_native_string_types",
111
- "default": False,
142
+ "default": True,
112
143
  "description": (
113
- "When True, uses SQL Server-native string type mappings: "
144
+ "When True (default), uses SQL Server-native string type mappings: "
114
145
  "STRING -> VARCHAR(MAX), NCHAR -> NCHAR(1), NVARCHAR -> NVARCHAR(4000). "
115
- "When False (default), preserves legacy mappings: "
146
+ "When False, preserves deprecated legacy mappings: "
116
147
  "STRING and NVARCHAR -> VARCHAR(8000), NCHAR -> CHAR(1). "
117
- "The new behaviour is intended to become the default in a future release."
148
+ "The legacy False behavior is deprecated "
149
+ "and will be removed in a future release."
118
150
  ),
119
151
  },
120
152
  {
@@ -129,14 +161,15 @@ class SQLServerAdapter(SQLAdapter):
129
161
  },
130
162
  {
131
163
  "name": "dbt_sqlserver_use_dbt_transactions",
132
- "default": False,
164
+ "default": True,
133
165
  "description": (
134
- "When True, dbt transaction hooks (begin/commit) emit real T-SQL "
166
+ "When True (default), dbt transaction hooks (begin/commit) emit real T-SQL "
135
167
  "BEGIN TRANSACTION / COMMIT TRANSACTION statements. "
136
- "When False (default and legacy), begin/commit are no-ops and each statement "
137
- "is auto-committed by the driver. This means earlier successful statements "
168
+ "When False, begin/commit are no-ops and each statement "
169
+ "is auto-committed by the driver, meaning earlier successful statements "
138
170
  "are not rolled back if a later statement fails. "
139
- "This behavior is intended to become the default in a future release."
171
+ "The legacy False behavior is deprecated "
172
+ "and will be removed in a future release."
140
173
  ),
141
174
  },
142
175
  ]
@@ -155,6 +188,22 @@ class SQLServerAdapter(SQLAdapter):
155
188
  ]
156
189
  return columns
157
190
 
191
+ @classmethod
192
+ def quote(cls, identifier: str) -> str:
193
+ """Double-quote an identifier, doubling any embedded double quote.
194
+
195
+ ``SQLAdapter.quote`` interpolates the identifier verbatim, so a name
196
+ containing a ``"`` would close the quoted identifier early and the
197
+ remainder would parse as SQL. T-SQL escapes a delimiter by doubling
198
+ it -- the same rule ``QUOTENAME()`` applies to brackets -- so
199
+ ``ab"cd`` must render as ``"ab""cd"``.
200
+
201
+ This is the quoting used by every macro that formats an identifier
202
+ (see #785). Relation rendering escapes nothing, since it goes through
203
+ ``BaseRelation.quote_character`` upstream rather than this method.
204
+ """
205
+ return '"{}"'.format(str(identifier).replace('"', '""'))
206
+
158
207
  @classmethod
159
208
  def convert_boolean_type(cls, agate_table, col_idx):
160
209
  return "bit"
@@ -273,9 +322,9 @@ class SQLServerAdapter(SQLAdapter):
273
322
  if not fetch:
274
323
  conn.handle.commit()
275
324
  if fetch == "one":
276
- return cursor.fetchone()
325
+ return _normalize_result_datetimes(cursor.fetchone())
277
326
  elif fetch == "all":
278
- return cursor.fetchall()
327
+ return _normalize_result_datetimes(cursor.fetchall())
279
328
  else:
280
329
  return
281
330
  except BaseException:
@@ -427,6 +476,57 @@ class SQLServerAdapter(SQLAdapter):
427
476
  def parse_index(self, raw_index: Any) -> Optional[SQLServerIndexConfig]:
428
477
  return SQLServerIndexConfig.parse(raw_index)
429
478
 
479
+ @available
480
+ def index_needs_own_batch(self, raw_index: Any) -> bool:
481
+ """True when raw_index's build_options (ONLINE / RESUMABLE) force its
482
+ CREATE INDEX to run outside any transaction: SQL Server rejects
483
+ RESUMABLE inside a user transaction (error 574), and an ONLINE build
484
+ wrapped in one holds its locks until commit, negating the point."""
485
+ parsed = self.parse_index(raw_index)
486
+ if not parsed:
487
+ return False
488
+ return create_needs_own_batch(parsed.build_options)
489
+
490
+ @available
491
+ def commit_if_open(self) -> None:
492
+ """Commit the current transaction if one is open - a no-op otherwise.
493
+
494
+ dbt_sqlserver_use_dbt_transactions on (default) wraps a
495
+ materialization's whole build, from its first statement through its
496
+ own trailing ``adapter.commit()``, in one continuous ambient
497
+ transaction. Some statements must not share that transaction with
498
+ whatever runs after them: an ONLINE/RESUMABLE index build (SQL Server
499
+ rejects RESUMABLE inside a user transaction outright, and ONLINE
500
+ holds its locks until commit either way), or a full-refresh-in-
501
+ progress marker, which exists specifically to survive a later
502
+ failure and so must not roll back with it. This makes the prior
503
+ statement durable on its own; pair with begin_if_closed once the
504
+ statement(s) that must run outside a transaction are done, to leave
505
+ later code (more such statements, or the materialization's own
506
+ trailing ``adapter.commit()``, which raises if it finds nothing open)
507
+ working as if this call had never happened.
508
+
509
+ A no-op when no transaction is open: the caller's statement already
510
+ ran autocommitted on its own (e.g. via ``run_query``'s
511
+ ``auto_begin=false``, before anything else began one), so there is
512
+ nothing to flush. Also a no-op, at the SQL level, whenever
513
+ dbt_sqlserver_use_dbt_transactions is off: begin/commit still flip
514
+ dbt-core's bookkeeping (see
515
+ SQLServerConnectionManager.add_begin_query/add_commit_query), but
516
+ emit no real T-SQL, matching the driver's own autocommit.
517
+ """
518
+ connection = self.connections.get_thread_connection()
519
+ if connection is not None and connection.transaction_open:
520
+ self.connections.commit()
521
+
522
+ @available
523
+ def begin_if_closed(self) -> None:
524
+ """Begin a transaction if none is open - a no-op otherwise. See
525
+ commit_if_open, which this pairs with."""
526
+ connection = self.connections.get_thread_connection()
527
+ if connection is not None and not connection.transaction_open:
528
+ self.connections.begin()
529
+
430
530
  @available
431
531
  def validate_indexes(
432
532
  self, raw_indexes: Any, as_columnstore: Any = False, drop_unmanaged: Any = False
@@ -43,6 +43,14 @@ def is_mssql_python_backend(backend: "SQLServerBackend") -> bool:
43
43
  return backend.value == SQLSERVER_BACKEND_MSSQL_PYTHON
44
44
 
45
45
 
46
+ def is_adbc_backend(backend: "SQLServerBackend") -> bool:
47
+ """Return whether the coerced backend enum targets ``adbc``."""
48
+
49
+ from dbt.adapters.sqlserver.sqlserver_credentials import SQLServerBackend
50
+
51
+ return backend == SQLServerBackend.adbc
52
+
53
+
46
54
  def normalize_authentication_key(value: Optional[str]) -> str:
47
55
  """Normalize a SQL Server auth or lookup key for cross-layer comparisons."""
48
56
 
@@ -8,6 +8,7 @@ lives in ``sqlserver_runtime.py`` and is orchestrated by
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ import urllib.parse
11
12
  from contextlib import suppress
12
13
  from typing import Any, Callable, Tuple
13
14
 
@@ -42,6 +43,7 @@ from dbt.adapters.sqlserver.sqlserver_runtime import (
42
43
  _RUNTIME_STATE,
43
44
  MssqlPythonModuleProtocol,
44
45
  PyodbcModuleProtocol,
46
+ _get_adbc,
45
47
  )
46
48
 
47
49
  logger = AdapterLogger("sqlserver")
@@ -259,12 +261,19 @@ def log_connection_string(connection_string: str) -> None:
259
261
 
260
262
 
261
263
  def is_pyodbc_handle(handle: Any) -> bool:
262
- """Detect a pyodbc handle without importing pyodbc from the caller."""
264
+ """Detect a pyodbc handle without importing pyodbc from the caller.
265
+
266
+ ADBC handles (from ``adbc_driver_manager``) must always return False,
267
+ even when the handle is a mock in test code.
268
+ """
263
269
 
264
270
  handle_type = type(handle)
265
271
  module_name = getattr(handle_type, "__module__", "") or ""
266
272
  class_name = getattr(handle_type, "__name__", "") or ""
267
273
 
274
+ if "adbc" in module_name.lower() or "adbc" in class_name.lower():
275
+ return False
276
+
268
277
  if "pyodbc" in module_name or "pyodbc" in class_name:
269
278
  return True
270
279
 
@@ -340,3 +349,88 @@ def _connect_pyodbc(
340
349
  timeout=credentials.login_timeout,
341
350
  )
342
351
  return _finalize_connection_handle(handle, credentials)
352
+
353
+
354
+ def _sanitize_adbc_uri_for_logging(uri: str) -> str:
355
+ """Redact the password component of an ADBC URI for safe logging."""
356
+ parsed = urllib.parse.urlparse(uri)
357
+ if parsed.password:
358
+ safe_netloc = parsed.netloc.replace(
359
+ f":{parsed.password}@",
360
+ ":***@",
361
+ )
362
+ sanitized = parsed._replace(netloc=safe_netloc).geturl()
363
+ return sanitized
364
+ return uri
365
+
366
+
367
+ def build_adbc_connection_uri(credentials: SQLServerCredentials) -> str:
368
+ """Build a go-mssqldb URI for the ADBC backend.
369
+
370
+ Constructs URIs in the format:
371
+ ``sqlserver://[user:password@]host[:port]?query_params``
372
+
373
+ - User and password are URL-encoded so special characters (``@``, ``:``,
374
+ ``/``, etc.) are escaped per RFC 3986.
375
+ - Named instances (host containing ``\\``) omit the port.
376
+ - Query parameters: ``database``, ``encrypt``, ``TrustServerCertificate``,
377
+ and optional ``connection timeout``.
378
+ """
379
+
380
+ host = (credentials.host or "").strip()
381
+ port = credentials.port or 1433
382
+ database = credentials.database or ""
383
+ uid = credentials.UID or ""
384
+ pwd = credentials.PWD or ""
385
+
386
+ userinfo = f"{urllib.parse.quote(uid, safe='')}:{urllib.parse.quote(pwd, safe='')}"
387
+
388
+ if "\\" in host:
389
+ authority = f"{userinfo}@{host}"
390
+ else:
391
+ authority = f"{userinfo}@{host}:{port}"
392
+
393
+ query_parts = [
394
+ f"database={urllib.parse.quote(database, safe='')}",
395
+ f"encrypt={str(credentials.encrypt).lower()}",
396
+ f"TrustServerCertificate={str(credentials.trust_cert).lower()}",
397
+ ]
398
+
399
+ if credentials.login_timeout and credentials.login_timeout > 0:
400
+ query_parts.append(f"connection timeout={credentials.login_timeout}")
401
+
402
+ query_string = "&".join(query_parts)
403
+ return f"sqlserver://{authority}?{query_string}"
404
+
405
+
406
+ def _connect_adbc(credentials: SQLServerCredentials, uri: str) -> Any:
407
+ """Open an ADBC connection using the constructed go-mssqldb URI.
408
+
409
+ The ADBC mssql driver (``"mssql"``) is the only supported driver.
410
+ Connections are opened with ``autocommit=True`` so that DDL statements
411
+ (``CREATE SCHEMA``, ``CREATE TABLE``, …) auto-commit without extra
412
+ round-trips. When ``dbt_sqlserver_use_dbt_transactions: true``, dbt
413
+ emits explicit ``BEGIN TRANSACTION`` / ``COMMIT TRANSACTION`` SQL
414
+ that correctly nests on top of the autocommit session -- exactly the
415
+ same strategy used by the pyodbc backend.
416
+ """
417
+
418
+ sanitized = _sanitize_adbc_uri_for_logging(uri)
419
+ logger.debug(f"Connecting to SQL Server with ADBC: {sanitized}")
420
+
421
+ adbc_module = _get_adbc()
422
+ return adbc_module.connect(
423
+ driver="mssql",
424
+ db_kwargs={"uri": uri},
425
+ autocommit=True,
426
+ )
427
+
428
+
429
+ def get_adbc_retryable_exceptions() -> Tuple[type[Exception], ...]:
430
+ """Return the ADBC exception types that the connection manager may retry."""
431
+
432
+ adbc_module = _get_adbc()
433
+ return (
434
+ adbc_module.InternalError,
435
+ adbc_module.OperationalError,
436
+ )