Skip to content

Commit f3d4c2e

Browse files
committed
Detect STR()+STR() as string concat and convert + to ||
_convert_string_concat now also matches CAST(... AS VARCHAR) adjacent to + operators, so STR([x]) + STR([y]) produces CAST(x AS VARCHAR) || CAST(y AS VARCHAR) instead of using + which fails in DuckDB/Postgres for text operands.
1 parent 0ddf836 commit f3d4c2e

2 files changed

Lines changed: 21 additions & 6 deletions

File tree

sidemantic/adapters/tableau.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -417,13 +417,19 @@ def _translate_iif(text: str) -> str:
417417
def _convert_string_concat(text: str) -> str:
418418
"""Convert Tableau's + string concatenation to SQL ||.
419419
420-
Replaces + with || when at least one adjacent operand is a string literal.
421-
Uses a simple heuristic: if a + is preceded or followed by a single-quoted
422-
string (possibly with whitespace), replace it with ||.
420+
Replaces + with || when at least one adjacent operand is a string-producing
421+
expression: a string literal ('...') or a CAST(... AS VARCHAR).
423422
"""
424-
# Match: 'string' + or + 'string'
425-
result = re.sub(r"('\s*)\+(\s*)", r"\1||\2", text)
426-
result = re.sub(r"(\s*)\+(\s*')", r"\1||\2", result)
423+
result = text
424+
prev = None
425+
while prev != result:
426+
prev = result
427+
# 'string' + ... or ... + 'string'
428+
result = re.sub(r"('\s*)\+(\s*)", r"\1||\2", result)
429+
result = re.sub(r"(\s*)\+(\s*')", r"\1||\2", result)
430+
# CAST(... AS VARCHAR) + ... or ... + CAST(... AS VARCHAR)
431+
result = re.sub(r"(AS\s+VARCHAR\)\s*)\+(\s*)", r"\1||\2", result, flags=re.IGNORECASE)
432+
result = re.sub(r"(\s*)\+(\s*CAST\()", r"\1||\2", result, flags=re.IGNORECASE)
427433
return result
428434

429435

tests/adapters/tableau/test_formula.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,15 @@ def test_string_concat_plus_to_pipes():
267267
assert "+" not in sql
268268

269269

270+
def test_string_concat_str_functions():
271+
"""STR() + STR() uses || not +."""
272+
sql, ok = _translate_formula("STR([x]) + STR([y])")
273+
assert ok
274+
assert "||" in sql
275+
assert "+" not in sql
276+
assert "CAST" in sql
277+
278+
270279
def test_arithmetic_plus_preserved():
271280
"""Arithmetic + is NOT converted to ||."""
272281
sql, ok = _translate_formula("[x] + [y]")

0 commit comments

Comments
 (0)