dbt-sqlserver 1.12.0rc4__tar.gz → 1.12.2__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 (87) hide show
  1. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/PKG-INFO +21 -14
  2. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/README.md +20 -13
  3. dbt_sqlserver-1.12.2/dbt/adapters/sqlserver/__version__.py +1 -0
  4. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_adapter.py +90 -8
  5. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_configs.py +2 -1
  6. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/columns.sql +31 -4
  7. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/metadata.sql +13 -4
  8. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/relation.sql +20 -11
  9. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/incremental/incremental.sql +2 -1
  10. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/incremental/merge.sql +3 -3
  11. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/view/view.sql +5 -1
  12. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/snapshots/helpers.sql +1 -1
  13. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/snapshots/snapshot.sql +2 -1
  14. dbt_sqlserver-1.12.2/dbt/include/sqlserver/macros/materializations/snapshots/strategies.sql +65 -0
  15. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/unit_tests.sql +10 -0
  16. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/relations/table/create.sql +4 -0
  17. dbt_sqlserver-1.12.2/dbt/include/sqlserver/macros/utils/array_append.sql +3 -0
  18. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt_sqlserver.egg-info/PKG-INFO +21 -14
  19. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt_sqlserver.egg-info/SOURCES.txt +1 -0
  20. dbt_sqlserver-1.12.0rc4/dbt/adapters/sqlserver/__version__.py +0 -1
  21. dbt_sqlserver-1.12.0rc4/dbt/include/sqlserver/macros/materializations/snapshots/strategies.sql +0 -5
  22. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/LICENSE +0 -0
  23. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/__init__.py +0 -0
  24. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/__init__.py +0 -0
  25. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/py.typed +0 -0
  26. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/relation_configs/__init__.py +0 -0
  27. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/relation_configs/index.py +0 -0
  28. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/relation_configs/policies.py +0 -0
  29. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_auth.py +0 -0
  30. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_backend.py +0 -0
  31. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_column.py +0 -0
  32. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_connections.py +0 -0
  33. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_constants.py +0 -0
  34. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_credentials.py +0 -0
  35. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_deny.py +0 -0
  36. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_helpers.py +0 -0
  37. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_mask.py +0 -0
  38. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_relation.py +0 -0
  39. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/adapters/sqlserver/sqlserver_runtime.py +0 -0
  40. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/__init__.py +0 -0
  41. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/dbt_project.yml +0 -0
  42. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/apply_denies.sql +0 -0
  43. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/apply_grants.sql +0 -0
  44. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/apply_masks.sql +0 -0
  45. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/catalog.sql +0 -0
  46. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/indexes.sql +0 -0
  47. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/persist_docs.sql +0 -0
  48. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/schema.sql +0 -0
  49. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/show.sql +0 -0
  50. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/adapters/validate_sql.sql +0 -0
  51. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/functions/helpers.sql +0 -0
  52. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/functions/scalar.sql +0 -0
  53. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/hooks.sql +0 -0
  54. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/incremental/incremental_strategies.sql +0 -0
  55. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/table/columns_spec_ddl.sql +0 -0
  56. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/table/table.sql +0 -0
  57. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/table/table_dml_refresh.sql +0 -0
  58. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/unit_test/get_fixture_sql.sql +0 -0
  59. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/unit_test/unit_test_create_table_as.sql +0 -0
  60. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/models/view/create_view_as.sql +0 -0
  61. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/snapshots/snapshot_merge.sql +0 -0
  62. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/materializations/tests.sql +0 -0
  63. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/relations/seeds/helpers.sql +0 -0
  64. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/relations/table/clone.sql +0 -0
  65. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/relations/views/create.sql +0 -0
  66. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/any_value.sql +0 -0
  67. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/array_construct.sql +0 -0
  68. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/cast_bool_to_text.sql +0 -0
  69. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/concat.sql +0 -0
  70. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/date_trunc.sql +0 -0
  71. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/dateadd.sql +0 -0
  72. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/get_tables_by_pattern.sql +0 -0
  73. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/hash.sql +0 -0
  74. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/last_day.sql +0 -0
  75. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/length.sql +0 -0
  76. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/listagg.sql +0 -0
  77. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/openquery.sql +0 -0
  78. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/position.sql +0 -0
  79. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/safe_cast.sql +0 -0
  80. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/split_part.sql +0 -0
  81. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/macros/utils/timestamps.sql +0 -0
  82. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt/include/sqlserver/profile_template.yml +0 -0
  83. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt_sqlserver.egg-info/dependency_links.txt +0 -0
  84. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt_sqlserver.egg-info/requires.txt +0 -0
  85. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/dbt_sqlserver.egg-info/top_level.txt +0 -0
  86. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/pyproject.toml +0 -0
  87. {dbt_sqlserver-1.12.0rc4 → dbt_sqlserver-1.12.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbt-sqlserver
3
- Version: 1.12.0rc4
3
+ Version: 1.12.2
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
@@ -77,7 +77,7 @@ We've bundled all documentation on the dbt docs site:
77
77
  - [Profile setup & authentication](https://docs.getdbt.com/reference/warehouse-profiles/mssql-profile)
78
78
  - [Adapter documentation, usage and important notes](https://docs.getdbt.com/reference/resource-configs/mssql-configs)
79
79
 
80
- Join us on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) to ask questions, get help, or to discuss the project.
80
+ Ask questions and get help in [GitHub Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions/categories/q-a). The [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) channel is there for chatting with the community.
81
81
 
82
82
  ## Installation
83
83
 
@@ -166,11 +166,11 @@ An Arrow-native backend built on [ADBC](https://arrow.apache.org/adbc/), avoidin
166
166
  pip install -U "dbt-sqlserver[adbc]"
167
167
  ```
168
168
 
169
- 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
+ The driver binary is not on PyPI and must be installed once via the `dbc` CLI. See [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/adbc_backend.md) for the full setup, configuration, and known-differences guide.
170
170
 
171
171
  ## Changelog
172
172
 
173
- See [the changelog](CHANGELOG.md)
173
+ See [the changelog](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CHANGELOG.md)
174
174
 
175
175
  ## Configuration
176
176
 
@@ -201,7 +201,7 @@ The same setting is also honoured via `vars:` for backwards compatibility; the b
201
201
 
202
202
  ### `backend`
203
203
 
204
- *(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.
204
+ *(default: `pyodbc`)* Set to `mssql-python` or `adbc` (experimental, see [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/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.
205
205
 
206
206
  ### `dbt_sqlserver_enable_safe_type_expansion`
207
207
 
@@ -227,7 +227,7 @@ two paths that commit a full-refresh marker before the load
227
227
  existing table), where neither scope can roll it back. `build` stages
228
228
  it inside the hook's transaction, for the one case `load` cannot serve: a
229
229
  `transaction: true` pre-hook that creates an object the model reads. See
230
- [docs/transaction_scope.md](docs/transaction_scope.md) for the full flow and
230
+ [docs/transaction_scope.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/transaction_scope.md) for the full flow and
231
231
  the post-hook ordering change.
232
232
 
233
233
  ### `dbt_sqlserver_use_dbt_transactions`
@@ -240,11 +240,12 @@ The driver connection remains in autocommit mode (`autocommit=true`) in both mod
240
240
 
241
241
  This is now the default and should be tested carefully with project-specific materializations and hooks. Projects that depend on autocommit-only behavior should set the flag to `false` during migration.
242
242
 
243
+ **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
244
+
243
245
  ```yaml
244
246
  # dbt_project.yml
245
247
  flags:
246
- dbt_sqlserver_enable_safe_type_expansion: true
247
- dbt_sqlserver_use_dbt_transactions: true # default
248
+ dbt_sqlserver_use_dbt_transactions: false # legacy autocommit; deprecated
248
249
  ```
249
250
 
250
251
  ### `dbt_sqlserver_use_native_string_types`
@@ -287,7 +288,11 @@ your_profile:
287
288
 
288
289
  ### `prefer_single_alter_column`
289
290
 
290
- *(default: `false`)* Model-level config that controls how `alter_column_type` changes column types on tables. When `false` (default), the adapter uses the safer approach: add a temporary column, copy data, drop the original, and rename. When `true`, the adapter uses a single `ALTER COLUMN` statement, which is faster on small, medium tables and instant on safe type expansions but may fail for types that cannot be implicitly converted.
291
+ *(default: unset)* Model-level config that controls how `alter_column_type` changes column types on tables:
292
+
293
+ * `true`: a single `ALTER COLUMN` statement. It is atomic, instant for a longer `varchar`/`nvarchar`, and keeps the column's indexes, default constraint and position. It fails for types that cannot be implicitly converted, and changing `varchar` to `nvarchar` on a large clustered columnstore table can fail with Msg 35357 (dictionary size limit).
294
+ * `false`: add a temporary column, copy the data, drop the original and rename. It fails when an index, default constraint or statistics object depends on the column, and it moves the column to the end of the table. A failed attempt no longer blocks the next run.
295
+ * unset (default): `true` when column type expansion widens a column within its type (a longer `varchar`, or `varchar(max)`), `false` for every other change.
291
296
 
292
297
  ```sql
293
298
  -- In an incremental model
@@ -295,8 +300,6 @@ your_profile:
295
300
  prefer_single_alter_column=true) }}
296
301
  ```
297
302
 
298
- **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
299
-
300
303
  ### `as_columnstore`
301
304
 
302
305
  *(default: `true`)* When building a table, the adapter creates a [clustered columnstore index](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-overview) (CCI) on it. Set `as_columnstore: false` to build a plain rowstore table instead.
@@ -322,6 +325,10 @@ You can also set it per model:
322
325
 
323
326
  With `table_refresh_method: dml`, a schema change makes the refresh fall back to a rename-swap. On that run — and only that run — the scratch table is rebuilt the way this adapter builds every other table, so it carries the model's columnstore index, and under an enforced contract its `NOT NULL`s and inline constraints, into the swap. That run therefore executes the model's SQL twice — once for the `SELECT … INTO` that probes for the schema change, once for the rebuild — and builds the columnstore index once. Steady-state refreshes are unaffected and keep the single `SELECT … INTO`. A table that lost its columnstore index to this bug before you upgraded is not repaired automatically: its schema still matches, so it stays on the cheap path. To rebuild it, temporarily set `full_refresh_build: prebuilt` and run with `--full-refresh`.
324
327
 
328
+ ### Ephemeral models
329
+
330
+ Ephemeral models are supported, with one limit: an ephemeral model can't start with its own `WITH`. dbt inlines it as a CTE, and T-SQL can't nest a `WITH` inside one (still true on SQL Server 2025). Write it with derived tables instead, or materialize it as a `view`, which SQL Server expands into the calling query the same way.
331
+
325
332
  ### Constraints
326
333
 
327
334
  Constraints declared in a model's yaml are applied when — and only when — the model's [contract](https://docs.getdbt.com/reference/resource-configs/contract) is enforced, which is what every dbt adapter does and keeps their cost opt-in. (dbt-core does not raise if you declare constraints with the contract off — they are simply never emitted.) `not_null`, `check`, `unique`, `primary_key` and `foreign_key` are all supported.
@@ -334,7 +341,7 @@ A *model-level* constraint carrying `name:` is applied by `ALTER TABLE … ADD C
334
341
 
335
342
  Name a constraint when you want it stable across environments (schema-comparison tools report the generated names as differences) or need to reference it later. A `name:` on a *column-level* constraint is ignored with a warning — declare it under the model's `constraints:` key instead.
336
343
 
337
- The full set of rules, with the test that verifies each, is in [docs/constraints.md](docs/constraints.md).
344
+ The full set of rules, with the test that verifies each, is in [docs/constraints.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/constraints.md).
338
345
 
339
346
  ```yaml
340
347
  models:
@@ -492,8 +499,8 @@ The `denies` config re-applies object-level DENYs after each build, diffed again
492
499
  [![Integration tests on SQL Server](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml/badge.svg)](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml)
493
500
 
494
501
  This adapter is community-maintained.
495
- You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in the Slack channel.
496
- If you're unsure how to get started, check out our [contributing guide](CONTRIBUTING.md).
502
+ You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in [Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions) and on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W).
503
+ If you're unsure how to get started, check out our [contributing guide](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CONTRIBUTING.md).
497
504
 
498
505
  ## License
499
506
 
@@ -39,7 +39,7 @@ We've bundled all documentation on the dbt docs site:
39
39
  - [Profile setup & authentication](https://docs.getdbt.com/reference/warehouse-profiles/mssql-profile)
40
40
  - [Adapter documentation, usage and important notes](https://docs.getdbt.com/reference/resource-configs/mssql-configs)
41
41
 
42
- Join us on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) to ask questions, get help, or to discuss the project.
42
+ Ask questions and get help in [GitHub Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions/categories/q-a). The [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) channel is there for chatting with the community.
43
43
 
44
44
  ## Installation
45
45
 
@@ -128,11 +128,11 @@ An Arrow-native backend built on [ADBC](https://arrow.apache.org/adbc/), avoidin
128
128
  pip install -U "dbt-sqlserver[adbc]"
129
129
  ```
130
130
 
131
- 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
+ The driver binary is not on PyPI and must be installed once via the `dbc` CLI. See [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/adbc_backend.md) for the full setup, configuration, and known-differences guide.
132
132
 
133
133
  ## Changelog
134
134
 
135
- See [the changelog](CHANGELOG.md)
135
+ See [the changelog](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CHANGELOG.md)
136
136
 
137
137
  ## Configuration
138
138
 
@@ -163,7 +163,7 @@ The same setting is also honoured via `vars:` for backwards compatibility; the b
163
163
 
164
164
  ### `backend`
165
165
 
166
- *(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.
166
+ *(default: `pyodbc`)* Set to `mssql-python` or `adbc` (experimental, see [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/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.
167
167
 
168
168
  ### `dbt_sqlserver_enable_safe_type_expansion`
169
169
 
@@ -189,7 +189,7 @@ two paths that commit a full-refresh marker before the load
189
189
  existing table), where neither scope can roll it back. `build` stages
190
190
  it inside the hook's transaction, for the one case `load` cannot serve: a
191
191
  `transaction: true` pre-hook that creates an object the model reads. See
192
- [docs/transaction_scope.md](docs/transaction_scope.md) for the full flow and
192
+ [docs/transaction_scope.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/transaction_scope.md) for the full flow and
193
193
  the post-hook ordering change.
194
194
 
195
195
  ### `dbt_sqlserver_use_dbt_transactions`
@@ -202,11 +202,12 @@ The driver connection remains in autocommit mode (`autocommit=true`) in both mod
202
202
 
203
203
  This is now the default and should be tested carefully with project-specific materializations and hooks. Projects that depend on autocommit-only behavior should set the flag to `false` during migration.
204
204
 
205
+ **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
206
+
205
207
  ```yaml
206
208
  # dbt_project.yml
207
209
  flags:
208
- dbt_sqlserver_enable_safe_type_expansion: true
209
- dbt_sqlserver_use_dbt_transactions: true # default
210
+ dbt_sqlserver_use_dbt_transactions: false # legacy autocommit; deprecated
210
211
  ```
211
212
 
212
213
  ### `dbt_sqlserver_use_native_string_types`
@@ -249,7 +250,11 @@ your_profile:
249
250
 
250
251
  ### `prefer_single_alter_column`
251
252
 
252
- *(default: `false`)* Model-level config that controls how `alter_column_type` changes column types on tables. When `false` (default), the adapter uses the safer approach: add a temporary column, copy data, drop the original, and rename. When `true`, the adapter uses a single `ALTER COLUMN` statement, which is faster on small, medium tables and instant on safe type expansions but may fail for types that cannot be implicitly converted.
253
+ *(default: unset)* Model-level config that controls how `alter_column_type` changes column types on tables:
254
+
255
+ * `true`: a single `ALTER COLUMN` statement. It is atomic, instant for a longer `varchar`/`nvarchar`, and keeps the column's indexes, default constraint and position. It fails for types that cannot be implicitly converted, and changing `varchar` to `nvarchar` on a large clustered columnstore table can fail with Msg 35357 (dictionary size limit).
256
+ * `false`: add a temporary column, copy the data, drop the original and rename. It fails when an index, default constraint or statistics object depends on the column, and it moves the column to the end of the table. A failed attempt no longer blocks the next run.
257
+ * unset (default): `true` when column type expansion widens a column within its type (a longer `varchar`, or `varchar(max)`), `false` for every other change.
253
258
 
254
259
  ```sql
255
260
  -- In an incremental model
@@ -257,8 +262,6 @@ your_profile:
257
262
  prefer_single_alter_column=true) }}
258
263
  ```
259
264
 
260
- **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
261
-
262
265
  ### `as_columnstore`
263
266
 
264
267
  *(default: `true`)* When building a table, the adapter creates a [clustered columnstore index](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-overview) (CCI) on it. Set `as_columnstore: false` to build a plain rowstore table instead.
@@ -284,6 +287,10 @@ You can also set it per model:
284
287
 
285
288
  With `table_refresh_method: dml`, a schema change makes the refresh fall back to a rename-swap. On that run — and only that run — the scratch table is rebuilt the way this adapter builds every other table, so it carries the model's columnstore index, and under an enforced contract its `NOT NULL`s and inline constraints, into the swap. That run therefore executes the model's SQL twice — once for the `SELECT … INTO` that probes for the schema change, once for the rebuild — and builds the columnstore index once. Steady-state refreshes are unaffected and keep the single `SELECT … INTO`. A table that lost its columnstore index to this bug before you upgraded is not repaired automatically: its schema still matches, so it stays on the cheap path. To rebuild it, temporarily set `full_refresh_build: prebuilt` and run with `--full-refresh`.
286
289
 
290
+ ### Ephemeral models
291
+
292
+ Ephemeral models are supported, with one limit: an ephemeral model can't start with its own `WITH`. dbt inlines it as a CTE, and T-SQL can't nest a `WITH` inside one (still true on SQL Server 2025). Write it with derived tables instead, or materialize it as a `view`, which SQL Server expands into the calling query the same way.
293
+
287
294
  ### Constraints
288
295
 
289
296
  Constraints declared in a model's yaml are applied when — and only when — the model's [contract](https://docs.getdbt.com/reference/resource-configs/contract) is enforced, which is what every dbt adapter does and keeps their cost opt-in. (dbt-core does not raise if you declare constraints with the contract off — they are simply never emitted.) `not_null`, `check`, `unique`, `primary_key` and `foreign_key` are all supported.
@@ -296,7 +303,7 @@ A *model-level* constraint carrying `name:` is applied by `ALTER TABLE … ADD C
296
303
 
297
304
  Name a constraint when you want it stable across environments (schema-comparison tools report the generated names as differences) or need to reference it later. A `name:` on a *column-level* constraint is ignored with a warning — declare it under the model's `constraints:` key instead.
298
305
 
299
- The full set of rules, with the test that verifies each, is in [docs/constraints.md](docs/constraints.md).
306
+ The full set of rules, with the test that verifies each, is in [docs/constraints.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/constraints.md).
300
307
 
301
308
  ```yaml
302
309
  models:
@@ -454,8 +461,8 @@ The `denies` config re-applies object-level DENYs after each build, diffed again
454
461
  [![Integration tests on SQL Server](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml/badge.svg)](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml)
455
462
 
456
463
  This adapter is community-maintained.
457
- You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in the Slack channel.
458
- If you're unsure how to get started, check out our [contributing guide](CONTRIBUTING.md).
464
+ You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in [Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions) and on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W).
465
+ If you're unsure how to get started, check out our [contributing guide](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CONTRIBUTING.md).
459
466
 
460
467
  ## License
461
468
 
@@ -0,0 +1 @@
1
+ version = "1.12.2"
@@ -1,6 +1,7 @@
1
1
  import datetime as _dt
2
2
  import re
3
- from typing import Any, Dict, List, Optional, Tuple, Type, Union
3
+ from contextlib import contextmanager
4
+ from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
4
5
 
5
6
  import agate
6
7
  import dbt_common.exceptions
@@ -446,8 +447,15 @@ class SQLServerAdapter(SQLAdapter):
446
447
 
447
448
  @classmethod
448
449
  def convert_number_type(cls, agate_table, col_idx):
449
- decimals = agate_table.aggregate(agate.MaxPrecision(col_idx))
450
- return "float" if decimals else "int"
450
+ if agate_table.aggregate(agate.MaxPrecision(col_idx)):
451
+ return "float"
452
+ values = agate_table.columns[col_idx].values_without_nulls()
453
+ low, high = (min(values), max(values)) if values else (0, 0)
454
+ if -(2**31) <= low and high < 2**31:
455
+ return "int"
456
+ if -(2**63) <= low and high < 2**63:
457
+ return "bigint"
458
+ return "numeric(38,0)"
451
459
 
452
460
  def create_schema(self, relation: BaseRelation) -> None:
453
461
  relation = relation.without_identifier()
@@ -470,6 +478,8 @@ class SQLServerAdapter(SQLAdapter):
470
478
  # see https://github.com/fishtown-analytics/dbt/pull/2255
471
479
  lens = [len(d.encode("utf-8")) for d in column.values_without_nulls()]
472
480
  max_len = max(lens) if lens else 64
481
+ if max_len > 8000:
482
+ return "varchar(max)"
473
483
  length = max_len if max_len > 16 else 16
474
484
  return "varchar({})".format(length)
475
485
 
@@ -828,13 +838,27 @@ class SQLServerAdapter(SQLAdapter):
828
838
 
829
839
  return True
830
840
 
831
- def expand_column_types(self, goal, current, max_rows: int = 1000000):
841
+ def expand_column_types(
842
+ self,
843
+ goal,
844
+ current,
845
+ max_rows: int = 1000000,
846
+ prefer_single_alter_column: Optional[bool] = None,
847
+ ):
832
848
  """Widen ``current``'s columns to match ``goal``, preserving the
833
849
  nvarchar / nchar family.
834
850
 
835
851
  Same-family resizes (a longer varchar) always proceed. Cross-family
836
852
  promotions (varchar -> nvarchar) are opt-in and gated; see
837
853
  ``_safe_expansion_allowed``.
854
+
855
+ ``prefer_single_alter_column`` is the model's config. Unset, a
856
+ same-family resize uses a single ``ALTER COLUMN``: metadata-only for a
857
+ longer varchar, atomic, and it keeps indexes, defaults and column
858
+ position, all of which block or move the four-step rewrite. A
859
+ cross-family promotion keeps the four-step path, since a single
860
+ ``ALTER COLUMN`` to nvarchar on a large columnstore table can fail
861
+ with Msg 35357 (dictionary size limit).
838
862
  """
839
863
 
840
864
  reference_columns = {c.name: c for c in self.get_columns_in_relation(goal)}
@@ -847,8 +871,9 @@ class SQLServerAdapter(SQLAdapter):
847
871
  if target_column is None:
848
872
  continue
849
873
 
874
+ same_family = target_column.can_expand_to(reference_column)
850
875
  if not (
851
- target_column.can_expand_to(reference_column)
876
+ same_family
852
877
  or (safe_expansion_allowed and target_column.can_expand_safe(reference_column))
853
878
  ):
854
879
  continue
@@ -865,11 +890,46 @@ class SQLServerAdapter(SQLAdapter):
865
890
  table=_make_ref_key_dict(current),
866
891
  )
867
892
  )
868
- self.alter_column_type(current, column_name, new_type)
893
+ prefer_single = (
894
+ same_family if prefer_single_alter_column is None else prefer_single_alter_column
895
+ )
896
+ self.alter_column_type(current, column_name, new_type, prefer_single)
897
+
898
+ def alter_column_type(
899
+ self,
900
+ relation,
901
+ column_name,
902
+ new_column_type,
903
+ prefer_single_alter_column: Optional[bool] = None,
904
+ ) -> None:
905
+ """Pass the model's ``prefer_single_alter_column`` to the macro.
906
+
907
+ A macro run from Python sees no model config, so
908
+ ``config.get('prefer_single_alter_column')`` inside it is always the
909
+ default; and dbt-adapters' ``alter_column_type`` dispatcher forwards
910
+ only the three standard arguments. When the setting is known, call
911
+ the SQL Server implementation with it directly (dbt-msft/dbt-sqlserver#836).
912
+ """
913
+ if prefer_single_alter_column is None:
914
+ super().alter_column_type(relation, column_name, new_column_type)
915
+ return
916
+ self.execute_macro(
917
+ "sqlserver__alter_column_type",
918
+ kwargs={
919
+ "relation": relation,
920
+ "column_name": column_name,
921
+ "new_column_type": new_column_type,
922
+ "prefer_single": prefer_single_alter_column,
923
+ },
924
+ )
869
925
 
870
926
  @available.parse_none
871
927
  def expand_target_column_types(
872
- self, from_relation: BaseRelation, to_relation: BaseRelation, max_rows: int = 1000000
928
+ self,
929
+ from_relation: BaseRelation,
930
+ to_relation: BaseRelation,
931
+ max_rows: int = 1000000,
932
+ prefer_single_alter_column: Optional[bool] = None,
873
933
  ) -> None:
874
934
  if not isinstance(from_relation, self.Relation):
875
935
  from dbt.adapters.base.impl import MacroArgTypeError
@@ -889,7 +949,7 @@ class SQLServerAdapter(SQLAdapter):
889
949
  got_value=to_relation,
890
950
  expected_type=self.Relation,
891
951
  )
892
- self.expand_column_types(from_relation, to_relation, max_rows)
952
+ self.expand_column_types(from_relation, to_relation, max_rows, prefer_single_alter_column)
893
953
 
894
954
  @available
895
955
  def parse_index(self, raw_index: Any) -> Optional[SQLServerIndexConfig]:
@@ -906,6 +966,28 @@ class SQLServerAdapter(SQLAdapter):
906
966
  return False
907
967
  return create_needs_own_batch(parsed.build_options)
908
968
 
969
+ # dbt-core's run-operation runs on connections with these names.
970
+ _RUN_OPERATION_CONNECTION_PREFIXES = ("macro_", "inline_query")
971
+
972
+ @contextmanager
973
+ def connection_named(
974
+ self, name: str, query_header_context: Any = None, should_release_connection: bool = True
975
+ ) -> Iterator[None]:
976
+ """Commit a run-operation's open transaction when it finishes cleanly.
977
+
978
+ Stopgap for dbt-labs/dbt#16434, remove once dbt-core commits after a
979
+ run-operation: dbt-core never does, so with dbt-managed transactions
980
+ on, a write through ``statement()`` (auto_begin) is rolled back when
981
+ the connection closes. The commit sits after the ``yield`` so it runs
982
+ only when the macro did not raise; a failed macro still rolls back.
983
+ Matching on the connection name is a heuristic, since dbt-core marks
984
+ run-operation connections no other way.
985
+ """
986
+ with super().connection_named(name, query_header_context, should_release_connection):
987
+ yield
988
+ if name.startswith(self._RUN_OPERATION_CONNECTION_PREFIXES):
989
+ self.commit_if_open()
990
+
909
991
  @available
910
992
  def commit_if_open(self) -> None:
911
993
  """Commit the current transaction if one is open - a no-op otherwise.
@@ -10,7 +10,8 @@ from dbt.adapters.sqlserver.relation_configs import SQLServerIndexConfig
10
10
  @dataclass
11
11
  class SQLServerConfigs(AdapterConfig):
12
12
  auto_provision_aad_principals: Optional[bool] = False
13
- prefer_single_alter_column: Optional[bool] = False
13
+ # unset: single ALTER COLUMN for same-family widening, four-step otherwise
14
+ prefer_single_alter_column: Optional[bool] = None
14
15
  column_type_expansion_max_rows: int = 1000000
15
16
  indexes: Optional[Tuple[SQLServerIndexConfig, ...]] = None
16
17
  # false (default) | warn | true - how index reconciliation treats
@@ -36,19 +36,38 @@
36
36
  {% endif %}
37
37
  {% endmacro %}
38
38
 
39
- {% macro sqlserver__alter_column_type(relation, column_name, new_column_type) %}
39
+ {% macro sqlserver__alter_column_type(relation, column_name, new_column_type, prefer_single=none) %}
40
40
 
41
- {% set prefer_single = config.get('prefer_single_alter_column', false) %}
41
+ {#-- Called from Python (expand_target_column_types) the macro sees no model config, so the adapter passes prefer_single in (dbt-msft/dbt-sqlserver#836) --#}
42
+ {% if prefer_single is none %}
43
+ {% set prefer_single = config.get('prefer_single_alter_column', false) %}
44
+ {% endif %}
45
+
46
+ {#-- ALTER COLUMN without NOT NULL makes the column nullable --#}
47
+ {% set nullable_sql %}
48
+ {{ get_use_database_sql(relation.database) }}
49
+ select columnproperty(object_id('{{ escape_single_quotes(relation) }}'), '{{ escape_single_quotes(column_name) }}', 'AllowsNull')
50
+ {%- endset %}
51
+ {%- set not_null = run_query(nullable_sql).columns[0].values()[0] == 0 -%}
42
52
 
43
53
  {% if prefer_single and relation.type == 'table' %}
44
54
  {% set alter_sql %}
45
55
  alter {{ relation.type }} {{ relation }}
46
- alter column "{{ column_name }}" {{ new_column_type }};
56
+ alter column "{{ column_name }}" {{ new_column_type }}{{ ' not null' if not_null }};
47
57
  {%- endset %}
48
58
  {% do run_query(alter_sql) %}
49
59
 
50
60
  {% else %}
51
61
  {%- set tmp_column = column_name + "__dbt_alter" -%}
62
+ {%- set relation_name = escape_single_quotes(relation.include(database=False)) -%}
63
+
64
+ {#-- The four steps below autocommit one by one, so a failed run can leave tmp_column behind, and the next run's ADD then fails forever (dbt-msft/dbt-sqlserver#836).
65
+ Drop it first. It is only a partial copy while the original column still exists; if the original is gone, tmp_column holds the data, so it is left alone. --#}
66
+ {% set drop_leftover %}
67
+ if col_length('{{ relation_name }}', '{{ escape_single_quotes(tmp_column) }}') is not null
68
+ and col_length('{{ relation_name }}', '{{ escape_single_quotes(column_name) }}') is not null
69
+ alter {{ relation.type }} {{ relation }} drop column "{{ tmp_column }}";
70
+ {%- endset %}
52
71
 
53
72
  {% set add_column %}
54
73
  alter {{ relation.type }} {{ relation }}
@@ -62,13 +81,21 @@
62
81
  drop column "{{ column_name }}";
63
82
  {%- endset %}
64
83
  {% set rename_column %}
65
- exec sp_rename '{{ escape_single_quotes(relation.include(database=False)) }}.{{ escape_single_quotes(adapter.quote(tmp_column)) }}', '{{ escape_single_quotes(column_name) }}', 'column'
84
+ exec sp_rename '{{ relation_name }}.{{ escape_single_quotes(adapter.quote(tmp_column)) }}', '{{ escape_single_quotes(column_name) }}', 'column'
85
+ {%- endset %}
86
+ {% set alter_sql_not_null %}
87
+ alter {{ relation.type }} {{ relation }}
88
+ alter column "{{ column_name }}" {{ new_column_type }} not null;
66
89
  {%- endset %}
67
90
 
91
+ {% do run_query(drop_leftover) %}
68
92
  {% do run_query(add_column) %}
69
93
  {% do run_query(update_column) %}
70
94
  {% do run_query(drop_column) %}
71
95
  {% do run_query(rename_column) %}
96
+ {% if not_null %}
97
+ {% do run_query(alter_sql_not_null) %}
98
+ {% endif %}
72
99
  {% endif %}
73
100
 
74
101
  {% endmacro %}
@@ -197,11 +197,13 @@
197
197
  - dm_exec_describe_first_result_set is referenced once: each reference compiles the body.
198
198
  - Its error row is kept, so a body it cannot describe is refreshed.
199
199
  - COLLATE DATABASE_DEFAULT, or the join fails where catalog and database collations differ.
200
- - The body is inlined: the function needs nvarchar and mssql-python binds str as varchar. -#}
200
+ - Run through sp_executesql, so the statement text is the same for every view and SQL
201
+ Server caches one plan for all of them.
202
+ - The values are inlined, typed by the parameter list: the function needs nvarchar and
203
+ mssql-python binds str as varchar. -#}
201
204
  {% macro sqlserver__get_view_skip_check_sql(relation, compiled_sql) -%}
202
- {%- set object_name = "quotename('" ~ relation.schema ~ "') + '.' + quotename('" ~ relation.identifier ~ "')" -%}
203
- {{ get_use_database_sql(relation.database) }}
204
- declare @dbt_sqlserver_skip_check_body nvarchar(max) = N'{{ escape_single_quotes(compiled_sql) }}';
205
+ {%- set object_name = "quotename(@schema) + '.' + quotename(@identifier)" -%}
206
+ {%- set check_sql -%}
205
207
  select
206
208
  object_definition(object_id({{ object_name }}, 'V')) as definition,
207
209
  case when exists (
@@ -234,6 +236,13 @@
234
236
  <> isnull(cached.collation_name, '') collate database_default
235
237
  ) then 1 else 0 end as needs_refresh
236
238
  where object_id({{ object_name }}, 'V') is not null
239
+ {%- endset -%}
240
+ {{ get_use_database_sql(relation.database) }}
241
+ exec sp_executesql N'{{ escape_single_quotes(check_sql) }}',
242
+ N'@schema sysname, @identifier sysname, @dbt_sqlserver_skip_check_body nvarchar(max)',
243
+ @schema = N'{{ escape_single_quotes(relation.schema) }}',
244
+ @identifier = N'{{ escape_single_quotes(relation.identifier) }}',
245
+ @dbt_sqlserver_skip_check_body = N'{{ escape_single_quotes(compiled_sql) }}';
237
246
  {% endmacro %}
238
247
 
239
248
  {% macro sqlserver__get_relation_last_modified(information_schema, relations) -%}
@@ -8,14 +8,9 @@
8
8
 
9
9
  {% macro sqlserver__get_drop_sql(relation) -%}
10
10
  {% if relation.type == 'view' -%}
11
- {#- auto_begin=false, matching dbt-adapters' own relations/drop.sql: a
12
- read-only lookup must not open the ambient transaction. This is the
13
- first statement of the temp-relation build (create.sql ->
14
- adapter.drop_relation), so with the default auto_begin=True it held
15
- that build's sys.sysschobjs X locks until the materialization
16
- committed, deadlocking a concurrent worker (error 1205). -#}
17
- {% call statement('find_references', fetch_result=true, auto_begin=false) %}
18
- {{ get_use_database_sql(relation.database) }}
11
+ {#- Run through sp_executesql, so the statement text is the same for every
12
+ relation and SQL Server caches one plan for all of them. -#}
13
+ {%- set find_references_sql -%}
19
14
  select
20
15
  sch.name as schema_name,
21
16
  obj.name as view_name
@@ -24,11 +19,25 @@
24
19
  on refs.referencing_id = obj.object_id
25
20
  inner join sys.schemas sch {{ information_schema_hints() }}
26
21
  on obj.schema_id = sch.schema_id
27
- where refs.referenced_database_name = '{{ relation.database }}'
28
- and refs.referenced_schema_name = '{{ relation.schema }}'
29
- and refs.referenced_entity_name = '{{ relation.identifier }}'
22
+ where refs.referenced_database_name = @database
23
+ and refs.referenced_schema_name = @schema
24
+ and refs.referenced_entity_name = @identifier
30
25
  and obj.type = 'V'
31
26
  {{ get_query_options() }}
27
+ {%- endset %}
28
+ {#- auto_begin=false, matching dbt-adapters' own relations/drop.sql: a
29
+ read-only lookup must not open the ambient transaction. This is the
30
+ first statement of the temp-relation build (create.sql ->
31
+ adapter.drop_relation), so with the default auto_begin=True it held
32
+ that build's sys.sysschobjs X locks until the materialization
33
+ committed, deadlocking a concurrent worker (error 1205). -#}
34
+ {% call statement('find_references', fetch_result=true, auto_begin=false) %}
35
+ {{ get_use_database_sql(relation.database) }}
36
+ exec sp_executesql N'{{ escape_single_quotes(find_references_sql) }}',
37
+ N'@database sysname, @schema sysname, @identifier sysname',
38
+ @database = N'{{ escape_single_quotes(relation.database) }}',
39
+ @schema = N'{{ escape_single_quotes(relation.schema) }}',
40
+ @identifier = N'{{ escape_single_quotes(relation.identifier) }}';
32
41
  {% endcall %}
33
42
  {% set references = load_result('find_references')['data'] %}
34
43
  {% for reference in references -%}
@@ -175,7 +175,8 @@
175
175
  {% do adapter.expand_target_column_types(
176
176
  from_relation=temp_relation,
177
177
  to_relation=target_relation,
178
- max_rows=expansion_max_rows) %}
178
+ max_rows=expansion_max_rows,
179
+ prefer_single_alter_column=config.get('prefer_single_alter_column')) %}
179
180
  {% endif %}
180
181
  {#-- Process schema changes. Returns dict of changes if successful. Use source columns for upserting/merging --#}
181
182
  {% set dest_columns = process_schema_changes(on_schema_change, temp_relation, existing_relation) %}
@@ -15,13 +15,13 @@
15
15
  {% if unique_key %}
16
16
  {% if unique_key is sequence and unique_key is not string %}
17
17
  SET NOCOUNT ON;
18
- delete from {{ target }}
18
+ delete DBT_INTERNAL_DEST from {{ target }} as DBT_INTERNAL_DEST
19
19
  where exists (
20
20
  select null
21
21
  from {{ source }}
22
22
  where
23
23
  {% for key in unique_key %}
24
- {{ source }}.{{ key }} = {{ target }}.{{ key }}
24
+ {{ source }}.{{ key }} = DBT_INTERNAL_DEST.{{ key }}
25
25
  {{ "and " if not loop.last }}
26
26
  {% endfor %}
27
27
  )
@@ -34,7 +34,7 @@
34
34
  SET NOCOUNT OFF;
35
35
  {% else %}
36
36
  SET NOCOUNT ON;
37
- delete from {{ target }}
37
+ delete DBT_INTERNAL_DEST from {{ target }} as DBT_INTERNAL_DEST
38
38
  where (
39
39
  {{ unique_key }}) in (
40
40
  select ({{ unique_key }})
@@ -134,7 +134,11 @@
134
134
 
135
135
  {{ adapter.commit() }}
136
136
 
137
- {{ drop_relation_if_exists(backup_relation) }}
137
+ {#- Only the table branch above makes a backup. A view is altered in place, and a
138
+ drop would still query the missing backup's dependents. -#}
139
+ {% if existing_relation is not none and existing_relation.type != 'view' %}
140
+ {{ drop_relation_if_exists(backup_relation) }}
141
+ {% endif %}
138
142
 
139
143
  {{ run_hooks(post_hooks, inside_transaction=False) }}
140
144
 
@@ -1,7 +1,7 @@
1
1
  {% macro sqlserver__create_columns(relation, columns) %}
2
2
  {% set column_list %}
3
3
  {% for column_entry in columns %}
4
- {{column_entry.name}} {{column_entry.data_type}}{{ ", " if not loop.last }}
4
+ {{ adapter.quote(column_entry.name) }} {{column_entry.data_type}}{{ ", " if not loop.last }}
5
5
  {% endfor %}
6
6
  {% endset %}
7
7
 
@@ -115,7 +115,8 @@
115
115
  {% set expansion_max_rows = config.get('column_type_expansion_max_rows', 1000000) %}
116
116
  {% do adapter.expand_target_column_types(from_relation=staging_table,
117
117
  to_relation=target_relation,
118
- max_rows=expansion_max_rows) %}
118
+ max_rows=expansion_max_rows,
119
+ prefer_single_alter_column=config.get('prefer_single_alter_column')) %}
119
120
 
120
121
  {% set remove_columns = ['dbt_change_type', 'DBT_CHANGE_TYPE', 'dbt_unique_key', 'DBT_UNIQUE_KEY'] %}
121
122
  {% if unique_key | is_list %}
@@ -0,0 +1,65 @@
1
+ {% macro sqlserver__snapshot_hash_arguments(args) %}
2
+ CONVERT(VARCHAR(32), HashBytes('MD5', {% for arg in args %}
3
+ coalesce(cast({{ arg }} as varchar(8000)), '') {% if not loop.last %} + '|' + {% endif %}
4
+ {% endfor %}), 2)
5
+ {% endmacro %}
6
+
7
+
8
+ {#
9
+ dbt-adapters does not dispatch "snapshot_check_all_get_existing_columns", so
10
+ this shadows the global project's macro; adapter package macros take
11
+ precedence. Keep in sync with dbt-adapters when upgrading. When upstream
12
+ dispatches it, delete the public wrapper and keep the sqlserver__ handler.
13
+
14
+ Change from upstream: for a check_cols list, upstream reads the columns'
15
+ casing from "select <check_cols> from (<sql>) subq", and T-SQL can't nest a
16
+ WITH in a subquery. This reads the query's columns unwrapped, as the 'all'
17
+ branch does, and picks check_cols from them case-insensitively.
18
+ #}
19
+
20
+ {% macro snapshot_check_all_get_existing_columns(node, target_exists, check_cols_config) -%}
21
+ {{ return(sqlserver__snapshot_check_all_get_existing_columns(node, target_exists, check_cols_config)) }}
22
+ {%- endmacro %}
23
+
24
+
25
+ {% macro sqlserver__snapshot_check_all_get_existing_columns(node, target_exists, check_cols_config) -%}
26
+ {%- if not target_exists -%}
27
+ {#-- no table yet -> return whatever the query does --#}
28
+ {{ return((false, query_columns)) }}
29
+ {%- endif -%}
30
+
31
+ {#-- handle any schema changes --#}
32
+ {%- set target_relation = adapter.get_relation(database=node.database, schema=node.schema, identifier=node.alias) -%}
33
+
34
+ {% if check_cols_config == 'all' %}
35
+ {%- set query_columns = get_columns_in_query(node['compiled_code']) -%}
36
+
37
+ {% elif check_cols_config is iterable and (check_cols_config | length) > 0 %}
38
+ {%- set all_columns = get_columns_in_query(node['compiled_code']) -%}
39
+ {%- set all_columns_lower = all_columns | map('lower') | list -%}
40
+ {%- set query_columns = [] -%}
41
+ {%- for check_col in check_cols_config -%}
42
+ {%- if (check_col | lower) not in all_columns_lower -%}
43
+ {% do exceptions.raise_compiler_error("check_cols column '" ~ check_col ~ "' is not in the snapshot query") %}
44
+ {%- endif -%}
45
+ {%- do query_columns.append(all_columns[all_columns_lower.index(check_col | lower)]) -%}
46
+ {%- endfor -%}
47
+
48
+ {% else %}
49
+ {% do exceptions.raise_compiler_error("Invalid value for 'check_cols': " ~ check_cols_config) %}
50
+ {% endif %}
51
+
52
+ {%- set existing_cols = adapter.get_columns_in_relation(target_relation) | map(attribute = 'name') | list -%}
53
+ {%- set ns = namespace() -%} {#-- handle for-loop scoping with a namespace --#}
54
+ {%- set ns.column_added = false -%}
55
+
56
+ {%- set intersection = [] -%}
57
+ {%- for col in query_columns -%}
58
+ {%- if col in existing_cols -%}
59
+ {%- do intersection.append(adapter.quote(col)) -%}
60
+ {%- else -%}
61
+ {% set ns.column_added = true %}
62
+ {%- endif -%}
63
+ {%- endfor -%}
64
+ {{ return((ns.column_added, intersection)) }}
65
+ {%- endmacro %}
@@ -1,3 +1,13 @@
1
+ {%- materialization unit, adapter='sqlserver' -%}
2
+ {#- The default drops its fixture table inside the transaction
3
+ statement('main') opens and never commits, so with
4
+ dbt_sqlserver_use_dbt_transactions on the drop rolls back
5
+ (dbt-labs/dbt#16499). -#}
6
+ {% set relations = materialization_unit_default() %}
7
+ {% do adapter.commit_if_open() %}
8
+ {{ return(relations) }}
9
+ {%- endmaterialization -%}
10
+
1
11
  {% macro sqlserver__get_unit_test_sql(main_sql, expected_fixture_sql, expected_column_names) -%}
2
12
 
3
13
  {{ get_use_database_sql(target.database) }}
@@ -35,7 +35,11 @@
35
35
  {{ get_assert_columns_equivalent(sql) }}
36
36
  {{ build_columns_constraints(relation) }}
37
37
  {%- else -%}
38
+ {#- the UNION ALL keeps SELECT INTO from copying an IDENTITY column, which the
39
+ load's explicit ids would then violate; nullability and types are unchanged -#}
38
40
  SELECT TOP 0 * INTO {{ relation }} FROM {{ tmp_relation }}
41
+ UNION ALL
42
+ SELECT TOP 0 * FROM {{ tmp_relation }}
39
43
  {%- endif -%}
40
44
  {%- endmacro %}
41
45
 
@@ -0,0 +1,3 @@
1
+ {% macro sqlserver__array_append(array, new_element) -%}
2
+ JSON_MODIFY({{ array }}, 'append $', {{ new_element }})
3
+ {%- endmacro %}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbt-sqlserver
3
- Version: 1.12.0rc4
3
+ Version: 1.12.2
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
@@ -77,7 +77,7 @@ We've bundled all documentation on the dbt docs site:
77
77
  - [Profile setup & authentication](https://docs.getdbt.com/reference/warehouse-profiles/mssql-profile)
78
78
  - [Adapter documentation, usage and important notes](https://docs.getdbt.com/reference/resource-configs/mssql-configs)
79
79
 
80
- Join us on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) to ask questions, get help, or to discuss the project.
80
+ Ask questions and get help in [GitHub Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions/categories/q-a). The [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W) channel is there for chatting with the community.
81
81
 
82
82
  ## Installation
83
83
 
@@ -166,11 +166,11 @@ An Arrow-native backend built on [ADBC](https://arrow.apache.org/adbc/), avoidin
166
166
  pip install -U "dbt-sqlserver[adbc]"
167
167
  ```
168
168
 
169
- 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
+ The driver binary is not on PyPI and must be installed once via the `dbc` CLI. See [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/adbc_backend.md) for the full setup, configuration, and known-differences guide.
170
170
 
171
171
  ## Changelog
172
172
 
173
- See [the changelog](CHANGELOG.md)
173
+ See [the changelog](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CHANGELOG.md)
174
174
 
175
175
  ## Configuration
176
176
 
@@ -201,7 +201,7 @@ The same setting is also honoured via `vars:` for backwards compatibility; the b
201
201
 
202
202
  ### `backend`
203
203
 
204
- *(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.
204
+ *(default: `pyodbc`)* Set to `mssql-python` or `adbc` (experimental, see [docs/adbc_backend.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/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.
205
205
 
206
206
  ### `dbt_sqlserver_enable_safe_type_expansion`
207
207
 
@@ -227,7 +227,7 @@ two paths that commit a full-refresh marker before the load
227
227
  existing table), where neither scope can roll it back. `build` stages
228
228
  it inside the hook's transaction, for the one case `load` cannot serve: a
229
229
  `transaction: true` pre-hook that creates an object the model reads. See
230
- [docs/transaction_scope.md](docs/transaction_scope.md) for the full flow and
230
+ [docs/transaction_scope.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/transaction_scope.md) for the full flow and
231
231
  the post-hook ordering change.
232
232
 
233
233
  ### `dbt_sqlserver_use_dbt_transactions`
@@ -240,11 +240,12 @@ The driver connection remains in autocommit mode (`autocommit=true`) in both mod
240
240
 
241
241
  This is now the default and should be tested carefully with project-specific materializations and hooks. Projects that depend on autocommit-only behavior should set the flag to `false` during migration.
242
242
 
243
+ **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
244
+
243
245
  ```yaml
244
246
  # dbt_project.yml
245
247
  flags:
246
- dbt_sqlserver_enable_safe_type_expansion: true
247
- dbt_sqlserver_use_dbt_transactions: true # default
248
+ dbt_sqlserver_use_dbt_transactions: false # legacy autocommit; deprecated
248
249
  ```
249
250
 
250
251
  ### `dbt_sqlserver_use_native_string_types`
@@ -287,7 +288,11 @@ your_profile:
287
288
 
288
289
  ### `prefer_single_alter_column`
289
290
 
290
- *(default: `false`)* Model-level config that controls how `alter_column_type` changes column types on tables. When `false` (default), the adapter uses the safer approach: add a temporary column, copy data, drop the original, and rename. When `true`, the adapter uses a single `ALTER COLUMN` statement, which is faster on small, medium tables and instant on safe type expansions but may fail for types that cannot be implicitly converted.
291
+ *(default: unset)* Model-level config that controls how `alter_column_type` changes column types on tables:
292
+
293
+ * `true`: a single `ALTER COLUMN` statement. It is atomic, instant for a longer `varchar`/`nvarchar`, and keeps the column's indexes, default constraint and position. It fails for types that cannot be implicitly converted, and changing `varchar` to `nvarchar` on a large clustered columnstore table can fail with Msg 35357 (dictionary size limit).
294
+ * `false`: add a temporary column, copy the data, drop the original and rename. It fails when an index, default constraint or statistics object depends on the column, and it moves the column to the end of the table. A failed attempt no longer blocks the next run.
295
+ * unset (default): `true` when column type expansion widens a column within its type (a longer `varchar`, or `varchar(max)`), `false` for every other change.
291
296
 
292
297
  ```sql
293
298
  -- In an incremental model
@@ -295,8 +300,6 @@ your_profile:
295
300
  prefer_single_alter_column=true) }}
296
301
  ```
297
302
 
298
- **Compatibility notes:** Enabling `dbt_sqlserver_use_dbt_transactions: true` may expose transaction-state assumptions hidden by autocommit-only mode. Explicit transaction macros may interact with dbt-managed transactions, and cleanup after failed DDL/DML may differ. Review pre/post hooks for in-transaction vs out-of-transaction semantics.
299
-
300
303
  ### `as_columnstore`
301
304
 
302
305
  *(default: `true`)* When building a table, the adapter creates a [clustered columnstore index](https://learn.microsoft.com/en-us/sql/relational-databases/indexes/columnstore-indexes-overview) (CCI) on it. Set `as_columnstore: false` to build a plain rowstore table instead.
@@ -322,6 +325,10 @@ You can also set it per model:
322
325
 
323
326
  With `table_refresh_method: dml`, a schema change makes the refresh fall back to a rename-swap. On that run — and only that run — the scratch table is rebuilt the way this adapter builds every other table, so it carries the model's columnstore index, and under an enforced contract its `NOT NULL`s and inline constraints, into the swap. That run therefore executes the model's SQL twice — once for the `SELECT … INTO` that probes for the schema change, once for the rebuild — and builds the columnstore index once. Steady-state refreshes are unaffected and keep the single `SELECT … INTO`. A table that lost its columnstore index to this bug before you upgraded is not repaired automatically: its schema still matches, so it stays on the cheap path. To rebuild it, temporarily set `full_refresh_build: prebuilt` and run with `--full-refresh`.
324
327
 
328
+ ### Ephemeral models
329
+
330
+ Ephemeral models are supported, with one limit: an ephemeral model can't start with its own `WITH`. dbt inlines it as a CTE, and T-SQL can't nest a `WITH` inside one (still true on SQL Server 2025). Write it with derived tables instead, or materialize it as a `view`, which SQL Server expands into the calling query the same way.
331
+
325
332
  ### Constraints
326
333
 
327
334
  Constraints declared in a model's yaml are applied when — and only when — the model's [contract](https://docs.getdbt.com/reference/resource-configs/contract) is enforced, which is what every dbt adapter does and keeps their cost opt-in. (dbt-core does not raise if you declare constraints with the contract off — they are simply never emitted.) `not_null`, `check`, `unique`, `primary_key` and `foreign_key` are all supported.
@@ -334,7 +341,7 @@ A *model-level* constraint carrying `name:` is applied by `ALTER TABLE … ADD C
334
341
 
335
342
  Name a constraint when you want it stable across environments (schema-comparison tools report the generated names as differences) or need to reference it later. A `name:` on a *column-level* constraint is ignored with a warning — declare it under the model's `constraints:` key instead.
336
343
 
337
- The full set of rules, with the test that verifies each, is in [docs/constraints.md](docs/constraints.md).
344
+ The full set of rules, with the test that verifies each, is in [docs/constraints.md](https://github.com/dbt-msft/dbt-sqlserver/blob/master/docs/constraints.md).
338
345
 
339
346
  ```yaml
340
347
  models:
@@ -492,8 +499,8 @@ The `denies` config re-applies object-level DENYs after each build, diffed again
492
499
  [![Integration tests on SQL Server](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml/badge.svg)](https://github.com/dbt-msft/dbt-sqlserver/actions/workflows/integration-tests-sqlserver.yml)
493
500
 
494
501
  This adapter is community-maintained.
495
- You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in the Slack channel.
496
- If you're unsure how to get started, check out our [contributing guide](CONTRIBUTING.md).
502
+ You are welcome to contribute by creating issues, opening or reviewing pull requests, or helping other users in [Discussions](https://github.com/dbt-msft/dbt-sqlserver/discussions) and on the [dbt Slack](https://getdbt.slack.com/archives/CMRMDDQ9W).
503
+ If you're unsure how to get started, check out our [contributing guide](https://github.com/dbt-msft/dbt-sqlserver/blob/master/CONTRIBUTING.md).
497
504
 
498
505
  ## License
499
506
 
@@ -60,6 +60,7 @@ dbt/include/sqlserver/macros/relations/table/clone.sql
60
60
  dbt/include/sqlserver/macros/relations/table/create.sql
61
61
  dbt/include/sqlserver/macros/relations/views/create.sql
62
62
  dbt/include/sqlserver/macros/utils/any_value.sql
63
+ dbt/include/sqlserver/macros/utils/array_append.sql
63
64
  dbt/include/sqlserver/macros/utils/array_construct.sql
64
65
  dbt/include/sqlserver/macros/utils/cast_bool_to_text.sql
65
66
  dbt/include/sqlserver/macros/utils/concat.sql
@@ -1 +0,0 @@
1
- version = "1.12.0rc4"
@@ -1,5 +0,0 @@
1
- {% macro sqlserver__snapshot_hash_arguments(args) %}
2
- CONVERT(VARCHAR(32), HashBytes('MD5', {% for arg in args %}
3
- coalesce(cast({{ arg }} as varchar(8000)), '') {% if not loop.last %} + '|' + {% endif %}
4
- {% endfor %}), 2)
5
- {% endmacro %}