TQuel Paper: Implementing Temporal Operators in Postgres

pjungwir1 pts0 comments

Illuminated Computing<br>TQuel Paper

Code like song

Illuminated Computing

TQuel Paper

2026-07-21<br>by Paul A. Jungwirth re<br>postgres, temporal

With UPDATE/DELETE FOR PORTION OF looking like it will land in Postgres 19, I’ve been thinking about next steps. I read a very helpful paper on temporal relational algebra last May by Richard Snodgrass. Here are some notes on it.

The paper was “An Overview of TQuel”. It’s Chapter 6 in Temporal Databases: Theory, Design, and Implementation from 1993.

TQuel was an extension to Quel, the query language for Ingres. Ingres, of course, was the predecessor to Postgres!

My main motivation reading this paper was to learn more about the algebraic identities of temporal relational operators. A query planner depends on such identities to transform your query into a more efficient shape. For instance, if you can filter rows before joining tables instead of after, you’ll get a much faster execution. The useful identities for regular relational operators are well-known, but what about temporal operators? If we ever want to support temporal joins and setops in Postgres, we have to figure that out.

I implemented some temporal operators in SQL in my temporal_ops extension. Since they are just SQL, I don’t have to worry about optimizer correctness. But it would be better to have dedicated executor nodes. That should get us closer to an optimal implementation. I want to teach that extension to inject CustomScans . . . somehow . . . maybe with a post-parser hook? Once I have that, I can experiment with planner transformations.

But as a first step, I’m trying to see what is in the research already. One surprise in Snodgrass’s paper was that TQuel valid-times are not intervals (like Postgres rangetypes or SQL:2011 PERIODs). Instead they are sets of “chronons”: all the times that the tuple is true, whether contiguous or not. I can see how that might be a more “pure” representation. There is something artificial to forcing the valid-time to be only a single contiguous stretch of time. Fortunately a set of chronons is exactly a multirange, so it is still something you could represent in Postgres.

A bigger surprise was that TQuel tracks valid-time for each attribute, instead of for the overall tuple. That is a bit more complicated! Actually it reminds me of how Date/Darwen/Lorentzos and Johnston both propose a separate-table-per-attribute structure (which Date calls “Sixth Normal Form”). You can see my temporal databases bibliography for more about that. There are hints in both of their books that they suspect this is asking too much from ordinary database users. And Snodgrass’s choice here made me worried that his results would be hard to apply to a practical RDBMS like Postgres.

But then I realized that any SQL:2011 valid-time tuple can be trivially represented in TQuel’s format: just copy the tuple valid-time onto each attribute. In fact Snodgrass has a term for that: a “homogeneous relation”. (Such TQuel relations still have multirange valid-time though.) That makes me hopeful that identities that are true in TQuel might still be true in SQL:2011. They might not be though, if TQuel operators on homogeneous relations don’t always have homogeneous relation results: if for those operators homogeneous relations aren’t “closed”.

Also TQuel has no nulls and no duplicates, but even that may not matter much. A valid-time never has null endpoints. Well with Postgres rangetypes we do use null to represent “unbounded”, but that has different behavior than a normal null. It is no different than using, say, 3000-01-01, except it works for any base type (unlike 'Infinity'). And I don’t think valid-time adds any further differences between duplicate and non-duplicate behavior. So the TQuel algebra should be as applicable as any non-temporal no-NULL no-duplicate algebra. At least it doesn’t add any new issues.

One result in the paper is that for homogeneous relations, TQuel operators are “snapshot reducible”. For such relations, “the valid-time operators ∪̂, −̂, ⨯̂, σ̂, and π̂ reduce to their snapshot counterparts.” In other words, if you want a final result at a single time t (a snapshot), you can evaluate temporal operators and then take the snapshot (which is slow) or take snapshots first, from all your base tables, and then evaluate non-temporal operators. You can “push down the qual” that valid_at @> now() (or whatever time you care about). It is a homomorphism:

σ(R ⨯̂ S) = σ(R) × σ(S)

I think this is the most common query shape against temporal tables, especially for complicated reports. You don’t need the whole history; you just want the result at a given moment. It means that if you are converting an existing app to use application-time tables, you don’t need temporal operators yet. You don’t even need to rewrite the queries. Just declare views that filter your history tables by now() (or by some variable), and run the queries on them. I talked about this in my PG DATA 2026 talk...

tquel temporal operators time postgres valid

Related Articles