Repository: tobymao/sqlglot

Issue:
`div0()` parsing bug in `parse_one(sql, read=sqlglot.Dialects.SNOWFLAKE)`
The following snippet:

```python
import sqlglot

sql = """
select
    div0(2 - 1, 1)
;
"""

sql = sqlglot.parse_one(sql, read=sqlglot.Dialects.SNOWFLAKE)

print(sql)
```

results in:

```sql
select
    case when 1 = 0 and not 2 - 1 is null then
        0
    else
        2 - 1 / 1
    end
```

which is not right and results in incorrect calculations because the division operator `/` takes priority. The `else` clause should be `(2 - 1) / 1` instead.

Thanks for looking into it!

https://docs.snowflake.com/en/sql-reference/functions/div0

Hints from issue comments:
@VaggelisD: Hey @diogo-fernan,

As a heads up, `print(sql)` generates a query in the "base" dialect which can yield incorrect results; To get back a Snowflake query, you should instead use `.sql('snowflake')`, i.e:

```
>>> ast = sqlglot.parse_one("select div0(2 - 1, 1)", dialect="snowflake")
>>> ast.sql('snowflake')
'SELECT IFF(1 = 0 AND NOT 2 - 1 IS NULL, 0, 2 - 1 / 1)'
```

However, the order of operations is still incorrect, will fix this shortly.@diogo-fernan: Roger that and thanks for working on the fix @VaggelisD.@diogo-fernan: @VaggelisD perhaps you also want to add `div0null()` to the parsing list while you are at it. It is currently not parsed by SQLGlot.

https://docs.snowflake.com/en/sql-reference/functions/div0null@VaggelisD: SQLGlot wraps functions that it cannot properly parse yet in a placeholder node (`exp.Anonymous`) which preserves the roundtrip i.e Snowflake -> Snowflake. 

Did `DIV0NULL` come up during transpilation to a different dialect or did you notice any issues while generating back Snowflake queries? It should be a relatively low lift if you could take a stab at it, but I'll go ahead and add it to our backlog. 

Solution patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
index 80bd7bd188..97b879082a 100644
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -106,11 +106,14 @@ def _builder(args: t.List) -> E:
 
 # https://docs.snowflake.com/en/sql-reference/functions/div0
 def _build_if_from_div0(args: t.List) -> exp.If:
-    cond = exp.EQ(this=seq_get(args, 1), expression=exp.Literal.number(0)).and_(
-        exp.Is(this=seq_get(args, 0), expression=exp.null()).not_()
+    lhs = exp._wrap(seq_get(args, 0), exp.Binary)
+    rhs = exp._wrap(seq_get(args, 1), exp.Binary)
+
+    cond = exp.EQ(this=rhs, expression=exp.Literal.number(0)).and_(
+        exp.Is(this=lhs, expression=exp.null()).not_()
     )
     true = exp.Literal.number(0)
-    false = exp.Div(this=seq_get(args, 0), expression=seq_get(args, 1))
+    false = exp.Div(this=lhs, expression=rhs)
     return exp.If(this=cond, true=true, false=false)
 
 
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index 7bc2f6e4e3..679a844e84 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -6972,7 +6972,15 @@ def _combine(
     return this
 
 
-def _wrap(expression: E, kind: t.Type[Expression]) -> E | Paren:
+@t.overload
+def _wrap(expression: None, kind: t.Type[Expression]) -> None: ...
+
+
+@t.overload
+def _wrap(expression: E, kind: t.Type[Expression]) -> E | Paren: ...
+
+
+def _wrap(expression: t.Optional[E], kind: t.Type[Expression]) -> t.Optional[E] | Paren:
     return Paren(this=expression) if isinstance(expression, kind) else expression
 
 


Test patch:
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
index e2db661e01..515a07c4f4 100644
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -605,6 +605,17 @@ def test_snowflake(self):
                 "duckdb": "CASE WHEN bar = 0 AND NOT foo IS NULL THEN 0 ELSE foo / bar END",
             },
         )
+        self.validate_all(
+            "DIV0(a - b, c - d)",
+            write={
+                "snowflake": "IFF((c - d) = 0 AND NOT (a - b) IS NULL, 0, (a - b) / (c - d))",
+                "sqlite": "IIF((c - d) = 0 AND NOT (a - b) IS NULL, 0, CAST((a - b) AS REAL) / (c - d))",
+                "presto": "IF((c - d) = 0 AND NOT (a - b) IS NULL, 0, CAST((a - b) AS DOUBLE) / (c - d))",
+                "spark": "IF((c - d) = 0 AND NOT (a - b) IS NULL, 0, (a - b) / (c - d))",
+                "hive": "IF((c - d) = 0 AND NOT (a - b) IS NULL, 0, (a - b) / (c - d))",
+                "duckdb": "CASE WHEN (c - d) = 0 AND NOT (a - b) IS NULL THEN 0 ELSE (a - b) / (c - d) END",
+            },
+        )
         self.validate_all(
             "ZEROIFNULL(foo)",
             write={