On 16th of March 2026, Peter Eisentraut committed patch:
SQL Property Graph Queries (SQL/PGQ) Implementation of SQL property graph queries, according to SQL/PGQ standard (ISO/IEC 9075-16:2023). This adds: - GRAPH_TABLE table function for graph pattern matching - DDL commands CREATE/ALTER/DROP PROPERTY GRAPH - several new system catalogs and information schema views - psql \dG command - pg_get_propgraphdef() function for pg_dump and psql A property graph is a relation with a new relkind RELKIND_PROPGRAPH. It acts like a view in many ways. It is rewritten to a standard relational query in the rewriter. Access privileges act similar to a security invoker view. (The security definer variant is not currently implemented.) Starting documentation can be found in doc/src/sgml/ddl.sgml and doc/src/sgml/queries.sgml. Author: Peter Eisentraut <peter@eisentraut.org> Author: Ashutosh Bapat <ashutosh.bapat.oss@gmail.com> Reviewed-by: Junwang Zhao <zhjwpku@gmail.com> Reviewed-by: Ajay Pal <ajay.pal.k@gmail.com> Reviewed-by: Henson Choi <assam258@gmail.com> Discussion: https://www.postgresql.org/message-id/flat/a855795d-e697-4fa5-8698-d20122126567@eisentraut.org
To be honest I somehow missed it. But recently(ish) I saw blogposts about it, and it made me do some research.
Then I tried to do the research. And I just don't get it. From where I sit it's extra language that wraps easy joins in some symbolic language for no apparent (to me) reason.
Don't get me wrong – the problem lies in me, and my understanding, but I just can't get excited about it.
Instead of usual:
select count(*) from knows k1 join knows k2 on k1.b = k2.a where k1.a = 42;
we're supposed to write:
SELECT count(*) FROM GRAPH_TABLE(big_social MATCH (a IS person WHERE a.id = 42)-[IS knows]->(b IS person)-[IS knows]->(c IS person) COLUMNS (c.id));
While I do, kinda, understand the syntax, it will probably take non-trivial time to get used to it, and, at the moment, benefits escape me.
Maybe because of things that will get added to it in the future, that will make writing certain types of queries easier?
Anyway, since I clearly don't grok it yet, I'll point you instead to series of blogposts from CYBERTEC:
- Handling graphs with SQL/PGQ in PostgreSQL
- Heterogeneous Graphs in SQL/PGQ on PostgreSQL 19
- How SQL/PGQ Rewrites to Joins on PostgreSQL 19
- Data Lineage in PostgreSQL 19: Finally, an Answer When the CFO Asks “Where Did This Number Come From?"
In any way, regardless of my personal problems with understanding, I know it's a big deal, so I'd like to thank everybody involved. And perhaps someone can point me to some source of information about “why I should get more enthusiastic about it"…
I have same feeling. On second hand, for the task (and data) that are naturally based on graphs, this syntax can better readable. Using joins in these cases hides natural mathematical model.
Interesting perspective. I think SQL/PGQ will likely be most valuable for complex relationship analysis rather than simple joins. For straightforward queries, traditional SQL is often easier to read, but having native graph support in PostgreSQL is a significant step forward. It will be interesting to see how developers adopt it in real-world applications. Thanks for the insightful write-up.
Once variable-length traversal (`()->(){*}` syntax) is implemented, it could lead to declarative expression of graph traversal with optimized graph algorithms and graph-specific indexes, without having to rewrite in an imperative way using `with recursive`.
Agree with this – pointless until the paths can be of variable length. I haven’t seen anyone working on it but that’d be a game changer in some cases!
I’ve loved your “waiting for” series for a decade, and have been checking to see what you had to say about SQL/PGQ.
I’m *hoping* that Postgres’ implementation of SQL/PGQ will bring graph-style queries into the mainstream. The PG 19 implementation covers a lot of the standard, but doesn’t yet implement much in the way of graph algorithms like shortest path, etc. Oracle has around 60 graph searches implemented out of the box.
Why would anyone want to learn an embedded syntax based on Cypher to replace simple(ish) standard SQL? Fair question. If you really do have a case where a couple of simple of joins handle all that you need, then I’d say using a (at this point obscure) sub-language is a bad idea. However, that’s not all that SQL/PGQ can do for you. The examples floating around out there seem to focus on money-laundering detection and a couple of other highly specific/niche applications that I can’t relate to. However, “graph” queries in general are something that the relational model and SQL have always been pretty clunky for.
I think of graph queries from a more prosaic perspective: Trees. Navigating trees in SQL is awkward. Defining trees or hierarchies in relational terms feels awkward. This isn’t a niche issue, it’s something we all run into *all the time*. Some common examples:
— Bills of materials, or assemblies and subassemblies
— Hierarchies of all sorts, including genealogies and taxonomies.
— Events on items as they pass through a process over time
Super common workloads where the *type* of relationship is something we understand easily in the real world (wolves are a kind of canid, Bob is spouse of Anne, laces are a part of a wingtip shoe assembly, etc.) The relational model *can* store this kind of thing, and SQL *can* navigate it…but it’s not pretty. For storage, you can use a “closure table”, stuff things into a nested object, use a parent:child double-linked table, etc. For reading, you need tree walks which are implemented in SQL with RECURSIVE CTEs. (Not actual recursion, but you know how it works.) SQL is not anyone’s favorite tree-processing language. (Cough, cough) I believe that this is one of the *fundamental* reasons people continuously smoosh relational data into objects, and then need a whole layer of extract/compact/decompress operations and functions. Ugh.
So, that’s what’s exciting about SQL/PGQ: It provides a compact (if cryptic at first glance) query syntax that focuses on *meaningful relationships* rather than raw tables. That’s the beauty of PROPERTY GRAPH. It’s (almost) a view, it’s not a distinct storage system. You define a model, native to Postgres, that describes how your physical tables relate *conceptually*. You give this “view” a name, and you can attach properties to that “view”. Then, you query the model with QUERY GRAPH_TABLE. For simple work, not necessarily helpful, but for anything more than simple work…*so* much simpler. A few months back, I built out a bunch of standard SQL queries for data sets with related parts. Modern versions of Postgres have decent graph query support in RECURSIVE CTEs, with cycle detection and more. You can even do lateral tree walks now. (Again, so much simpler in an imperative-style syntax…but never mind.) The code grows *fast*, particularly if you have to do a LATERAL JOIN to get what amounts to a nested loop. Which, in tree walks on ordinary cases, you often do. After getting it all working, I realized that this valid SQL is *completely useless.* With an even modestly complex problem, meaning most anything past a toy syntax example, the code is *unmaintainable*. It was the kind of code where the next person would look at it, not know where to start, and probably pull the code into Python (pick your poison) instead. I hate writing code that has to be thrown away.
In the case above, I wasn’t using Postgres, I was using the ~experimental SQL/PGQ extension for DuckDB. It’s quite decent, but I ran into some fiddly bits. Might have been me. A column store seems ideal for PROPERTY GRAPH as the engine can pull columns listed as graph properties discretely, which is better than pulling rows and discarding most of them. However, I’d really like to see graph queries in Postgres…and I hope that this implementation proves popular…and that PG20+ implements more quality-of-life features.
For those who get into this stuff, keep an eye on DuckDB ’cause it’s awesome. Plus, it moves a whole lot faster than Postgres…and is based on very modern database research. Plus, it’s fun. If you have some heavy-duty workloads, maybe you’ll need a dedicated graph database, but otherwise I want to throw down a bit of skepticism. Unless you really need something like Neo4j, then you *don’t* want it. Dedicated graph engines, like Neo4j, can deliver a lot of performance….at a cost. The cost being that they materialize, in advance, the connections between nodes. So, now you’ve got these quite heavy data structures that have to be built (takes time), stored (takes space) and maintained (time and space.) Like any materialization/caching-like system, you’re making a trade-off. Are there cases where the extra processing time and space pay for themselves are worthwhile. I’m sure there are. Do you have such a case? Find out first, before duplicating or moving data out of Postgres into another system.
Oh, and tools with a focus on graph searches may well implement a ton of great algorithms that *are* great to have. Oracle, Neo4j, etc. Here’s hoping that we get the basics in PG20!
@David:
thanks for detailed answer.
So far I hadn’t had a usecase that would call for use of similar things, but your explanation cleared things a bit. Will keep that in mind, but not sure if/when I’ll stumble upon a usecase that would benefit from it 🙂