Repository: tobymao/sqlglot

Issue:
parsing error duckdb: parser fails when using @ operator in the query.

**Fully reproducible code snippet**


```
from sqlglot import parse_one

parse_one("select @(a) from test", dialect = "duckdb")

parse_one("select @(-11.1323) from test", dialect = "duckdb")
```


The above code is failing as it couldn't able to recognize @ operator. Please find the error details below


```
Traceback (most recent call last):
  File "<python-input-4>", line 1, in <module>
    parse_one("select @(11.1) from test", dialect = "duckdb")
    ~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssk/summation/code/.venv/lib/python3.13/site-packages/sqlglot/__init__.py", line 139, in parse_one
    result = dialect.parse(sql, **opts)
  File "/Users/ssk/summation/code/.venv/lib/python3.13/site-packages/sqlglot/dialects/dialect.py", line 991, in parse
    return self.parser(**opts).parse(self.tokenize(sql), sql)
           ~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssk/summation/code/.venv/lib/python3.13/site-packages/sqlglot/parser.py", line 1498, in parse
    return self._parse(
           ~~~~~~~~~~~^
        parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    )
    ^
  File "/Users/ssk/summation/code/.venv/lib/python3.13/site-packages/sqlglot/parser.py", line 1570, in _parse
    self.raise_error("Invalid expression / Unexpected token")
    ~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/ssk/summation/code/.venv/lib/python3.13/site-packages/sqlglot/parser.py", line 1611, in raise_error
    raise error
sqlglot.errors.ParseError: Invalid expression / Unexpected token. Line 1, Col: 8.
```


**Expected Output**

This should behave like using abs function. The following snippet works and expects the failed one to produce the same output. `@` operator is another way to represent `abs` function


```
from sqlglot import parse_one

parse_one("select abs(a) from test", dialect = "duckdb")

parse_one("select abs(-11.1323) from test", dialect = "duckdb")

```


**Official Documentation**

@ operator is used for calculating absolute value of a number

https://duckdb.org/docs/stable/sql/functions/numeric#x

Discussion:

Solution patch:
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py
index 6c9d87315f..b362cfea22 100644
--- a/sqlglot/dialects/duckdb.py
+++ b/sqlglot/dialects/duckdb.py
@@ -439,6 +439,7 @@ class Parser(parser.Parser):
         NO_PAREN_FUNCTION_PARSERS = {
             **parser.Parser.NO_PAREN_FUNCTION_PARSERS,
             "MAP": lambda self: self._parse_map(),
+            "@": lambda self: exp.Abs(this=self._parse_bitwise()),
         }
 
         TABLE_ALIAS_TOKENS = parser.Parser.TABLE_ALIAS_TOKENS - {


Test patch:
diff --git a/tests/dialects/test_duckdb.py b/tests/dialects/test_duckdb.py
index 955cb4d2c2..7229a1dd00 100644
--- a/tests/dialects/test_duckdb.py
+++ b/tests/dialects/test_duckdb.py
@@ -1569,3 +1569,29 @@ def test_prefix_aliases(self):
             """,
             "SELECT l_returnflag, l_linestatus, SUM(l_quantity) AS sum_qty, SUM(l_extendedprice) AS sum_base_price, SUM(l_extendedprice * (1 - l_discount)) AS sum_disc_price, SUM(l_extendedprice * (1 - l_discount) * (1 + l_tax)) AS sum_charge, AVG(l_quantity) AS avg_qty, AVG(l_extendedprice) AS avg_price, AVG(l_discount) AS avg_disc, COUNT(*) AS count_order",
         )
+
+    def test_at_sign_to_abs(self):
+        self.validate_identity(
+            "SELECT @col FROM t",
+            "SELECT ABS(col) FROM t",
+        )
+        self.validate_identity(
+            "SELECT @col + 1 FROM t",
+            "SELECT ABS(col + 1) FROM t",
+        )
+        self.validate_identity(
+            "SELECT (@col) + 1 FROM t",
+            "SELECT (ABS(col)) + 1 FROM t",
+        )
+        self.validate_identity(
+            "SELECT @(-1)",
+            "SELECT ABS((-1))",
+        )
+        self.validate_identity(
+            "SELECT @(-1) + 1",
+            "SELECT ABS((-1) + 1)",
+        )
+        self.validate_identity(
+            "SELECT (@-1) + 1",
+            "SELECT (ABS(-1)) + 1",
+        )