From 1142982040e753bb70318376e68cb0636b9454e4 Mon Sep 17 00:00:00 2001 From: Ben Knight Date: Wed, 20 May 2026 12:08:46 +0000 Subject: [PATCH 1/2] Test interaction between TABLOCK (#640) and query_options (#613). A contract-enforced table model with query_options emits both `WITH (TABLOCK)` on the INSERT target and the `OPTION (...)` query hint on the SELECT side. This test only makes sense once both features are present, so it lives on a dedicated branch that merges #640 and #613. --- .../test_interaction_tablock_query_options.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 tests/functional/adapter/mssql/test_interaction_tablock_query_options.py diff --git a/tests/functional/adapter/mssql/test_interaction_tablock_query_options.py b/tests/functional/adapter/mssql/test_interaction_tablock_query_options.py new file mode 100644 index 000000000..9ba7be47c --- /dev/null +++ b/tests/functional/adapter/mssql/test_interaction_tablock_query_options.py @@ -0,0 +1,65 @@ +"""Interaction coverage for #613 (query_options) + #640 (TABLOCK on contract INSERT). + +Each feature is exercised by its own test suite in isolation. This file verifies +that when both are present, a contract-enforced table model with query_options +emits both `WITH (TABLOCK)` (the INSERT target hint) and the `OPTION (...)` +query hint without either path interfering with the other. + +The test lives on a dedicated branch that merges both feature branches because +neither individual branch can exercise the combined output: #640 alone ignores +query_options config, and #613 alone uses the non-contract `SELECT * INTO` +path which has no TABLOCK to emit. +""" + +import os + +import pytest + +from dbt.tests.util import run_dbt + +model_sql = """ +{{ config(materialized='table', query_options={'MAXDOP': 1}) }} +select 1 as id +""" + +model_yml = """ +version: 2 +models: + - name: contract_with_options + config: + contract: + enforced: true + columns: + - name: id + data_type: int +""" + + +class TestContractTableWithQueryOptions: + @pytest.fixture(scope="class") + def models(self): + return { + "contract_with_options.sql": model_sql, + "schema.yml": model_yml, + } + + def test_both_hints_in_compiled_sql(self, project): + results = run_dbt(["run"]) + assert len(results) == 1 + assert results[0].status == "success" + + target_dir = os.path.join(project.project_root, "target", "run") + for root, _dirs, files in os.walk(target_dir): + if "contract_with_options.sql" in files: + with open(os.path.join(root, "contract_with_options.sql"), "r") as f: + sql = f.read() + break + else: + raise AssertionError("Could not find compiled contract_with_options.sql") + + # TABLOCK is the INSERT target hint (#640). + assert "WITH (TABLOCK)" in sql + # MAXDOP comes from query_options (#613). + assert "MAXDOP 1" in sql + # The default LABEL is always present. + assert "LABEL =" in sql From 82d2de3d24c36a8d52fd9506fdee9ad93be5b7d8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 01:58:06 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/functional/adapter/mssql/test_query_options.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/functional/adapter/mssql/test_query_options.py b/tests/functional/adapter/mssql/test_query_options.py index 55dcd82d9..8e6339abe 100644 --- a/tests/functional/adapter/mssql/test_query_options.py +++ b/tests/functional/adapter/mssql/test_query_options.py @@ -431,8 +431,7 @@ class TestApplyLabelBackwardCompat: @pytest.fixture(scope="class") def macros(self): - return { - "verify_apply_label.sql": """ + return {"verify_apply_label.sql": """ {% macro verify_apply_label() %} {%- set result = apply_label() -%} {{ log("apply_label returned: " ~ result, info=True) }} @@ -443,8 +442,7 @@ def macros(self): {{ exceptions.raise_compiler_error("apply_label() must not emit query_options hints") }} {%- endif -%} {% endmacro %} -""" - } +"""} def test_apply_label_callable_and_label_only(self, project): # run-operation will fail (non-zero exit) if apply_label is undefined