Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/type-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
name: Type check
on: # yamllint disable-line rule:truthy
workflow_dispatch:
push:
branches:
- master
- v*
pull_request:
branches:
- master
- v*

# ty runs here rather than on pre-commit.ci, which blocks the network access ty
# needs to resolve this project's dependencies.
jobs:
ty:
name: ty
runs-on: ubuntu-latest
permissions:
contents: read
steps:

- uses: actions/checkout@v7

- name: Install uv
uses: astral-sh/setup-uv@v9
with:
enable-cache: true
python-version: '3.11'

- name: Run ty
run: uv run --frozen ty check
3 changes: 0 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@ target/
# vscode
.vscode/

# Mypy cache
.mypy_cache/

# Environments
*.env
.venv
Expand Down
39 changes: 21 additions & 18 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
default_language_version:
python: python3.11
ci:
# The ty hook shells out to uv to resolve this project's dependencies, and
# pre-commit.ci runs hooks without network access. The Type check workflow
# runs ty in GitHub Actions instead.
skip:
- ty
repos:
- repo: 'https://github.com/pre-commit/pre-commit-hooks'
rev: v6.0.0
Expand Down Expand Up @@ -45,22 +51,19 @@ repos:
args:
- '--check'
- '--diff'
- repo: 'https://github.com/pre-commit/mirrors-mypy'
rev: v2.1.0
# Runs the ty pinned in pyproject.toml, the same command the Type check
# workflow runs, so the version is declared in exactly one place. The hook
# takes no file arguments: an edit in one file can move diagnostics into
# another, so ty rechecks its whole configured scope every run.
- repo: local
hooks:
- id: mypy
args:
- '--show-error-codes'
- '--ignore-missing-imports'
- '--explicit-package-bases'
files: '^dbt/adapters'
- id: mypy
alias: mypy-check
stages:
- manual
args:
- '--show-error-codes'
- '--pretty'
- '--ignore-missing-imports'
- '--explicit-package-bases'
files: '^dbt/adapters'
- id: ty
name: ty
entry: uv run --frozen ty check
language: system
types_or:
- python
- pyi
pass_filenames: false
always_run: true
require_serial: true
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@

- Fix `dbt_sqlserver_use_dbt_transactions: True` (now the default) breaking two things that assumed the old autocommit-per-statement behavior: building an index with `build_options: {online: true}` or `{resumable: true}` on a fresh table or `table_refresh_method`'s rename-swap path (SQL Server rejects `RESUMABLE` inside a user transaction outright — `create_indexes` didn't split these out the way index reconciliation already did); and the `dbt_full_refresh_incomplete` crash marker on `full_refresh_build=prebuilt` and incremental full refreshes, which previously rode the same ambient transaction as the rebuild it's meant to survive, so a real failure mid-rebuild rolled the marker back along with everything else instead of leaving it in place to block the next normal run.

#### Under the hood

- Replace mypy with [`ty`](https://docs.astral.sh/ty/) for type checking, completing the move to a single Astral toolchain (Ruff for lint and format, `ty` for types). Same scope as before (`dbt/adapters`, unresolvable `dbt.*` imports ignored), configured in `pyproject.toml`. `ty` runs in a new Type check workflow rather than on pre-commit.ci, which blocks the network access it needs. [#712](https://github.com/dbt-msft/dbt-sqlserver/issues/712)

### v1.11.0

#### Features
Expand Down
12 changes: 6 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ dev: ## Installs adapter in develop mode along with development dependencies
@\
uv sync --all-extras && uv run pre-commit install

.PHONY: mypy
mypy: ## Runs mypy against staged changes for static type checking.
.PHONY: ty
ty: ## Runs ty over the adapter package for static type checking.
@\
uv run pre-commit run --hook-stage manual mypy-check
uv run pre-commit run --hook-stage manual ty

.PHONY: ruff
ruff: ## Runs ruff against staged changes to enforce style guide.
Expand All @@ -22,10 +22,10 @@ format: ## Runs ruff format against staged changes to enforce style guide.
uv run pre-commit run --hook-stage manual ruff-format-check -v

.PHONY: lint
lint: ## Runs ruff and mypy code checks against staged changes.
lint: ## Runs ruff and ty code checks against staged changes.
@status=0; \
uv run pre-commit run ruff-check-manual --hook-stage manual || status=1; \
uv run pre-commit run mypy-check --hook-stage manual || status=1; \
uv run pre-commit run ty --hook-stage manual || status=1; \
exit $$status

.PHONY: all
Expand All @@ -49,7 +49,7 @@ test: ## Runs unit tests and code checks against staged changes.
uv run pytest -n auto -ra -v tests/unit || status=1; \
uv run pre-commit run ruff-format-check --hook-stage manual || status=1; \
uv run pre-commit run ruff-check-manual --hook-stage manual || status=1; \
uv run pre-commit run mypy-check --hook-stage manual || status=1; \
uv run pre-commit run ty --hook-stage manual || status=1; \
exit $$status

.PHONY: server
Expand Down
10 changes: 8 additions & 2 deletions dbt/adapters/sqlserver/relation_configs/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -593,13 +593,19 @@ def index_config_changes(
SQLServerIndexConfig.parse_relation_results(rows_by_name[name])
)
changes.append(
SQLServerIndexConfigChange(action=RelationConfigChangeAction.drop, context=context)
SQLServerIndexConfigChange(
# `action` is inherited from RelationConfigChange, a dataclass a
# type checker cannot see; see [tool.ty.rules] in pyproject.toml.
action=RelationConfigChangeAction.drop, # ty: ignore[unknown-argument]
context=context,
)
)
for name, config in expected_by_name.items():
if name not in existing_names:
changes.append(
SQLServerIndexConfigChange(
action=RelationConfigChangeAction.create, context=config
action=RelationConfigChangeAction.create, # ty: ignore[unknown-argument]
context=config,
)
)
return changes, warnings
21 changes: 13 additions & 8 deletions dbt/adapters/sqlserver/sqlserver_adapter.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import datetime as _dt
from typing import Any, Dict, List, Optional, Tuple, Union
from typing import Any, Dict, List, Optional, Tuple, Type, Union

import agate
import dbt_common.exceptions
Expand Down Expand Up @@ -82,7 +82,8 @@ class SQLServerAdapter(SQLAdapter):
"""

ConnectionManager = SQLServerConnectionManager
Column = SQLServerColumn
# Annotated because __init__ swaps in the SQLServerColumnNative subclass.
Column: Type[SQLServerColumn] = SQLServerColumn
AdapterSpecificConfigs = SQLServerConfigs
Relation = SQLServerRelation

Expand Down Expand Up @@ -115,8 +116,12 @@ def __init__(self, config, mp_context=None):

@property
def _behavior_flags(self) -> List[BehaviorFlag]:
return [
{
# dbt-common declares BehaviorFlag's optional keys with a NotRequired
# that falls back to Optional under a try/except ImportError shim, so a
# type checker reads `source` and `docs_url` as required and rejects
# every flag below. The suppressions go stale once that shim is dropped.
return [ # ty: ignore[invalid-return-type]
{ # ty: ignore[missing-typed-dict-key]
"name": "dbt_sqlserver_use_default_schema_concat",
"default": False,
"description": (
Expand All @@ -128,7 +133,7 @@ def _behavior_flags(self) -> List[BehaviorFlag]:
"macro in your project instead."
),
},
{
{ # ty: ignore[missing-typed-dict-key]
"name": "dbt_sqlserver_disable_empty_relation_aliases",
"default": True,
"description": (
Expand All @@ -137,7 +142,7 @@ def _behavior_flags(self) -> List[BehaviorFlag]:
"out of alias generation temporarily for testing."
),
},
{
{ # ty: ignore[missing-typed-dict-key]
"name": "dbt_sqlserver_use_native_string_types",
"default": True,
"description": (
Expand All @@ -149,7 +154,7 @@ def _behavior_flags(self) -> List[BehaviorFlag]:
"and will be removed in a future release."
),
},
{
{ # ty: ignore[missing-typed-dict-key]
"name": "dbt_sqlserver_enable_safe_type_expansion",
"default": False,
"description": (
Expand All @@ -159,7 +164,7 @@ def _behavior_flags(self) -> List[BehaviorFlag]:
"and numeric(p,s) -> numeric(p2,s2) using alter column."
),
},
{
{ # ty: ignore[missing-typed-dict-key]
"name": "dbt_sqlserver_use_dbt_transactions",
"default": True,
"description": (
Expand Down
2 changes: 1 addition & 1 deletion dbt/adapters/sqlserver/sqlserver_relation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

@dataclass(frozen=True, eq=False, repr=False)
class SQLServerRelation(BaseRelation):
type: Optional[SQLServerRelationType] = None # type: ignore
type: Optional[SQLServerRelationType] = None
include_policy: SQLServerIncludePolicy = field(
default_factory=lambda: SQLServerIncludePolicy()
)
Expand Down
20 changes: 18 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ dev = [
"flaky",
"freezegun>=1.5.0,<2.0",
"ipdb",
"mypy==2.1.0",
"pre-commit",
"pytest",
"pytest-cov",
Expand All @@ -77,6 +76,9 @@ dev = [
"ruff",
"tox>=3.13",
"twine",
# Pinned exactly: this is the single source of the ty version, read by the
# pre-commit hook, `make ty` and the Type check workflow alike.
"ty==0.0.64",
]

[project.urls]
Expand Down Expand Up @@ -119,4 +121,18 @@ known-first-party = ["dbt"]
ban-relative-imports = "all"

[tool.ty.environment]
python = ".venv"
# Check against the oldest interpreter the adapter is built for, matching
# ruff's target-version, rather than whichever version .venv happens to hold.
python-version = "3.11"

[tool.ty.src]
# The adapter package only; the test suite is not type checked.
include = ["dbt/adapters"]

[tool.ty.rules]
# `dbt` is a namespace split across distributions: dbt-core and dbt-adapters
# own `dbt/` and `dbt/adapters/` in site-packages, this repo contributes
# `dbt/adapters/sqlserver` from the project root, and the two halves are joined
# at runtime by the editable install's import hook. No static checker can
# follow that, so every `dbt.*` import reads as unresolvable here.
unresolved-import = "ignore"
Loading
Loading