Skip to content

Commit 83507b8

Browse files
Fix Trino/Presto SHA256/SHA512 hex rendering under the pinned sqlglot
Bare SHA256(varchar) is a type error on Trino and binary semantics on Presto, so the macro now builds LOWER(TO_HEX(SHA256(TO_UTF8(...)))) itself for the Presto family, mirroring those generators' MD5 handling. A cached probe checks whether the dialect already renders exp.SHA2 in the hex form, so once the sqlglot pin includes tobymao/sqlglot#7824 the macro defers to the native rendering and never wraps twice. Adds direct Trino and Presto SHA256/SHA512 output assertions that hold on both sides of the pin bump. Signed-off-by: Pawan Singh Kapkoti <42340841+Pawansingh3889@users.noreply.github.com>
1 parent fbc2f3a commit 83507b8

2 files changed

Lines changed: 57 additions & 4 deletions

File tree

sqlmesh/core/macros.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,9 +982,40 @@ def generate_surrogate_key(
982982
# not a binary digest, on every dialect.
983983
func = exp.SHA2(this=func.this, length=func.args.get("length"))
984984

985+
if isinstance(func, exp.SHA2) and _sha2_renders_binary(evaluator.dialect):
986+
# Presto/Trino render a bare SHA256(varchar) for exp.SHA2 on sqlglot
987+
# versions without tobymao/sqlglot#7824: a type error on Trino, and
988+
# binary rather than string semantics where it runs. Build the
989+
# hex-string form explicitly, mirroring what those generators do for
990+
# MD5: LOWER(TO_HEX(SHA256(TO_UTF8(...)))). The probe keeps this
991+
# branch inert once sqlglot renders the hex form natively, so the
992+
# expression is never wrapped twice.
993+
return exp.Lower(
994+
this=exp.Hex(
995+
this=exp.SHA2(
996+
this=exp.Encode(this=func.this, charset=exp.Literal.string("utf-8")),
997+
length=func.args.get("length"),
998+
)
999+
)
1000+
)
1001+
9851002
return func
9861003

9871004

1005+
@lru_cache(maxsize=None)
1006+
def _sha2_renders_binary(dialect: DialectType) -> bool:
1007+
"""Whether this dialect renders exp.SHA2 as a bare binary-semantics call.
1008+
1009+
Only the Presto family models string and binary hashes separately; other
1010+
dialects' SHA256(varchar) already returns a hex string.
1011+
"""
1012+
dialect_name = (str(dialect) if dialect else "").split(",")[0].strip().lower()
1013+
if dialect_name not in ("presto", "trino", "athena"):
1014+
return False
1015+
probe = exp.SHA2(this=exp.column("_sqlmesh_probe"), length=exp.Literal.number(256))
1016+
return "TO_HEX" not in probe.sql(dialect=dialect)
1017+
1018+
9881019
@macro()
9891020
def safe_add(_: MacroEvaluator, *fields: exp.Expr) -> exp.Case:
9901021
"""Adds numbers together, substitutes nulls for 0s and only returns null if all fields are null.

tests/core/test_macros.py

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,29 @@ def render(dialect: str, hash_function: str) -> str:
12251225
render("trino", "MD5")
12261226
== "SELECT LOWER(TO_HEX(MD5(TO_UTF8(CAST(COALESCE(CAST(a AS VARCHAR), '_sqlmesh_surrogate_key_null_') AS VARCHAR))))) FROM foo"
12271227
)
1228-
# Trino/Presto render SHA256/SHA512 surrogate keys as
1229-
# LOWER(TO_HEX(SHA256(TO_UTF8(...)))) once sqlglot ships
1230-
# tobymao/sqlglot#7824; the string assertions for that belong with the
1231-
# sqlglot version bump.
1228+
1229+
# The reported bug (#5871): Trino/Presto SHA256/SHA512 surrogate keys must
1230+
# be the hex-string form, not a bare SHA256(varchar). The macro-side
1231+
# fallback produces it under the current sqlglot pin; once sqlglot renders
1232+
# exp.SHA2 this way natively (tobymao/sqlglot#7824), the probe disables
1233+
# the fallback and these assertions hold unchanged.
1234+
for _dialect in ("trino", "presto"):
1235+
assert (
1236+
render(_dialect, "SHA256")
1237+
== "SELECT LOWER(TO_HEX(SHA256(TO_UTF8(CAST(COALESCE(CAST(a AS VARCHAR), '_sqlmesh_surrogate_key_null_') AS VARCHAR))))) FROM foo"
1238+
)
1239+
assert (
1240+
render(_dialect, "SHA512")
1241+
== "SELECT LOWER(TO_HEX(SHA512(TO_UTF8(CAST(COALESCE(CAST(a AS VARCHAR), '_sqlmesh_surrogate_key_null_') AS VARCHAR))))) FROM foo"
1242+
)
1243+
1244+
# The fallback is scoped to the Presto family: dialects whose bare
1245+
# SHA256(varchar) already returns a hex string are left to sqlglot.
1246+
from sqlmesh.core.macros import _sha2_renders_binary
1247+
1248+
assert not _sha2_renders_binary("duckdb")
1249+
assert not _sha2_renders_binary("bigquery")
1250+
assert (
1251+
render("snowflake", "SHA256")
1252+
== "SELECT SHA256(CONCAT(COALESCE(CAST(a AS VARCHAR), '_sqlmesh_surrogate_key_null_'))) FROM foo"
1253+
)

0 commit comments

Comments
 (0)