Skip to content

Commit 088710b

Browse files
authored
fix: Validate and autoformat query tag values (#1253)
<!-- Please review our pull request review process in CONTRIBUTING.md before your proceed. --> Resolves # <!--- Include the number of the issue addressed by this PR above if applicable. Example: resolves #1234 Please review our pull request review process in CONTRIBUTING.md before your proceed. --> ### Description - Auto-escape special characters in query tag values with a warning message - Throw error when query tag value (after applying escapes) exceeds 128 length limit ### Checklist - [ ] I have run this code in development and it appears to resolve the stated issue - [ ] This PR includes tests, or tests are not required/relevant for this PR - [ ] I have updated the `CHANGELOG.md` and added information about my change to the "dbt-databricks next" section.
1 parent d68e1f3 commit 088710b

3 files changed

Lines changed: 76 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
## dbt-databricks 1.11.1 (TBD)
22

3+
### Under the hood
4+
5+
- Add validation for query tag value length and auto-escape special characters
6+
37
## dbt-databricks 1.11.0 (Oct 29, 2025)
48

59
### Features

dbt/adapters/databricks/utils.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,24 @@ def validate_query_tags(tags: dict[str, str], source: str = "") -> None:
151151
f"Reserved keys are: {', '.join(sorted(QueryTagsUtils.RESERVED_KEYS))}"
152152
)
153153

154+
# Escape commas, colons, and backslashes in tag values
155+
for key in tags.keys():
156+
value = tags[key]
157+
if re.search(r"[\\,:]", value):
158+
logger.warning(
159+
f"{source_prefix}Query tag value for key '{key}' contains unescaped "
160+
f"character(s): {value}. Escaping..."
161+
)
162+
tags[key] = value.replace("\\", "\\\\").replace(",", "\\,").replace(":", "\\:")
163+
164+
# Validate that no tag value exceeds 128 characters
165+
long_values = {k: v for k, v in tags.items() if len(v) > 128}
166+
if long_values:
167+
raise DbtValidationError(
168+
f"{source_prefix}Query tag values must be at most 128 characters. "
169+
f"Following keys have values exceeding the limit: {', '.join(long_values.keys())}."
170+
)
171+
154172
# Check tag limit
155173
if len(tags) > QueryTagsUtils.MAX_TAGS:
156174
raise DbtValidationError(

tests/unit/test_query_tags.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,60 @@ def test_validate_query_tags_valid(self):
8484
# Should not raise any exception
8585
QueryTagsUtils.validate_query_tags(tags)
8686

87+
def test_validate_query_tags_escapes_comma(self):
88+
"""Test that commas in tag values are escaped."""
89+
tags = {"team": "marketing,sales"}
90+
QueryTagsUtils.validate_query_tags(tags)
91+
assert tags["team"] == "marketing\\,sales"
92+
93+
def test_validate_query_tags_escapes_colon(self):
94+
"""Test that colons in tag values are escaped."""
95+
tags = {"description": "project:alpha"}
96+
QueryTagsUtils.validate_query_tags(tags)
97+
assert tags["description"] == "project\\:alpha"
98+
99+
def test_validate_query_tags_escapes_backslash(self):
100+
"""Test that backslashes in tag values are escaped."""
101+
tags = {"path": "folder\\subfolder"}
102+
QueryTagsUtils.validate_query_tags(tags)
103+
assert tags["path"] == "folder\\\\subfolder"
104+
105+
def test_validate_query_tags_escapes_multiple_special_chars(self):
106+
"""Test that multiple special characters are all escaped."""
107+
tags = {"complex": "value:with,comma\\and\\backslash"}
108+
QueryTagsUtils.validate_query_tags(tags)
109+
assert tags["complex"] == "value\\:with\\,comma\\\\and\\\\backslash"
110+
111+
def test_validate_query_tags_multiple_values_too_long(self):
112+
tags = {
113+
"long_tag_1": "a" * 129,
114+
"short_tag": "ok",
115+
"long_tag_2": "b" * 130,
116+
}
117+
expected_msg = (
118+
"Query tag values must be at most 128 characters. "
119+
"Following keys have values exceeding the limit: "
120+
"long_tag_1, long_tag_2."
121+
)
122+
with pytest.raises(DbtValidationError, match=expected_msg):
123+
QueryTagsUtils.validate_query_tags(tags)
124+
125+
def test_validate_query_tags_value_after_escaping_too_long(self):
126+
"""Test validation fails when tag value exceeds 128 chars after escaping."""
127+
# Create a value that's 127 characters but will exceed 128 after escaping
128+
# We need at least 64 commas (each comma becomes \\, adding 1 char)
129+
value_with_commas = ",".join(["a"] * 64) # Creates "a,a,a..." with 63 commas
130+
# After escaping, each comma becomes \\, so length increases by 63
131+
tags = {"tag_with_commas": value_with_commas}
132+
133+
# First it escapes, then it validates length
134+
expected_msg = (
135+
"Query tag values must be at most 128 characters. "
136+
"Following keys have values exceeding the limit: tag_with_commas."
137+
)
138+
with pytest.raises(DbtValidationError, match=expected_msg):
139+
QueryTagsUtils.validate_query_tags(tags)
140+
87141
def test_merge_query_tags_precedence(self):
88142
"""Test that model tags override connection tags."""
89143
connection_tags = {"team": "marketing", "cost_center": "3000"}

0 commit comments

Comments
 (0)