internlm/SWE-Fixer-Train-110K huggingface dataset:
Repository: tobymao/sqlglot

Issue:
MySQL parser not support operator in DATE_ADD params after INTERVAL
DATE_ADD, DATE_SUB ... 

examle:

`
query = "select DATE_ADD('2023-06-23 12:00:00', INTERVAL 2 * 2  MONTH) from x"

exp = sqlglot.parse_one(query, read=sqlglot.dialects.mysql.MySQL)
`

error:
`
sqlglot.errors.ParseError: Expecting ). Line 2, Col: 68.
  
select DATE_ADD('2023-06-23 12:00:00', INTERVAL 2 * 2  MONTH) from x
`




Discussion:

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -2863,7 +2863,11 @@ def _parse_interval(self) -> t.Optional[exp.Interval]:
         if not self._match(TokenType.INTERVAL):
             return None
 
-        this = self._parse_primary() or self._parse_term()
+        if self._match(TokenType.STRING, advance=False):
+            this = self._parse_primary()
+        else:
+            this = self._parse_term()
+
         unit = self._parse_function() or self._parse_var()
 
         # Most dialects support, e.g., the form INTERVAL '5' day, thus we try to parse


Test patch:
diff --git a/tests/dialects/test_mysql.py b/tests/dialects/test_mysql.py
--- a/tests/dialects/test_mysql.py
+++ b/tests/dialects/test_mysql.py
@@ -402,6 +402,12 @@ def test_mysql(self):
         self.validate_all("CAST(x AS SIGNED INTEGER)", write={"mysql": "CAST(x AS SIGNED)"})
         self.validate_all("CAST(x AS UNSIGNED)", write={"mysql": "CAST(x AS UNSIGNED)"})
         self.validate_all("CAST(x AS UNSIGNED INTEGER)", write={"mysql": "CAST(x AS UNSIGNED)"})
+        self.validate_all(
+            "SELECT DATE_ADD('2023-06-23 12:00:00', INTERVAL 2 * 2 MONTH) FROM foo",
+            write={
+                "mysql": "SELECT DATE_ADD('2023-06-23 12:00:00', INTERVAL (2 * 2) MONTH) FROM foo",
+            },
+        )
         self.validate_all(
             "SELECT * FROM t LOCK IN SHARE MODE", write={"mysql": "SELECT * FROM t FOR SHARE"}
         )
Repository: tobymao/sqlglot

Issue:
Spark to DuckDB 'datediff' transpilation error
Possibly related to #1929 

Getting an issue transpiling from Spark to DuckDB, the 'datediff' function is transpiling correctly to "date_diff" but the period parameter is being returned as "None" instead of "Day".

Source sql:
![image](https://github.com/tobymao/sqlglot/assets/136445689/329dbe3d-8e7b-452e-9557-35747a054075)

Expected:
![image](https://github.com/tobymao/sqlglot/assets/136445689/c8ee6801-18fc-425e-9666-57b2ce577a4f)

Actual:
![image](https://github.com/tobymao/sqlglot/assets/136445689/a730cfd3-bf57-44cf-b977-b545df53de79)

Can you take a look please.


Discussion:
Will take a look, thanks for the report

Resolving patch:
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -211,7 +211,7 @@ class Generator(generator.Generator):
             exp.DateFromParts: rename_func("MAKE_DATE"),
             exp.DateSub: _date_delta_sql,
             exp.DateDiff: lambda self, e: self.func(
-                "DATE_DIFF", f"'{e.args.get('unit', 'day')}'", e.expression, e.this
+                "DATE_DIFF", f"'{e.args.get('unit') or 'day'}'", e.expression, e.this
             ),
             exp.DateStrToDate: datestrtodate_sql,
             exp.DateToDi: lambda self, e: f"CAST(STRFTIME({self.sql(e, 'this')}, {DuckDB.DATEINT_FORMAT}) AS INT)",


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -56,6 +56,15 @@ def test_duckdb(self):
         self.validate_all("0x1010", write={"": "0 AS x1010"})
         self.validate_all("x ~ y", write={"duckdb": "REGEXP_MATCHES(x, y)"})
         self.validate_all("SELECT * FROM 'x.y'", write={"duckdb": 'SELECT * FROM "x.y"'})
+        self.validate_all(
+            "DATE_DIFF('day', CAST(b AS DATE), CAST(a AS DATE))",
+            read={
+                "duckdb": "DATE_DIFF('day', CAST(b AS DATE), CAST(a AS DATE))",
+                "hive": "DATEDIFF(a, b)",
+                "spark": "DATEDIFF(a, b)",
+                "spark2": "DATEDIFF(a, b)",
+            },
+        )
         self.validate_all(
             "XOR(a, b)",
             read={
Repository: tobymao/sqlglot

Issue:
MySQL tokenizer errors on "IS ?"
Hello,
I am trying out sqlglot and ran into an error when testing various parameterized mysql queries.

The following works just fine
```
query = "SELECT a FROM b WHERE c IS TRUE;"
parse_one(query, dialect='mysql')

query = "SELECT a FROM b WHERE c = ?;"
parse_one(query, dialect='mysql')
```

But when a placeholder is used with the `IS` operator it errors.
```
query = "SELECT a FROM b WHERE c IS ?;"
parse_one(query, dialect='mysql')
```
The error
```
sqlglot.errors.ParseError: Invalid expression / Unexpected token. Line 1, Col: 26.
  SELECT a FROM b WHERE c IS ?;
```

I tried searching around the closed issues to see if this was previously discussed, as well as in slack, but I wasn't able to find anything.

Any ideas what's going on? Is this intentional?

EDIT: grammar


Discussion:
UPDATE: I tried wrapping `IS ?` with parantheses as a last ditch attempt, and that worked.
```
query = "SELECT a FROM b WHERE c (IS ?);"
parse_one(query, dialect='mysql')
```
Follow up question is why? The MySQL documentation on the IS operator doesn't specify needing these parentheses
https://dev.mysql.com/doc/refman/8.0/en/comparison-operators.html#operator_is

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -4299,19 +4299,19 @@ def _parse_var_or_string(self) -> t.Optional[exp.Expression]:
     def _parse_null(self) -> t.Optional[exp.Expression]:
         if self._match(TokenType.NULL):
             return self.PRIMARY_PARSERS[TokenType.NULL](self, self._prev)
-        return None
+        return self._parse_placeholder()
 
     def _parse_boolean(self) -> t.Optional[exp.Expression]:
         if self._match(TokenType.TRUE):
             return self.PRIMARY_PARSERS[TokenType.TRUE](self, self._prev)
         if self._match(TokenType.FALSE):
             return self.PRIMARY_PARSERS[TokenType.FALSE](self, self._prev)
-        return None
+        return self._parse_placeholder()
 
     def _parse_star(self) -> t.Optional[exp.Expression]:
         if self._match(TokenType.STAR):
             return self.PRIMARY_PARSERS[TokenType.STAR](self, self._prev)
-        return None
+        return self._parse_placeholder()
 
     def _parse_parameter(self) -> exp.Parameter:
         wrapped = self._match(TokenType.L_BRACE)


Test patch:
diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql
--- a/tests/fixtures/identity.sql
+++ b/tests/fixtures/identity.sql
@@ -730,6 +730,7 @@ WITH a AS (SELECT * FROM b) DELETE FROM a
 WITH a AS (SELECT * FROM b) CACHE TABLE a
 SELECT ? AS ? FROM x WHERE b BETWEEN ? AND ? GROUP BY ?, 1 LIMIT ?
 SELECT :hello, ? FROM x LIMIT :my_limit
+SELECT a FROM b WHERE c IS ?
 SELECT * FROM x OFFSET @skip FETCH NEXT @take ROWS ONLY
 WITH a AS ((SELECT b.foo AS foo, b.bar AS bar FROM b) UNION ALL (SELECT c.foo AS foo, c.bar AS bar FROM c)) SELECT * FROM a
 WITH a AS ((SELECT 1 AS b) UNION ALL (SELECT 1 AS b)) SELECT * FROM a
Repository: tobymao/sqlglot

Issue:
Empty map error
**Fully reproducible code snippet**
```python
from sqlglot import transpile
transpile("SELECT map()", read="trino", write="spark")
```
Raises the exception:
```
KeyError: 'keys'
```
When it should just transpile as:
```python
'SELECT map()'
```

**Official Documentation**
[Trino map](https://trino.io/docs/current/functions/map.html?highlight=map#map)
[SparkSQL map](https://spark.apache.org/docs/latest/api/sql/index.html#map)


Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/spark2.py b/sqlglot/dialects/spark2.py
--- a/sqlglot/dialects/spark2.py
+++ b/sqlglot/dialects/spark2.py
@@ -32,9 +32,13 @@ def _create_sql(self: Hive.Generator, e: exp.Create) -> str:
 
 
 def _map_sql(self: Hive.Generator, expression: exp.Map) -> str:
-    keys = self.sql(expression.args["keys"])
-    values = self.sql(expression.args["values"])
-    return f"MAP_FROM_ARRAYS({keys}, {values})"
+    keys = expression.args.get("keys")
+    values = expression.args.get("values")
+
+    if not keys or not values:
+        return "MAP()"
+
+    return f"MAP_FROM_ARRAYS({self.sql(keys)}, {self.sql(values)})"
 
 
 def _parse_as_cast(to_type: str) -> t.Callable[[t.List], exp.Expression]:


Test patch:
diff --git a/tests/dialects/test_spark.py b/tests/dialects/test_spark.py
--- a/tests/dialects/test_spark.py
+++ b/tests/dialects/test_spark.py
@@ -244,6 +244,23 @@ def test_spark(self):
             "SELECT STR_TO_MAP('a:1,b:2,c:3', ',', ':')",
         )
 
+        self.validate_all(
+            "MAP(1, 2, 3, 4)",
+            write={
+                "spark": "MAP(1, 2, 3, 4)",
+                "trino": "MAP(ARRAY[1, 3], ARRAY[2, 4])",
+            },
+        )
+        self.validate_all(
+            "MAP()",
+            read={
+                "spark": "MAP()",
+                "trino": "MAP()",
+            },
+            write={
+                "trino": "MAP(ARRAY[], ARRAY[])",
+            },
+        )
         self.validate_all(
             "SELECT STR_TO_MAP('a:1,b:2,c:3', ',', ':')",
             read={
Repository: tobymao/sqlglot

Issue:
Trino's `regexp_replace` can take 2 arguments
**Fully reproducible code snippet**
```py
import sqlglot
sqlglot.parse_one("SELECT regexp_replace('abc', 'a')", read="trino")
```

Raises an error requiring a third argument (what to replace `'a'` with) but, in Trino, `regexp_replace` can be used to just delete the regex matches and doesn't require a third argument.

**Official Documentation**
[`regexp_replace`](https://trino.io/docs/current/functions/regexp.html?highlight=regexp_replace#regexp_replace)



Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/presto.py b/sqlglot/dialects/presto.py
--- a/sqlglot/dialects/presto.py
+++ b/sqlglot/dialects/presto.py
@@ -220,6 +220,11 @@ class Parser(parser.Parser):
             "REGEXP_EXTRACT": lambda args: exp.RegexpExtract(
                 this=seq_get(args, 0), expression=seq_get(args, 1), group=seq_get(args, 2)
             ),
+            "REGEXP_REPLACE": lambda args: exp.RegexpReplace(
+                this=seq_get(args, 0),
+                expression=seq_get(args, 1),
+                replacement=seq_get(args, 2) or exp.Literal.string(""),
+            ),
             "ROW": exp.Struct.from_arg_list,
             "SEQUENCE": exp.GenerateSeries.from_arg_list,
             "SPLIT_TO_MAP": exp.StrToMap.from_arg_list,


Test patch:
diff --git a/tests/dialects/test_presto.py b/tests/dialects/test_presto.py
--- a/tests/dialects/test_presto.py
+++ b/tests/dialects/test_presto.py
@@ -137,6 +137,13 @@ def test_cast(self):
         )
 
     def test_regex(self):
+        self.validate_all(
+            "REGEXP_REPLACE('abcd', '[ab]')",
+            write={
+                "presto": "REGEXP_REPLACE('abcd', '[ab]', '')",
+                "spark": "REGEXP_REPLACE('abcd', '[ab]', '')",
+            },
+        )
         self.validate_all(
             "REGEXP_LIKE(a, 'x')",
             write={
Repository: tobymao/sqlglot

Issue:
Parser is failing due to UNIQUE keyword
when read='oracle' 

for table in parse_one("**SELECT UNIQUE col1, col2 FROM table**").find_all(exp.Table):
    print(table.name)

Parser is unable to understand the UNIQUE keyword. Without UNIQUE keywork parser works fine even for complex queries.

I'm trying to get all the table list from the above query, but parser fails when UNIQUE is avialable in the query, without UNIQUE, it returns correct table list.


Discussion:
please create a more detailed pr in the future, what dialect is this and can you link the relevant documentation

Resolving patch:
diff --git a/sqlglot/dialects/oracle.py b/sqlglot/dialects/oracle.py
--- a/sqlglot/dialects/oracle.py
+++ b/sqlglot/dialects/oracle.py
@@ -78,6 +78,10 @@ class Parser(parser.Parser):
             )
         }
 
+        # SELECT UNIQUE .. is old-style Oracle syntax for SELECT DISTINCT ..
+        # Reference: https://stackoverflow.com/a/336455
+        DISTINCT_TOKENS = {TokenType.DISTINCT, TokenType.UNIQUE}
+
         def _parse_column(self) -> t.Optional[exp.Expression]:
             column = super()._parse_column()
             if column:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -821,6 +821,8 @@ class Parser(metaclass=_Parser):
 
     ADD_CONSTRAINT_TOKENS = {TokenType.CONSTRAINT, TokenType.PRIMARY_KEY, TokenType.FOREIGN_KEY}
 
+    DISTINCT_TOKENS = {TokenType.DISTINCT}
+
     STRICT_CAST = True
 
     # A NULL arg in CONCAT yields NULL by default
@@ -1978,7 +1980,7 @@ def _parse_select(
 
             hint = self._parse_hint()
             all_ = self._match(TokenType.ALL)
-            distinct = self._match(TokenType.DISTINCT)
+            distinct = self._match_set(self.DISTINCT_TOKENS)
 
             kind = (
                 self._match(TokenType.ALIAS)


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -28,6 +28,10 @@ def test_oracle(self):
             'OVER (PARTITION BY department_id) AS "Worst", MAX(salary) KEEP (DENSE_RANK LAST ORDER BY commission_pct) '
             'OVER (PARTITION BY department_id) AS "Best" FROM employees ORDER BY department_id, salary, last_name'
         )
+        self.validate_identity(
+            "SELECT UNIQUE col1, col2 FROM table",
+            "SELECT DISTINCT col1, col2 FROM table",
+        )
 
         self.validate_all(
             "NVL(NULL, 1)",
Repository: tobymao/sqlglot

Issue:
Unable to parse the oracle query when ":OBJECT" bind is present
Hello,

Parser is throwing an error when there is ":OBJECT" bind is present in the oracle query.

**Code for your reference -**

```
import sqlglot.expressions as exp

query ="""
SELECT A.COL1, B.COL2 FROM SCHEMA1.TABLE1 A, SCHEMA2.TABLE2 B WHERE A.COL1 = :OBJECT AND B.COL2 = :SOMEVARIABLE 
"""

expressions = sqlglot.parse_one(query, read='oracle')
x = list(expressions.find_all(exp.Table))

for i in x:
  listTables = str(i).split(' ')[0]
  print(listTables)
```


Above code works fine if Bind name is different that OBJECT. If ':OBJECT' is present in the query, then it fails.

Request you to add support for ':OBJECT' for query parsing.


Discussion:

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -578,7 +578,7 @@ class Parser(metaclass=_Parser):
         TokenType.PLACEHOLDER: lambda self: self.expression(exp.Placeholder),
         TokenType.PARAMETER: lambda self: self._parse_parameter(),
         TokenType.COLON: lambda self: self.expression(exp.Placeholder, this=self._prev.text)
-        if self._match_set((TokenType.NUMBER, TokenType.VAR))
+        if self._match(TokenType.NUMBER) or self._match_set(self.ID_VAR_TOKENS)
         else None,
     }
 


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -6,6 +6,7 @@ class TestOracle(Validator):
     dialect = "oracle"
 
     def test_oracle(self):
+        self.validate_identity("SELECT :OBJECT")
         self.validate_identity("SELECT * FROM t FOR UPDATE")
         self.validate_identity("SELECT * FROM t FOR UPDATE WAIT 5")
         self.validate_identity("SELECT * FROM t FOR UPDATE NOWAIT")
Repository: tobymao/sqlglot

Issue:
Unable to parse the oracle query when "#" is present in the column names
Hello,

Parser is throwing an error when there is "#" character is present in the column name.

**Code for your reference -**

```
import sqlglot.expressions as exp

query ="""
SELECT A.COL#, B.COL2 FROM SCHEMA1.TABLE1 A
"""

expressions = sqlglot.parse_one(query, read='oracle')
x = list(expressions.find_all(exp.Table))

for i in x:
  listTables = str(i).split(' ')[0]
  print(listTables)
```


Above code fails as it cannot process '#' and throws the error.
Request you to add support for '#' for query parsing.


Discussion:
Please link any related documentation that shows this identifier syntax is valid in Oracle.
Hi @GeorgeSittas 

Many systems tables have columns with # and $ character.

Sample data for your reference - 
```
Schema | Table | Column
SYS   V_$ACTIVE_SESSION_HISTORY   EVENT#
SYS   GV_$LOG_HISTORY   FIRST_CHANGE#
SYSTEM   LOGMNR_GLOBAL$   SESSION#
SYSTEM   LOGMNR_COLTYPE$   COL#
```
Sample Query for your reference - 
```
SELECT OWNER, TABLE_NAME, COLUMN_NAME FROM dba_tab_cols
where COLUMN_NAME like '%#%';
```
Parser is failing if we have a query where we are reading from system tables which has # in the column names.

Please do the needful. Thanks for your support!

Resolving patch:
diff --git a/sqlglot/dialects/oracle.py b/sqlglot/dialects/oracle.py
--- a/sqlglot/dialects/oracle.py
+++ b/sqlglot/dialects/oracle.py
@@ -166,7 +166,7 @@ def xmltable_sql(self, expression: exp.XMLTable) -> str:
             return f"XMLTABLE({self.sep('')}{self.indent(this + passing + by_ref + columns)}{self.seg(')', sep='')}"
 
     class Tokenizer(tokens.Tokenizer):
-        VAR_SINGLE_TOKENS = {"@"}
+        VAR_SINGLE_TOKENS = {"@", "$", "#"}
 
         KEYWORDS = {
             **tokens.Tokenizer.KEYWORDS,


Test patch:
diff --git a/tests/dialects/test_oracle.py b/tests/dialects/test_oracle.py
--- a/tests/dialects/test_oracle.py
+++ b/tests/dialects/test_oracle.py
@@ -6,6 +6,7 @@ class TestOracle(Validator):
     dialect = "oracle"
 
     def test_oracle(self):
+        self.validate_identity("SELECT a$x#b")
         self.validate_identity("SELECT :OBJECT")
         self.validate_identity("SELECT * FROM t FOR UPDATE")
         self.validate_identity("SELECT * FROM t FOR UPDATE WAIT 5")
Repository: tobymao/sqlglot

Issue:
tsql ALTER TABLE doesn't use keyword COLUMN when adding a column 
**Fully reproducible code snippet**
```
>>> from sqlglot import transpile
>>> transpile("ALTER TABLE a ADD COLUMN b INT", write="tsql")
['ALTER TABLE a ADD COLUMN b INTEGER']
```

Expected output:
`'ALTER TABLE a ADD b INTEGER'`


Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/tsql.py b/sqlglot/dialects/tsql.py
--- a/sqlglot/dialects/tsql.py
+++ b/sqlglot/dialects/tsql.py
@@ -581,6 +581,7 @@ class Generator(generator.Generator):
         QUERY_HINTS = False
         RETURNING_END = False
         NVL2_SUPPORTED = False
+        ALTER_TABLE_ADD_COLUMN_KEYWORD = False
 
         TYPE_MAPPING = {
             **generator.Generator.TYPE_MAPPING,
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -186,6 +186,9 @@ class Generator:
     # SELECT * VALUES into SELECT UNION
     VALUES_AS_TABLE = True
 
+    # Whether or not the word COLUMN is included when adding a column with ALTER TABLE
+    ALTER_TABLE_ADD_COLUMN_KEYWORD = True
+
     TYPE_MAPPING = {
         exp.DataType.Type.NCHAR: "CHAR",
         exp.DataType.Type.NVARCHAR: "VARCHAR",
@@ -2267,7 +2270,11 @@ def altertable_sql(self, expression: exp.AlterTable) -> str:
         actions = expression.args["actions"]
 
         if isinstance(actions[0], exp.ColumnDef):
-            actions = self.expressions(expression, key="actions", prefix="ADD COLUMN ")
+            actions = self.expressions(
+                expression,
+                key="actions",
+                prefix="ADD COLUMN " if self.ALTER_TABLE_ADD_COLUMN_KEYWORD else "ADD ",
+            )
         elif isinstance(actions[0], exp.Schema):
             actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ")
         elif isinstance(actions[0], exp.Delete):


Test patch:
diff --git a/tests/dialects/test_tsql.py b/tests/dialects/test_tsql.py
--- a/tests/dialects/test_tsql.py
+++ b/tests/dialects/test_tsql.py
@@ -487,6 +487,12 @@ def test_ddl(self):
             },
         )
 
+        self.validate_all(
+            "ALTER TABLE a ADD b INTEGER",
+            read={"": "ALTER TABLE a ADD COLUMN b INT"},
+            write={"tsql": "ALTER TABLE a ADD b INTEGER"},
+        )
+
         self.validate_all(
             "CREATE TABLE #mytemp (a INTEGER, b CHAR(2), c TIME(4), d FLOAT(24))",
             write={
Repository: tobymao/sqlglot

Issue:
Listagg not recognized as aggregation function in snowflake dialect
Unable to find listagg in snowflake dialect using exp.AggFunc

**Fully reproducible code snippet**
```python
from sqlglot import Dialects, exp, parse_one

query = "SELECT listagg(data['some_field'], ',') FROM  some_table"
parsed_query = parse_one(query, read=Dialects.SNOWFLAKE.value)
agg_funcs = parsed_query.find_all(exp.AggFunc)
has_agg_funcs = any(agg_funcs) # getting False, expected True
``` 
**Official Documentation**
https://docs.snowflake.com/en/sql-reference/functions/listagg


Discussion:

Resolving patch:
diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py
--- a/sqlglot/dialects/snowflake.py
+++ b/sqlglot/dialects/snowflake.py
@@ -242,6 +242,7 @@ class Parser(parser.Parser):
             "DATEDIFF": _parse_datediff,
             "DIV0": _div0_to_if,
             "IFF": exp.If.from_arg_list,
+            "LISTAGG": exp.GroupConcat.from_arg_list,
             "NULLIFZERO": _nullifzero_to_if,
             "OBJECT_CONSTRUCT": _parse_object_construct,
             "REGEXP_REPLACE": _parse_regexp_replace,
@@ -361,6 +362,7 @@ class Generator(generator.Generator):
             exp.DataType: _datatype_sql,
             exp.DayOfWeek: rename_func("DAYOFWEEK"),
             exp.Extract: rename_func("DATE_PART"),
+            exp.GroupConcat: rename_func("LISTAGG"),
             exp.If: rename_func("IFF"),
             exp.LogicalAnd: rename_func("BOOLAND_AGG"),
             exp.LogicalOr: rename_func("BOOLOR_AGG"),
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -4449,7 +4449,7 @@ class Greatest(Func):
     is_var_len_args = True
 
 
-class GroupConcat(Func):
+class GroupConcat(AggFunc):
     arg_types = {"this": True, "separator": False}
 
 


Test patch:
diff --git a/tests/dialects/test_snowflake.py b/tests/dialects/test_snowflake.py
--- a/tests/dialects/test_snowflake.py
+++ b/tests/dialects/test_snowflake.py
@@ -36,6 +36,7 @@ def test_snowflake(self):
             },
         )
 
+        self.validate_identity("LISTAGG(data['some_field'], ',')")
         self.validate_identity("WEEKOFYEAR(tstamp)")
         self.validate_identity("SELECT SUM(amount) FROM mytable GROUP BY ALL")
         self.validate_identity("WITH x AS (SELECT 1 AS foo) SELECT foo FROM IDENTIFIER('x')")