split() treats the delimiter as a regular expression while replace() treats it literally
维护者通常 3 天内回复
评估
调研方向
阅读 src/backend/utils/adt/agtype.c,从 age_split() 开始,并将其分隔符处理与 age_replace() 进行比较。通过 Cypher 路径和直接 SQL 路径分别运行提供的 psql 复现。完成标准是:'.'、'|' 和 '+' 等字面分隔符无需进行正则表达式转义即可正确分割,同时替换行为保持一致。
由索引模型根据 Issue 内容生成。
描述
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)
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.
- 主要语言
- C
- 星标
- 4.9k
- 派生
- 535
- 平均合并
- 10 天 13 小时
- 30 天内合并 PR
- 7
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
apache/age 的其他 Issue
-
Integer division by -1 on INT64_MIN raises a misclassified floating-point exception (22P01) instead of bigint out of range可能已有人在做 @sean0407 于 12 天前认领。 未关闭bug
难度 2/5 1-3 小时 新手友好度 74/100
维护者通常 3 天内回复
-
难度 2/5 1-3 小时 新手友好度 88/100
维护者通常 3 天内回复
-
bug
难度 4/5 3-5 天 新手友好度 53/100
维护者通常 3 天内回复
-
bug
难度 4/5 3-5 天 新手友好度 45/100
维护者通常 3 天内回复
-
bug
难度 4/5 3-5 天 新手友好度 45/100
维护者通常 3 天内回复
相似的 Issue
-
难度 2/5 1-3 小时 新手友好度 76/100
-
难度 1/5 1 小时以内 新手友好度 93/100
webarkit/WebARKitLib#107 ·
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 72/100
维护者通常 1 天内回复
-
难度 2/5 1-3 小时 新手友好度 76/100
polhenarejos/pico-fido#302 ·
-
[BUG] Out-of-range negative slice end returns the whole string可能已有人在做 @Don-Yin 今天认领。 未关闭
难度 2/5 1-3 小时 新手友好度 67/100
fastfetch-cli/fastfetch#2668 ·
维护者通常 1 天内回复