Please enjoy the slides and transcript from Tuning Postgres for Analytics at PostgreSQL Conference Europe (PGConf.EU) in Valencia, Spain on 20 Oct 2026.
I'll add a link to the recording once it's available.
I've included just very brief alt-text for each slide image, but I've tried to add any links or other essential information into the body of the blog post.

Obviously, I don't know the specifics of your particular Postgres environment. But, as always, there are some generalisations that apply to most systems. So, just for today, we're going to imagine that you're running a small chain of popular coffee shops in Valencia. If you've seen any of my previous talks, you might start to notice a coffee and cake theme in lots of my talks, demos and examples!
You've implemented a state of the art system to manage your sales, stock, customer loyalty program etc. and you've carefully configured your production database for the needs of your day-to-day application workload.
Let's talk about what happens if you then need to run analytics queries against your application data. How can you do that without compromising the performance of your application?

First, a quick "about me":
I’ve spent my whole career working with databases; for the first 20 years or so as a database administrator. I’m currently a Senior Software Engineer on the Postgres extensions team at Snowflake, working mainly on the pg_lake set of extensions.
I’m actively involved in PostgreSQL Europe; amongst other things, leading the PGEU Diversity Committee and helping to organise pgconf.eu. I also write and present talks (about databases) at Postgres and Developer conferences.
If you want ideas for non-database-related conversation topics when you meet me in the hallway: I love being creative; crocheting cute things, sewing clothes and accessories, and 3D printing all sorts of random things. I also enjoy getting out and about on one of my bikes in the mountains where I live.

Back to the presentation.
Let's
[...]In my last post, a friend of mine kept four databases in production. Ask a team like that for a knowledge graph, and you can guess the fifth: a triple store. Search over company data, an ontology of products or skills, facts for an LLM application: a knowledge base often means RDF and SPARQL, and usually a separate server next to the relational database.
That server has a cost. The facts it needs already live in PostgreSQL tables, so an ETL job copies them as triples, and the copy goes stale as soon as the job ends. Two databases mean two security models, two backup schedules, two failover procedures, and no transaction that spans a row and a triple.
So I built pg_knowledge_base, a PostgreSQL 19 extension that runs Oxigraph’s SPARQL engine inside the server, without core patches. Triples live in ordinary tables, existing tables are read as RDF through R2RML mappings, and one SPARQL query can use both. Oxigraph’s HTTP endpoints remain for any SPARQL client, while transactions, replication and backups come from PostgreSQL. As with my previous extensions, Claude Code wrote the code, while I made the architectural decisions and checked the results.
Oxigraph is a graph database and RDF toolkit written in Rust, and its stated goal is “a compliant, safe, and fast graph database”. What matters for embedding is that its query stack is a set of small crates: spargebra parses SPARQL, sparopt optimizes it, spareval evaluates it, and sparesults and oxrdfio handle result and RDF formats. spareval evaluates over anything that implements its QueryableDataset trait, so pg_knowledge_base reuses Oxigraph’s parser, optimizer and evaluator and replaces only the storage: PostgreSQL tables instead of RocksDB.
Keeping the engine keeps its answers. Of the W3C SPARQL 1.1 query and update tests, 990 pass and none fails. Another 11 differ from the expected results exactly as Oxigraph’s own store does, mostly because it keeps typed literals as values ("01"^^xsd:integer comes back as 1), and 6 are skipped,
geos315: 3.15.0
proj98: 9.8.1
libgeotiff17: 1.7.4
librttopo: 1.1.0
libspatialite50: 5.1.0
gdal313 (with the Python 3.13 and Java bindings): 3.13.3
SFCGAL: 2.3.0
PostGIS (postgis33 ... postgis37): 3.3.10, 3.4.6, 3.5.7, 3.6.4, 3.7.0 RC
pgRouting: 4.0.2
ogr_fdw: 1.1.9
h3-pg: 4.5.0
postgis_tiger_geocoder: 2025.2
Postgres gives you no way to take back a single statement. When someone runs an UPDATE without its WHERE clause, recovery is a procedure rather than a command. You restore a copy of the database to the moment before the mistake, pull out the rows that were damaged, and reconcile them by hand against a live table that has kept moving since.This kind of recovery works, and teams do it. It also takes hours, needs your most senior person, and every step is a chance to make things worse while the table is still serving traffic. One second of bad SQL turns into an afternoon of careful work by the people you can least afford to tie up.Oracle users have Flashback for exactly this case, and it is really two features: one to read the past, one to put it back. Postgres has neither. Point in time recovery is what makes that procedure possible, but it recovers an entire cluster rather than a statement. You restore it alongside production and compare, because rewinding production itself would discard everything that happened after the mistake. I wanted something smaller and more precise, so I built pgVolvra.
Another story of something I saw in the field which surprised me. Turns out recovery_prefetch can cause outages.
Something which has been happening quietly in the background all the time, but we didn’t notice until we looked close enough to see it.
After a lot of work I discovered this was partly a case where someone had dropped an email to the postgres hackers list years ago “hey what about this” and everyone thought “nah it can’t be that bad” … and now here we are seeing production outages!
Exhibit A: Feb 2021, while the feature was being developed, a year and a half before it shipped, Stephen Frost
https://www.postgresql.org/message-id/20210214231815.GO27507@tamriel.snowman.net
Exhibit B: Mar 2025, clear report from the field, Michael Zhilin (Postgres Pro)
https://www.postgresql.org/message-id/9bdb428a-fc8e-4801-8fff-1ad0bc6f
[...]This is the second post in a series exploring graph databases and PostgreSQL. The first post covered what graphs are, what can be modeled as a graph, and the landscape of graph database systems. This post moves from the general to the specific: how we query graphs, why SQL can be an awkward fit, and what SQL/PGQ changes. It ends with the PostgreSQL implementation, why it was reverted, and a few notes on the main features still to come.
In the previous post, we ended on the observation that graph databases come in many forms, and that they differ in how they store graphs and what data models they support. They also differ in how you question them.
Unlike the relational world, which converged on SQL early and kept it as the dominant standard for decades, the graph ecosystem produced a proliferation of query languages:
(a:Person)-[:KNOWS]->(b:Person);
g.V().hasLabel('Person').out('KNOWS');
For this series, the relevant one is SQL/PGQ[5], standardized as part of SQL:2023. It is an extension of SQL that lets you define property graphs over existing tables and query them with GQL- and PGQL-like pattern syntax inside a standard SELECT.
Consider a social network stored in two tables: person and knows.
A Cypher query asking for friends of friends looks like this:
MATCH (a:Person)-[:KNOWS]->(b)-[:KNOWS]->(c)
WHERE a.name = 'Alice'
RETURN c.name
The equivalent in plain SQL is a two-way self-join:
SELECT DISTINCT c.name
FROM person a
JOIN knows k1 ON k1.src = a.id
JOIN person b ON k1.dst = b.id
JOIN knows k2 ON k2.src = b.id
JOIN person c ON k2.dst = c.id
WHERE a.na[...]
A friend of mine is an excellent architect but a complete snob. He has four different databases in production: PostgreSQL for OLTP, ClickHouse for OpenTelemetry data and web analytics, Elasticsearch for full-text search in the application, and MongoDB, “for historical reasons.”
In between, there are ETL and CDC pipelines, plus on-call developers hunting for where exactly the results don’t match. To him, it’s all a well-thought-out architecture; to me, it’s needless complexity in maintenance and development.
The other day I told him what I’ve been working on lately.
“I’ve managed to plug ClickHouse’s MergeTree table engine into PostgreSQL as a table access method, on a vanilla database core, without a fork, and with a vectorized query executor reading from it.”
“What for? Just use ClickHouse and spare yourself the pain.”
“Because I need JOINs across lots of tables, consistent data throughout the system, and one platform that’s familiar to develop and maintain.”
“That’s not how it works. It’s either reliable or fast!”
“Well, I’ve managed to combine the best of both worlds.”
“Wait a minute. Didn’t you just port Greenplum into a Postgres extension and then write a vectorized executor on top of it? What do you need MergeTree for?”
“Vectorization grinds through columns fast, but it chokes the moment it has to read the data.”
The backstory began when I ported Apache Cloudberry to PostgreSQL 19 as a set of extensions, which got me ORCA, Greenplum’s cost-based planner, on PostgreSQL 19. On top of that query optimizer I developed pg_vexec, a vectorized executor without a single core patch.
Its nodes, VecScan, VecHashJoin, VecAgg and VecSort, get into a query plan through the database’s standard extension mechanisms and compete with the ordinary nodes on cost. ORCA prices the vector nodes with its own cost model. Inside, the executor processes batches of 1,024 rows in columnar form with kernels bound to function OIDs. Where there is no dedic
[...]The PostGIS Team is pleased to release PostGIS 3.7.0rc3! Best Served with PostgreSQL 19 RC 1 , GEOS 3.15.0 , postgis_tiger_geocoder 2025.2 , and address_standardizer.
This version requires GEOS 3.10+, PostgreSQL 14-19beta3, Proj 6.1+, and libgmp. To take advantage of all postgis extension features, GEOS 3.15+ is needed. To take advantage of all postgis_sfcgal extension features SFCGAL 2.3+ is needed. Python 3 is a new requirement to build the documentation. Visual examples in the docs are off by default. To build docs with them, configure with --with-visual-examples and make sure you have a running PostgreSQL instance; they are then generated automatically when building the docs (or explicitly with make -C regress visual-examples).
This release contains fixes since 3.7.0rc3 release.
Cheat Sheets:
This release is a release candidate of a major release, it includes bug fixes since PostGIS 3.6.4 and new features.
We’re continuing our review of the March CommitFest for PostgreSQL 19. This third article in the series covers the changes related to our DBA3 and DBS courses.
As a reminder, here’s the plan for our coverage of the final PostgreSQL 19 CommitFest:
Part 5.3 (DBA3, DBS) -- you are here 🙂
Part 5.4 (SQL, DEV1, DEV2)
For the earlier CommitFests, see: 2025-07, 2025-09, 2025-11, 2026-01.
Here’s what’s covered:
...
This is the first post in a series exploring graph databases and PostgreSQL’s evolving role in the graph landscape. Over four posts, we’ll move from foundational concepts through PostgreSQL’s SQL/PGQ implementation details and its future, benchmark comparisons against PostgreSQL graph solutions (e.g Apache AGE) , and finally the broader trajectory of relational systems absorbing graph workloads.
The series assumes no prior graph database expertise.
A graph is a set of things and a set of connections between them. That is the whole definition.
The things are called vertices (or nodes) and the connections are called edges (or relationships).
Formally, a graph $G $ is modeled as a tuple $(V, E)$, where $V $ is a set of vertices and $E $ is a subset of the Cartesian product of $V $ with itself, representing a set of edges:
$E \subseteq V \times V$
We find 3 kinds of graphs: directed, undirected, and weighted.
For a directed graph, an edge $E $ equals the ordered pair $(u, v)$ in $E $, where $u $ is the source vertex and $V $ is the destination vertex; an example is a Twitter follow, where the relationship has a specific direction:
edge $e = (u, v) \in E$
For an undirected graph, an edge $E $ is an unordered pair containing $u $ and $V $ in $E $; an example is a Facebook friendship, where the relationship is mutual:
edge $e = \{ u, v \} \in E$
A weighted graph extends this structure as a triple $(V, E, w)$, where $w $ is a function that asigns each edge in $E $ a real number (a weight), which is useful for representing distances, costs, or strengths:
triple $(V, E, w)$ where $w: E \to \mathbb{R}$
The power of the graph model lies in its universality. Once you start looking, graphs are everywhere.
The source code is available on Codeberg.
The extension is also available on PGXN.
The extension is also availabe through the PostgreSQL rpm packages.
This release adds a new view to the extension, pg_index_bloat.
Number of posts in the past two months
Number of posts in the past two months
Get in touch with the Planet PostgreSQL administrators at planet at postgresql.org.