Skip to content

Commit f387112

Browse files
authored
fix(parser): robust CONCAT_WS (#7544)
* fix(parser): robust CONCAT_WS * fix double value * revert comment * fix * remove sf * add trino
1 parent 0bd0c46 commit f387112

12 files changed

Lines changed: 35 additions & 12 deletions

File tree

sqlglot/dialects/dialect.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,9 @@ class Dialect(metaclass=_Dialect):
399399
CONCAT_COALESCE = False
400400
"""A `NULL` arg in `CONCAT` yields `NULL` by default, but in some dialects it yields an empty string."""
401401

402+
CONCAT_WS_COALESCE = False
403+
"""A `NULL` arg in `CONCAT_WS` yields `NULL` by default, but in some dialects it is skipped."""
404+
402405
HEX_LOWERCASE = False
403406
"""Whether the `HEX` function returns a lowercase hexadecimal string."""
404407

sqlglot/dialects/dremio.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class Dremio(Dialect):
1010
SUPPORTS_USER_DEFINED_TYPES = False
1111
CONCAT_COALESCE = True
12+
CONCAT_WS_COALESCE = True
1213
TYPED_DIVISION = True
1314
NULL_ORDERING = "nulls_are_last"
1415
SUPPORTS_VALUES_DEFAULT = False

sqlglot/dialects/drill.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class Drill(Dialect):
1717
SUPPORTS_USER_DEFINED_TYPES = False
1818
TYPED_DIVISION = True
1919
CONCAT_COALESCE = True
20+
CONCAT_WS_COALESCE = True
2021

2122
TIME_MAPPING = {
2223
"y": "%Y",

sqlglot/dialects/duckdb.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class DuckDB(Dialect):
1919
SAFE_DIVISION = True
2020
INDEX_OFFSET = 1
2121
CONCAT_COALESCE = True
22+
CONCAT_WS_COALESCE = True
2223
SUPPORTS_ORDER_BY_ALL = True
2324
SUPPORTS_FIXED_SIZE_ARRAYS = True
2425
STRICT_JSON_PATH_SYNTAX = False

sqlglot/dialects/hive.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Hive(Dialect):
2020
IDENTIFIERS_CAN_START_WITH_DIGIT = True
2121
SUPPORTS_USER_DEFINED_TYPES = False
2222
SAFE_DIVISION = True
23+
CONCAT_WS_COALESCE = True
2324
ARRAY_AGG_INCLUDES_NULLS = None
2425
REGEXP_EXTRACT_DEFAULT_GROUP = 1
2526
ALTER_TABLE_SUPPORTS_CASCADE = True

sqlglot/dialects/postgres.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ class Postgres(Dialect):
1111
INDEX_OFFSET = 1
1212
TYPED_DIVISION = True
1313
CONCAT_COALESCE = True
14+
CONCAT_WS_COALESCE = True
1415
NULL_ORDERING = "nulls_are_large"
1516
TIME_FORMAT = "'YYYY-MM-DD HH24:MI:SS'"
1617
TABLESAMPLE_SIZE_IS_PERCENT = True

sqlglot/dialects/trino.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
class Trino(Presto):
1010
SUPPORTS_USER_DEFINED_TYPES = False
1111
LOG_BASE_FIRST = True
12+
CONCAT_WS_COALESCE = True
1213

1314
class Tokenizer(Presto.Tokenizer):
1415
KEYWORDS = {

sqlglot/dialects/tsql.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class TSQL(Dialect):
1515
LOG_BASE_FIRST = False
1616
TYPED_DIVISION = True
1717
CONCAT_COALESCE = True
18+
CONCAT_WS_COALESCE = True
1819
NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_INSENSITIVE
1920
ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN = False
2021

sqlglot/generator.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3397,7 +3397,13 @@ def convert_concat_args(self, expression: exp.Func) -> list[exp.Expr]:
33973397
if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"):
33983398
args = [exp.cast(e, exp.DType.TEXT) for e in args]
33993399

3400-
if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"):
3400+
concat_coalesce = (
3401+
self.dialect.CONCAT_WS_COALESCE
3402+
if isinstance(expression, exp.ConcatWs)
3403+
else self.dialect.CONCAT_COALESCE
3404+
)
3405+
3406+
if not concat_coalesce and expression.args.get("coalesce"):
34013407

34023408
def _wrap_with_coalesce(e: exp.Expr) -> exp.Expr:
34033409
if not e.type:
@@ -3432,8 +3438,8 @@ def concat_sql(self, expression: exp.Concat) -> str:
34323438
return self.func("CONCAT", *expressions)
34333439

34343440
def concatws_sql(self, expression: exp.ConcatWs) -> str:
3435-
if self.dialect.CONCAT_COALESCE and not expression.args.get("coalesce"):
3436-
# Dialect's CONCAT_WS function coalesces NULLs to empty strings, but the expression does not.
3441+
if self.dialect.CONCAT_WS_COALESCE and not expression.args.get("coalesce"):
3442+
# Dialect's CONCAT_WS function skips NULL args, but the expression does not.
34373443
# Wrap the entire call in a CASE expression that returns NULL if any input IS NULL.
34383444
all_args = expression.expressions
34393445
expression.set("coalesce", True)

sqlglot/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class Parser:
342342
"CONCAT_WS": lambda args, dialect: exp.ConcatWs(
343343
expressions=args,
344344
safe=not dialect.STRICT_STRING_CONCAT,
345-
coalesce=dialect.CONCAT_COALESCE,
345+
coalesce=dialect.CONCAT_WS_COALESCE,
346346
),
347347
"CONVERT_TIMEZONE": build_convert_timezone,
348348
"DATE_TO_DATE_STR": lambda args: exp.Cast(

0 commit comments

Comments
 (0)