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

   **AGE Version:** extension 1.8.0 on PostgreSQL 18.6
   **Installation Method:** Docker
   **API:** Cypher and direct SQL
   
   ## Steps to reproduce (paste into psql as-is)
   
   ```sql
   LOAD 'age';
   SET search_path = ag_catalog, public;
   
   -- Cypher path (re-run: DROP GRAPH xdb CASCADE first)
   SELECT * FROM create_graph('xdb');
   SELECT * FROM cypher('xdb', $$ RETURN split('a.b.c', '.') $$) AS (r agtype);
   
   -- SQL path (same C function, no graph needed)
   SELECT age_split('a.b.c', '.');        -- -> ["", "", "", "", "", ""]
   SELECT age_split('a1b1c', '1');        -- -> ["a", "b", "c"]   (plain 
delimiter: works)
   SELECT age_replace('a.b.c', '.', '-'); -- -> "a-b-c"
   SELECT age_split('a.b.c', '\.');      -- -> ["a", "b", "c"]   (only because 
it is regex-escaped)
   SELECT age_split('a|b', '|');          -- -> ["a", "|", "b"]
   SELECT age_split('a+b+c', '+');        -- -> ERROR: invalid regular 
expression: quantifier operand invalid
   ```
   
   ## Expected behavior
   
   openCypher defines the split delimiter as a **literal string**: 
`split('a.b.c', '.')` → `['a','b','c']`. Neo4j, Memgraph and FalkorDB all 
return this without any escaping, and `replace('a.b.c', '.', '-')` → `'a-b-c'` 
in AGE too — so both functions must interpret '.' identically.
   
   ## Actual behavior
   
   - `split('a.b.c', '.')` → `["","","","","",""]` (six empty strings): the 
delimiter is compiled as a **regular expression**, so '.' matches every 
character.
   - `split('a1b1c', '1')` → `["a","b","c"]`: plain delimiters work, so the 
regex interpretation only surfaces for metacharacters — exactly where queries 
silently break.
   - `split('a.b.c', '\.')` → `["a","b","c"]`: correct **only** because the 
user manually regex-escapes the dot.
   - `split('a|b', '|')` → `["a","|","b"]`: the empty-alternation regex splits 
at every position.
   - `split('a+b+c', '+')` → `ERROR: invalid regular expression: quantifier 
operand invalid`.
   - `replace('a.b.c', '.', '-')` → `"a-b-c"`: the same engine treats the same 
delimiter literally.
   
   The engine is internally inconsistent, deviates from openCypher and all 
sibling engines, and silently corrupts results for common literal delimiters 
('.', '|', '+', ...).
   
   ## Root cause
   
   `age_split()` (src/backend/utils/adt/agtype.c) passes the delimiter verbatim 
to PostgreSQL's `regexp_split_to_array()`, which compiles it as a regular 
expression; `age_replace()` performs a literal replacement. Cypher 
`split()`/`replace()` are rewritten to these same SQL functions, so both APIs 
are affected.
   


-- 
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