From 5ea17a7a570b59e9bce64f1bed4bec2cd15cb8a7 Mon Sep 17 00:00:00 2001 From: fivetran-amrutabhimsenayachit Date: Tue, 26 May 2026 12:55:26 -0400 Subject: [PATCH 1/2] feat(duckdb): Add transpilation support for BOOLEAN and TEXT cases of TRY_CAST function --- sqlglot-integration-tests | 2 +- sqlglot/generators/duckdb.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/sqlglot-integration-tests b/sqlglot-integration-tests index 520efdb573..9816f9b42d 160000 --- a/sqlglot-integration-tests +++ b/sqlglot-integration-tests @@ -1 +1 @@ -Subproject commit 520efdb573b6da4d4aa5ea372bb3d49af6a3ddc6 +Subproject commit 9816f9b42dd90d60243a55b3ceae2f7f6db2c79e diff --git a/sqlglot/generators/duckdb.py b/sqlglot/generators/duckdb.py index 49a4d486b5..2b4aeae855 100644 --- a/sqlglot/generators/duckdb.py +++ b/sqlglot/generators/duckdb.py @@ -4326,6 +4326,26 @@ def round_sql(self, expression: exp.Round) -> str: return self.func(func, this, decimals, truncate) + def trycast_sql(self, expression: exp.TryCast) -> str: + if not expression.args.get("requires_string"): + return super().trycast_sql(expression) + + src, to, to_type = expression.this, expression.to, expression.to.this + + if to_type == exp.DType.BOOLEAN: + return _to_boolean_sql(self, exp.ToBoolean(this=src.copy(), safe=True)) + elif to_type in exp.DataType.TEXT_TYPES and to.expressions: + return self.sql( + exp.case() + .when( + exp.LTE(this=exp.func("LENGTH", src.copy()), expression=to.expressions[0].this), + exp.cast(src.copy(), "TEXT"), + ) + .else_(exp.Null()) + ) + + return super().trycast_sql(expression) + def strtok_sql(self, expression: exp.Strtok) -> str: string_arg = expression.this delimiter_arg = expression.args.get("delimiter") From 42ac8f44e71cc4ab81a794e8bdefe5af24cb68cd Mon Sep 17 00:00:00 2001 From: fivetran-amrutabhimsenayachit Date: Thu, 28 May 2026 11:24:26 -0400 Subject: [PATCH 2/2] feat(duckdb): Address review comments --- sqlglot/expressions/functions.py | 2 +- sqlglot/generators/duckdb.py | 12 +++++++----- sqlglot/parser.py | 4 ++++ 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sqlglot/expressions/functions.py b/sqlglot/expressions/functions.py index e0442205b7..2d33343466 100644 --- a/sqlglot/expressions/functions.py +++ b/sqlglot/expressions/functions.py @@ -70,7 +70,7 @@ def is_type(self, *dtypes: DATA_TYPE) -> bool: class TryCast(Cast): - arg_types = {**Cast.arg_types, "requires_string": False} + arg_types = {**Cast.arg_types, "requires_string": False, "dialect_cast": False} class JSONCast(Cast): diff --git a/sqlglot/generators/duckdb.py b/sqlglot/generators/duckdb.py index 2b4aeae855..4d51ecc216 100644 --- a/sqlglot/generators/duckdb.py +++ b/sqlglot/generators/duckdb.py @@ -4327,19 +4327,21 @@ def round_sql(self, expression: exp.Round) -> str: return self.func(func, this, decimals, truncate) def trycast_sql(self, expression: exp.TryCast) -> str: - if not expression.args.get("requires_string"): + if not expression.args.get("dialect_cast"): return super().trycast_sql(expression) - src, to, to_type = expression.this, expression.to, expression.to.this + src = expression.this + to = expression.to + to_type = to.this if to_type == exp.DType.BOOLEAN: - return _to_boolean_sql(self, exp.ToBoolean(this=src.copy(), safe=True)) + return _to_boolean_sql(self, exp.ToBoolean(this=src, safe=True)) elif to_type in exp.DataType.TEXT_TYPES and to.expressions: return self.sql( exp.case() .when( - exp.LTE(this=exp.func("LENGTH", src.copy()), expression=to.expressions[0].this), - exp.cast(src.copy(), "TEXT"), + exp.LTE(this=exp.func("LENGTH", src), expression=to.expressions[0].this), + exp.cast(src, "TEXT"), ) .else_(exp.Null()) ) diff --git a/sqlglot/parser.py b/sqlglot/parser.py index 73e0520b56..262f2f698b 100644 --- a/sqlglot/parser.py +++ b/sqlglot/parser.py @@ -9928,6 +9928,10 @@ def build_cast(self, strict: bool, **kwargs) -> exp.Cast: if exp_class == exp.TryCast: kwargs["requires_string"] = self.dialect.TRY_CAST_REQUIRES_STRING + if self.dialect.TRY_CAST_REQUIRES_STRING and (to := kwargs.get("to")): + kwargs["dialect_cast"] = to.this == exp.DataType.Type.BOOLEAN or ( + to.this in exp.DataType.TEXT_TYPES and bool(to.expressions) + ) return self.expression(exp_class(**kwargs))