|
| 1 | +# SQL/PGQ:为 PostgreSQL 引入图查询能力 |
| 2 | + |
| 3 | +## 引言 |
| 4 | + |
| 5 | +2024 年 2 月,Peter Eisentraut 在 pgsql-hackers 邮件列表中宣布了 **SQL 属性图查询(SQL/PGQ)** 的原型实现——一种在 PostgreSQL 中直接执行图风格查询的新方式,遵循 SQL:2023 标准(ISO 9075-16)。该提议曾在 FOSDEM 开发者会议上被简要讨论,社区的兴趣促使 Peter 将进行中的工作整理并分享出来。 |
| 6 | + |
| 7 | +近两年后,补丁已演进为体量可观的实现:**118 个文件变更**,约 **14,800 行**新增代码。Peter 和 Ashutosh Bapat 是主要作者,Junwang Zhao 参与审阅,Ajay Pal 和 Henson Choi 参与测试。最新版本(v20260113)整合了从 v0 到 v14 及之后的全部功能——包括循环路径模式、访问权限、RLS 支持、图元素函数(`LABELS()`、`PROPERTY_NAMES()`)、多模式路径匹配、ECPG 支持、属性排序规则以及 `pg_overexplain` 集成。 |
| 8 | + |
| 9 | +SQL/PGQ 让你可以在现有关系表上定义属性图,并用路径模式(由边连接的顶点)进行查询,类似 Cypher 或 GQL。与专用图数据库不同,该方案将图映射到关系模型上:图是表的**视图**,图查询被改写为连接与并集。讨论中提出了重要的架构问题:这种转换应在 PostgreSQL 的**何时**、**以何种方式**完成——以及改写器是否是合适的实现位置。 |
| 10 | + |
| 11 | +## 背景与意义 |
| 12 | + |
| 13 | +许多应用的数据天然具有图结构:社交网络、供应链、推荐系统、反欺诈等。目前开发者通常需要: |
| 14 | + |
| 15 | +- 使用独立的图数据库(如 Neo4j),维护两套系统,或 |
| 16 | +- 在 PostgreSQL 中用递归 CTE 和复杂连接来编码图遍历。 |
| 17 | + |
| 18 | +SQL/PGQ 旨在为 PostgreSQL 用户提供一种标准的、声明式的方式来表达图查询,而无需脱离 SQL 或复制数据。该标准已被 Oracle 23c 等采用;将其引入 PostgreSQL 将提升互操作性,并使图能力惠及更广泛的用户。 |
| 19 | + |
| 20 | +## 技术分析 |
| 21 | + |
| 22 | +### SQL/PGQ 模型 |
| 23 | + |
| 24 | +SQL/PGQ 中的**属性图**是在已有表上定义的虚拟结构: |
| 25 | + |
| 26 | +- **顶点**:来自一个或多个表的行(可带可选标签)。 |
| 27 | +- **边**:关系,通常由外键推断或显式指定。 |
| 28 | +- **属性**:来自这些表的列。 |
| 29 | +- **标签**:可在多个元素表间共享(例如 `person` 同时用于 `customers` 和 `employees`),每个标签暴露自己的属性集合。 |
| 30 | + |
| 31 | +使用 `CREATE PROPERTY GRAPH` 创建图,用 `GRAPH_TABLE(... MATCH ... COLUMNS ...)` 查询。示例: |
| 32 | + |
| 33 | +```sql |
| 34 | +CREATE PROPERTY GRAPH myshop |
| 35 | + VERTEX TABLES ( |
| 36 | + products LABEL product, |
| 37 | + customers LABEL customer, |
| 38 | + orders LABEL "order" |
| 39 | + ) |
| 40 | + EDGE TABLES ( |
| 41 | + order_items SOURCE orders DESTINATION products LABEL contains, |
| 42 | + customer_orders SOURCE customers DESTINATION orders LABEL has_placed |
| 43 | + ); |
| 44 | + |
| 45 | +SELECT customer_name FROM GRAPH_TABLE (myshop |
| 46 | + MATCH (c IS customer)-[IS has_placed]->(o IS "order" WHERE o.ordered_when = current_date) |
| 47 | + COLUMNS (c.name AS customer_name)); |
| 48 | +``` |
| 49 | + |
| 50 | +`MATCH` 子句描述路径模式;实现会将其改写为 PostgreSQL 可以规划和执行连接与过滤。 |
| 51 | + |
| 52 | +### 实现方式:改写系统 |
| 53 | + |
| 54 | +实现采用**改写系统**完成图到关系的转换——与视图展开处于同一阶段。Peter 解释,这与 SQL/PGQ 规范一致:图映射到关系,查询像视图定义一样被展开。当优化器看到查询时,图结构已被展开为标准关系形式,从而与视图安全(权限、安全屏障)保持一致。 |
| 55 | + |
| 56 | +## 补丁演进:从 v0 到 v20260113 |
| 57 | + |
| 58 | +### v0:脆弱原型(2024 年 2 月) |
| 59 | + |
| 60 | +初始补丁约 332 KB。Peter 称其「相当脆弱」。它引入了 `CREATE PROPERTY GRAPH`、`GRAPH_TABLE`、基本路径模式,以及 `ddl.sgml` 和 `queries.sgml` 中的文档。 |
| 61 | + |
| 62 | +### v1 与早期完善(2024 年 6–8 月) |
| 63 | + |
| 64 | +Peter 与 Ashutosh 发布了 v1,具备「相当完整的最小功能集」。Ashutosh 贡献了: |
| 65 | + |
| 66 | +- **图模式中的 WHERE 子句**——例如 `MATCH (a)->(b)->(c) WHERE a.vname = c.vname` |
| 67 | +- **「列未找到」假阳性修复**:属性名原本引用自 `pg_attribute` 的堆元组;在 `RELCACHE_FORCE_RELEASE` 后可能指向已释放内存。修复方式是将属性名复制出来。 |
| 68 | +- 编译修复、pgperltidy 合规、隐式属性/标签的错误位置报告。 |
| 69 | + |
| 70 | +Imran Zaheer 增加了**标签和属性 EXCEPT 列表**的支持。 |
| 71 | + |
| 72 | +### v14:循环路径、访问权限、RLS(2024 年 8–10 月) |
| 73 | + |
| 74 | +Ashutosh 贡献了主要功能: |
| 75 | + |
| 76 | +- **循环路径模式**:元素变量在路径中重复出现的模式(例如同一顶点既在起点又在终点)。共享变量的元素必须具有相同类型和标签表达式;重复边模式不受支持。 |
| 77 | +- **属性图的访问权限**:属性图行为类似**安全调用者视图**——当前用户必须对底层表有权限。仅对用户可访问的元素,查询才能成功。安全定义者属性图未实现。 |
| 78 | +- **行级安全(RLS)**:`graph_table_rls.sql` 中的回归测试验证 RLS 与属性图的配合。 |
| 79 | +- **属性排序规则与边-顶点链接**:同名字的属性在各元素间必须具有相同排序规则。边的键列与引用顶点键的排序规则必须兼容。边-顶点链接使用等值运算符构建,并建立依赖,使这些运算符无法在未删除边的情况下被删除。 |
| 80 | +- **`\d` 与 `\dG` 变体**:对属性图执行 `\d` 显示元素、表、类型和终点顶点别名;`\d+` 增加标签与属性。`\dG` 列出属性图;`\dG+` 增加所有者与描述。 |
| 81 | + |
| 82 | +### Henson Choi:LABELS()、PROPERTY_NAMES()、多模式(2025 年 12 月) |
| 83 | + |
| 84 | +Henson Choi 贡献了三个补丁: |
| 85 | + |
| 86 | +- **LABELS() 图元素函数**:返回图元素的所有标签为 `text[]`。通过在子查询中包装元素表、添加虚拟 `__labels__` 列实现,使优化器在按标签过滤时能剪枝 Append 分支(例如 `WHERE 'Person' = ANY(LABELS(v))`)。 |
| 87 | +- **PROPERTY_NAMES() 图元素函数**:返回所有属性名为 `text[]`,同样支持基于属性的过滤剪枝。 |
| 88 | +- **多模式路径匹配**:支持 `MATCH` 中逗号分隔的路径模式,如 `MATCH (a)->(b), (b)->(c)`。共享变量的模式合并为一个连接;不相连的模式产生笛卡尔积(与 SQL/PGQ 和 Neo4j Cypher 一致)。 |
| 89 | + |
| 90 | +### v20260113:整合实现(2026 年 1 月) |
| 91 | + |
| 92 | +最新补丁(v20260113)将此前工作合并为单一 WIP 补丁: |
| 93 | + |
| 94 | +- **ECPG 支持**:ECPG 中的 SQL/PGQ——基本查询、预处理语句、游标、动态查询。ECPG 中的标签析取需要对 ecpg 词法分析器做改动。 |
| 95 | +- **pg_overexplain 集成**:属性图 RTE 与 `RELKIND_PROPGRAPH` 在 `EXPLAIN (RANGE_TABLE, ...)` 中得到识别。 |
| 96 | +- **扩展测试覆盖**:`create_property_graph.sql`(365 行)、`graph_table.sql`(561 行)、`graph_table_rls.sql`(363 行)、`privileges.sql`(58 行)。 |
| 97 | +- **rewriteGraphTable.c**:从约 420 行增至约 1,330 行;`propgraphcmds.c` 从约 1,000 行增至约 1,860 行。 |
| 98 | + |
| 99 | +## 社区讨论 |
| 100 | + |
| 101 | +### Andres Freund:对改写器的担忧 |
| 102 | + |
| 103 | +Andres Freund 提出结构层面的顾虑:通过改写系统转换会妨碍优化器利用图语义,并增加对改写系统的依赖。Peter 回应称,PGQ 在设计上以关系为核心(类似视图展开),标准和其他实现都遵循这一模型。Tomas Vondra 质疑是否应更长时间保留图结构以支持图专属索引或执行器节点;Ashutosh Bapat 指出,许多优化本身会改善底层连接,与视图展开保持一致对安全也有意义。 |
| 104 | + |
| 105 | +### Florents Tselai:文档 |
| 106 | + |
| 107 | +Florents Tselai 建议调整文档顺序:先回答「我的数据建模为图 G=(V, E),Postgres 能帮上忙吗?」再深入实现细节,并使用 `graph_table.sql` 中的可运行示例。他将该方案与 Apache Age 的 jsonpath 风格做法对比,但同意标准的关系映射适合 PostgreSQL 核心。 |
| 108 | + |
| 109 | +## 技术细节 |
| 110 | + |
| 111 | +### 架构 |
| 112 | + |
| 113 | +- **解析器**:`CREATE PROPERTY GRAPH`、`ALTER PROPERTY GRAPH`、`DROP PROPERTY GRAPH` 以及 `GRAPH_TABLE(... MATCH ... COLUMNS ...)` 的语法。 |
| 114 | +- **系统表**:`pg_propgraph_element`、`pg_propgraph_element_label`、`pg_propgraph_label`、`pg_propgraph_label_property`、`pg_propgraph_property`。 |
| 115 | +- **改写**:`rewriteGraphTable.c` 将图模式转换为连接与并集。 |
| 116 | +- **工具**:`pg_dump`、`psql` 的 `\d`/`\dG`、补全;`pg_get_propgraphdef()` 用于自省。 |
| 117 | +- **ECPG**:嵌入式 SQL 中的完整支持。 |
| 118 | + |
| 119 | +### 访问控制 |
| 120 | + |
| 121 | +1. 用户需要对属性图有 **SELECT** 权限。 |
| 122 | +2. 属性图采用**安全调用者**:当前用户必须对底层表有权限。仅对用户可访问的元素,查询才能成功。 |
| 123 | +3. 在安全定义者视图中,属性图访问使用视图所有者的权限;底层关系的访问使用执行者的权限。 |
| 124 | +4. 安全定义者属性图未实现(标准未提及)。 |
| 125 | + |
| 126 | +### 属性排序规则与边-顶点链接 |
| 127 | + |
| 128 | +- 同名属性在各元素间必须具有相同排序规则。 |
| 129 | +- 显式指定键时,边的键与顶点键的排序规则必须匹配(外键推导出的链接依赖约束本身)。 |
| 130 | +- 边-顶点链接使用等值运算符;边依赖这些运算符,因此无法独立删除。 |
| 131 | + |
| 132 | +## 当前状态 |
| 133 | + |
| 134 | +**v20260113** 补丁是一个整合后的 WIP。它包含: |
| 135 | + |
| 136 | +- 完整的 `CREATE PROPERTY GRAPH` / `ALTER` / `DROP`,支持标签、属性、KEY 子句、SOURCE/DESTINATION REFERENCES |
| 137 | +- `GRAPH_TABLE`,支持路径模式、模式内 WHERE、循环路径、多模式 |
| 138 | +- `LABELS()` 与 `PROPERTY_NAMES()` 图元素函数 |
| 139 | +- 访问权限、RLS、权限测试 |
| 140 | +- ECPG 支持、pg_overexplain 集成 |
| 141 | +- 文档与回归测试 |
| 142 | + |
| 143 | +补丁**尚未提交**。Peter 和 Ashutosh 持续改进;基于改写器的设计仍是当前选择,审阅与测试仍在进行中。 |
| 144 | + |
| 145 | +## 小结 |
| 146 | + |
| 147 | +SQL/PGQ 将为 PostgreSQL 带来标准化的图查询语法。实现已从脆弱原型发展为功能较全的补丁,包含循环路径、图元素函数、多模式匹配、访问控制、RLS、ECPG 支持及较完整的测试。主要架构选择——在改写系统中将图查询改写为关系形式——与标准和视图语义一致。若被提交,PostgreSQL 用户将能在不离开 SQL、不维护独立图数据库的情况下,在关系数据上表达路径模式。 |
| 148 | + |
| 149 | +## 参考资料 |
| 150 | + |
| 151 | +- [讨论串:SQL Property Graph Queries (SQL/PGQ)](https://www.postgresql.org/message-id/flat/a855795d-e697-4fa5-8698-d20122126567%40eisentraut.org) |
| 152 | +- [Oracle 23c:SQL Property Graphs and SQL/PGQ](https://oracle-base.com/articles/23c/sql-property-graphs-and-sql-pgq-23c) |
| 153 | +- [CIDR 2023:DuckPGQ](https://www.cidrdb.org/cidr2023/papers/p66-wolde.pdf) |
| 154 | +- [PGCon 2019:PostgreSQL 中的图数据库](https://www.pgcon.org/2019/schedule/events/1300.en.html) |
0 commit comments