From e501cafae4f302407006649ac352da0fca149528 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Fri, 7 Mar 2025 16:06:02 -0300 Subject: [PATCH 1/4] fix(snowflake): add support for FROM_HEX_BINARY The [FROM_HEX_BINARY](https://docs.snowflake.com/en/sql-reference/functions/hex_decode_binary) frunction is equivalent to the `Unhex` function that is already supported by sqlglot. To support it, we just had to add the map in snowflake and add the transform in duckdb. Partially solves #4852, we still need to add support for `FROM_HEX_STRING`. --- sqlglot/dialects/duckdb.py | 1 + sqlglot/dialects/snowflake.py | 2 ++ tests/dialects/test_snowflake.py | 9 +++++++++ 3 files changed, 12 insertions(+) diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py index d56201fd0b..2da20edd2e 100644 --- a/sqlglot/dialects/duckdb.py +++ b/sqlglot/dialects/duckdb.py @@ -666,6 +666,7 @@ class Generator(generator.Generator): exp.cast(e.expression, exp.DataType.Type.TIMESTAMP), exp.cast(e.this, exp.DataType.Type.TIMESTAMP), ), + exp.Unhex: rename_func("FROM_HEX"), exp.UnixToStr: lambda self, e: self.func( "STRFTIME", self.func("TO_TIMESTAMP", e.this), self.format_time(e) ), diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py index 4561a1bf5d..2bd3dfd0ef 100644 --- a/sqlglot/dialects/snowflake.py +++ b/sqlglot/dialects/snowflake.py @@ -431,6 +431,7 @@ class Parser(parser.Parser): this=seq_get(args, 0), expression=seq_get(args, 1), max_dist=seq_get(args, 2) ), "FLATTEN": exp.Explode.from_arg_list, + "FROM_HEX_BINARY": exp.Unhex.from_arg_list, "GET_PATH": lambda args, dialect: exp.JSONExtract( this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1)) ), @@ -1030,6 +1031,7 @@ class Generator(generator.Generator): exp.TsOrDsToTime: lambda self, e: self.func( "TRY_TO_TIME" if e.args.get("safe") else "TO_TIME", e.this, self.format_time(e) ), + exp.Unhex: rename_func("FROM_HEX_BINARY"), exp.UnixToTime: rename_func("TO_TIMESTAMP"), exp.Uuid: rename_func("UUID_STRING"), exp.VarMap: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"), diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py index a4b252fab6..23ca35083b 100644 --- a/tests/dialects/test_snowflake.py +++ b/tests/dialects/test_snowflake.py @@ -998,6 +998,15 @@ def test_snowflake(self): self.validate_identity("CREATE TABLE t (id INT PRIMARY KEY AUTOINCREMENT)") + self.validate_all( + "SELECT FROM_HEX_BINARY('65')", + write={ + "bigquery": "SELECT FROM_HEX('65')", + "duckdb": "SELECT FROM_HEX('65')", + "snowflake": "SELECT FROM_HEX_BINARY('65')", + }, + ) + def test_null_treatment(self): self.validate_all( r"SELECT FIRST_VALUE(TABLE1.COLUMN1) OVER (PARTITION BY RANDOM_COLUMN1, RANDOM_COLUMN2 ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) AS MY_ALIAS FROM TABLE1", From 51d84de59f4ddd4bd0996825cd8526d73334e7a6 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Fri, 7 Mar 2025 18:20:23 -0300 Subject: [PATCH 2/4] fix function name It was supposed to be HEX_DECODE_BINARY as seen in the docs https://docs.snowflake.com/en/sql-reference/functions/hex_decode_binary --- sqlglot/dialects/snowflake.py | 4 ++-- tests/dialects/test_snowflake.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py index 2bd3dfd0ef..cf193345c5 100644 --- a/sqlglot/dialects/snowflake.py +++ b/sqlglot/dialects/snowflake.py @@ -431,7 +431,7 @@ class Parser(parser.Parser): this=seq_get(args, 0), expression=seq_get(args, 1), max_dist=seq_get(args, 2) ), "FLATTEN": exp.Explode.from_arg_list, - "FROM_HEX_BINARY": exp.Unhex.from_arg_list, + "HEX_DECODE_BINARY": exp.Unhex.from_arg_list, "GET_PATH": lambda args, dialect: exp.JSONExtract( this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1)) ), @@ -1031,7 +1031,7 @@ class Generator(generator.Generator): exp.TsOrDsToTime: lambda self, e: self.func( "TRY_TO_TIME" if e.args.get("safe") else "TO_TIME", e.this, self.format_time(e) ), - exp.Unhex: rename_func("FROM_HEX_BINARY"), + exp.Unhex: rename_func("HEX_DECODE_BINARY"), exp.UnixToTime: rename_func("TO_TIMESTAMP"), exp.Uuid: rename_func("UUID_STRING"), exp.VarMap: lambda self, e: var_map_sql(self, e, "OBJECT_CONSTRUCT"), diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py index 23ca35083b..f46f56c2b4 100644 --- a/tests/dialects/test_snowflake.py +++ b/tests/dialects/test_snowflake.py @@ -999,11 +999,11 @@ def test_snowflake(self): self.validate_identity("CREATE TABLE t (id INT PRIMARY KEY AUTOINCREMENT)") self.validate_all( - "SELECT FROM_HEX_BINARY('65')", + "SELECT HEX_DECODE_BINARY('65')", write={ "bigquery": "SELECT FROM_HEX('65')", "duckdb": "SELECT FROM_HEX('65')", - "snowflake": "SELECT FROM_HEX_BINARY('65')", + "snowflake": "SELECT HEX_DECODE_BINARY('65')", }, ) From 3afc3ff2d7430cf5348efeadf850bec506bba579 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Fri, 7 Mar 2025 18:21:46 -0300 Subject: [PATCH 3/4] Keep HEX_DECODE_BINARY in its sorted position --- sqlglot/dialects/snowflake.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py index cf193345c5..32e0b55464 100644 --- a/sqlglot/dialects/snowflake.py +++ b/sqlglot/dialects/snowflake.py @@ -431,10 +431,10 @@ class Parser(parser.Parser): this=seq_get(args, 0), expression=seq_get(args, 1), max_dist=seq_get(args, 2) ), "FLATTEN": exp.Explode.from_arg_list, - "HEX_DECODE_BINARY": exp.Unhex.from_arg_list, "GET_PATH": lambda args, dialect: exp.JSONExtract( this=seq_get(args, 0), expression=dialect.to_json_path(seq_get(args, 1)) ), + "HEX_DECODE_BINARY": exp.Unhex.from_arg_list, "IFF": exp.If.from_arg_list, "LAST_DAY": lambda args: exp.LastDay( this=seq_get(args, 0), unit=map_date_part(seq_get(args, 1)) From 2a3c27347240026500c56e79abd772e06eb67cb6 Mon Sep 17 00:00:00 2001 From: Sebastian Kreft Date: Fri, 7 Mar 2025 18:41:30 -0300 Subject: [PATCH 4/4] remove from_hex alias from duckdb --- sqlglot/dialects/duckdb.py | 1 - tests/dialects/test_snowflake.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py index 2da20edd2e..d56201fd0b 100644 --- a/sqlglot/dialects/duckdb.py +++ b/sqlglot/dialects/duckdb.py @@ -666,7 +666,6 @@ class Generator(generator.Generator): exp.cast(e.expression, exp.DataType.Type.TIMESTAMP), exp.cast(e.this, exp.DataType.Type.TIMESTAMP), ), - exp.Unhex: rename_func("FROM_HEX"), exp.UnixToStr: lambda self, e: self.func( "STRFTIME", self.func("TO_TIMESTAMP", e.this), self.format_time(e) ), diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py index f46f56c2b4..a21dc4db0b 100644 --- a/tests/dialects/test_snowflake.py +++ b/tests/dialects/test_snowflake.py @@ -1002,7 +1002,7 @@ def test_snowflake(self): "SELECT HEX_DECODE_BINARY('65')", write={ "bigquery": "SELECT FROM_HEX('65')", - "duckdb": "SELECT FROM_HEX('65')", + "duckdb": "SELECT UNHEX('65')", "snowflake": "SELECT HEX_DECODE_BINARY('65')", }, )