uesleilima opened a new issue, #2370:
URL: https://github.com/apache/age/issues/2370

   **Describe the bug**
   
   The `execCypher()` function accepts a `cols` parameter that is used to build 
the `AS (col1 agtype, col2 agtype, ...)` clause in the generated SQL. The 
`_validate_column()` / `validate_identifier()` functions correctly validate 
that column names match `^[A-Za-z_][A-Za-z0-9_]*$`, but they do not check 
whether the name is a PostgreSQL reserved word.
   
   When a column alias happens to be a reserved word (e.g., `count`, `sum`, 
`type`, `order`, `group`, `select`), the generated SQL fails with a parse error 
because the alias is used unquoted in the `AS (...)` clause.
   
   This is especially problematic when column aliases are derived from Cypher 
`RETURN` clauses (e.g., `RETURN count(n) AS count`).
   
   **How are you accessing AGE?**
   
   Python driver (psycopg3)
   
   **Steps to Reproduce**
   
   ```python
   import age
   import psycopg
   
   conn = psycopg.connect("host=localhost dbname=postgres user=postgres",
                           cursor_factory=age.age.ClientCursor)
   age.setUpAge(conn, "test_graph")
   
   # This crashes because "count" is a PostgreSQL reserved word
   cursor = age.execCypher(conn, "test_graph",
                           "MATCH (n:TestNode) RETURN count(n)",
                           cols=["count"])
   ```
   
   **Expected behavior**
   
   The query should succeed. The driver should quote-escape reserved-word 
column aliases in the generated `AS (...)` clause, e.g.:
   
   ```sql
   SELECT * FROM cypher(NULL, NULL) AS ("count" agtype);
   ```
   
   **Actual behavior**
   
   PostgreSQL parse error because `count` is unquoted:
   
   ```sql
   -- Generated SQL (broken):
   SELECT * FROM cypher(NULL, NULL) AS (count agtype);
   -- ERROR: syntax error at or near "count"
   ```
   
   **Current workaround**
   
   We maintain a list of 60+ PostgreSQL reserved words and prefix them with 
`col_` before passing to `execCypher()`:
   
   ```python
   _PG_RESERVED_WORDS = frozenset({"all", "and", "as", "count", "group", 
"order",
                                    "select", "sum", "type", "where", ...})
   
   def _sanitize_alias(alias):
       if alias.lower() in _PG_RESERVED_WORDS:
           return f"col_{alias}"
       return alias
   ```
   
   **Suggested fix**
   
   In `_validate_column()` or `buildCypher()`, wrap column names in double 
quotes when they match PostgreSQL reserved words. PostgreSQL always accepts 
double-quoted identifiers, so quoting unconditionally (e.g., `f'"{{name}}" 
{{type_name}}'`) would be the simplest and safest approach:
   
   ```python
   # In buildCypher() or _validate_column():
   columnExp.append(f'"{{name}}" agtype')  # always quote — safe for all 
identifiers
   ```
   
   Alternatively, use `psycopg.sql.Identifier()` which handles quoting 
automatically:
   
   ```python
   from psycopg import sql
   # sql.Identifier(name) produces '"name"' only when needed
   ```
   
   **Environment**
   
   - Apache AGE: 1.5.0+ / master
   - Python driver: master branch (psycopg3 version)
   - psycopg: 3.2.x
   - Python: 3.10+


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to