SQL:2011 application time
Paul A Jungwirth <pj@illuminatedcomputing.com>
Commits
GET /api/v1/messages/:b64id/commits
the thread's linked commits as JSON, with link sources.
API reference →
-
Rename gist stratnum support function
- 32edf732e8dc 18.0 landed
-
Remove support for temporal RESTRICT foreign keys
- b83e8a2ca2eb 18.0 landed
-
Cache NO ACTION foreign keys separately from RESTRICT foreign keys
- 9926f854d077 18.0 landed
-
Fix NO ACTION temporal foreign keys when the referenced endpoints change
- 1772d554b089 18.0 landed
-
Improve whitespace in without_overlaps test
- 888d4523f0c2 18.0 landed
-
Tests for logical replication with temporal keys
- 939b0908c87a 18.0 landed
-
Support for GiST in get_equal_strategy_number()
- 74edabce7a33 18.0 landed
-
Make the conditions in IsIndexUsableForReplicaIdentityFull() more explicit
- 13544e790ef8 18.0 landed
-
Replace get_equal_strategy_number_for_am() by get_equal_strategy_number()
- a2a475b011cf 18.0 landed
-
Improve internal logical replication error for missing equality strategy
- 321c287351f7 18.0 landed
-
Simplify IsIndexUsableForReplicaIdentityFull()
- 7727049e8f66 18.0 landed
-
Fix ALTER TABLE / REPLICA IDENTITY for temporal tables
- 79b575d3bc09 18.0 landed
-
doc: Update pg_constraint.conexclop docs for WITHOUT OVERLAPS
- f683ba0867da 18.0 landed
-
doc: Add PERIOD to ALTER TABLE reference docs
- d56af4c882e2 18.0 landed
-
doc: Add WITHOUT OVERLAPS to ALTER TABLE reference docs
- bf621059500b 18.0 landed
-
Add temporal FOREIGN KEY contraints
- 89f908a6d0ac 18.0 landed
- 34768ee36165 17.0 landed
-
Add temporal PRIMARY KEY and UNIQUE constraints
- fc0438b4e805 18.0 landed
- 46a0cd4cefb4 17.0 landed
-
Add stratnum GiST support function
- 7406ab623fee 18.0 landed
- 6db4598fcb82 17.0 landed
-
Avoid crashing when a JIT-inlined backend function throws an error.
- 5d6c64d29097 17.0 cited
-
Revert temporal primary keys and foreign keys
- 8aee330af55d 17.0 landed
-
Fix ON CONFLICT DO NOTHING/UPDATE for temporal indexes
- 144c2ce0cc75 17.0 landed
-
Add test for REPLICA IDENTITY with a temporal key
- 482e108cd38d 17.0 landed
-
Use half-open interval notation in without_overlaps tests
- 5577a71fb0cc 17.0 landed
-
Use daterange and YMD in without_overlaps tests instead of tsrange.
- a88c800deb6f 17.0 landed
-
Rename pg_constraint.conwithoutoverlaps to conperiod
- 030e10ff1a36 17.0 landed
-
Fix comment on gist_stratnum_btree
- 86232a49a437 17.0 landed
-
Add missing TAP test name
- 1ab763fc22ad 16.0 cited
-
Improve error handling of HMAC computations
- 5513dc6a304d 15.0 cited
-
Rename functions to avoid future conflicts
- ee419607381d 15.0 landed
Attachments
- v4-0001-Add-PERIODs.patch (application/octet-stream) patch v4-0001
- v4-0003-Add-UPDATE-DELETE-FOR-PORTION-OF.patch (application/octet-stream) patch v4-0003
- v4-0002-Add-temporal-PRIMARY-KEY-and-UNIQUE-constraints.patch (application/octet-stream) patch v4-0002
- v4-0004-Add-temporal-FOREIGN-KEYs.patch (application/octet-stream) patch v4-0004
Hello,
Here is a set of patches to add SQL:2011 application-time support (aka
valid-time).
Previous discussion was on
https://www.postgresql.org/message-id/20200930073908.GQ1996@paquier.xyz
but I thought I should update the email subject.
There are four patches here:
- Add PERIODs.
- Add temporal PRIMARY KEY and UNIQUE constraints.
- Add UPDATE/DELETE FOR PORTION OF.
- Add temporal FOREIGN KEYs.
The PERIOD patch is mostly Vik Fearing's work (submitted here a few
years ago), so he should get credit for that!
All patches have tests & documentation. I do have a few more tests I
plan to write, and there are some questions for reviewers embedded in
patches (mostly about when to lock and/or copy data structures). I've
tried to format these as C++ comments to indicate they should be
removed before committing.
Throughout I've made sure that wherever SQL:2011 accepts a PERIOD, we
also accept a range column. So in all these examples valid_at could be
either one:
PRIMARY KEY (id, valid_at WITHOUT OVERLAPS)
FOREIGN KEY (id, PERIOD valid_at)
REFERENCES too (id, PERIOD valid_at)
FOR PORTION OF valid_at FROM t1 TO t2
Range types are superior to PERIODs in many ways, so I think we should
support both. For example you can SELECT them, WHERE them, GROUP BY
them, pass them to functions, return them from functions, do
arithmetic on them, index them, etc.
In fact whether you use a PERIOD or a range, the implementation uses
ranges a lot, since they are such a good fit. A temporal PK is really
an exclusion constraint, etc. When you define a PERIOD, we find a
matching range type and store its oid on the period record. If there
are more than one range type we raise an error, but you can give a
rangetype option to remove the ambiguity. This means we support
PERIODs of any type (basically), not just dates & timestamps.
According to SQL:2011 we should automatically set any columns used by
a PERIOD to NOT NULL. I've ignored that requirement, since permitting
nullable columns is strictly greater functionality: you can always
make the columns NOT NULL if you like. Interpreting NULLs as unbounded
fits better with our range types, and it means you don't have to use
sentinels. (Timestamp has +-Infinity, but many types don't.) Oracle
also accepts null PERIOD columns and treats them the same way. I don't
think it would break anything though to force PERIOD columns to NOT
NULL. If you hate sentinels you can just use range columns. But still
I see no reason to force this on our users.
In the FOR PORTION OF bounds I accept MINVALUE and MAXVALUE as special
tokens. I chose the names to be consistent with partition syntax. This
isn't part of the standard but seems nice.
Here are a few other things to discuss:
- My patch only adds application time. There is a separate patch to
add system time: https://commitfest.postgresql.org/33/2316/ I don't
foresee any serious conflicts between our work, and in general I think
each patch implements its functionality at an appropriate (but
different) level of abstraction. But I haven't looked at that patch
recently. I'll try to give some comments during this commitfest. The
one place they probably overlap is with defining PERIODs. Since
system-time periods *must* be named SYSTEM_TIME, even that overlap
should be slight, but it still might be worth accepting the PERIOD
patch here before adopting either. Even SYSTEM_TIME ought to be
recorded in information_schema.periods IIRC.
- The biggest thing remaining to do is to add support for partitioned
tables. I would love some help with that if anyone is interested.
- Since temporal PKs are implemented with exclusion constraints they
use GiST indexes, so you can't really use them without the btree_gist
extension (unless *all* your key parts are ranges---which is how we
test exclusion constraints). Personally I'm okay with this, since even
exclusion constraints are pretty useless without that extension. But
it seems like something to talk about.
- At PgCon 2020 Vik suggested a different way of querying for FK
checks, which he used in his own temporal tables extension. It is more
complicated but he thinks it may be faster. I plan to try both and run
some benchmarks. I'm not sure whether his approach will work with
CASCADE/SET NULL/SET DEFAULT---but I haven't looked at it in a while.
- It is hard to avoid a shift/reduce conflict in FOR PORTION OF
<period_or_range> FROM <expr> TO <expr> because expressions may
contain date INTERVALs that also may contain TO. So this is an error:
FOR PORTION OF valid_at
FROM '2018-03-01' AT TIME ZONE INTERVAL '2' HOUR
TO '2019-01-01'
but this works:
FOR PORTION OF valid_at
FROM ('2018-03-01' AT TIME ZONE INTERVAL '2' HOUR)
TO '2019-01-01'
I'm personally satisfied with that, but if anyone thinks it can be
improved please let me know. It would be nice if the parser were smart
enough to see that without a second TO, it must belong to FOR PORTION
OF, not the interval. But *I'm* not smart enough to teach it that. :-)
If only it could have a greater lookahead. . . .
- Normally we return the number of rows affected by an UPDATE/DELETE.
What do you think we should do when a FOR PORTION OF causes extra rows
to be inserted? I'm not doing anything special here today. After all
foreign keys don't do anything extra when they CASCADE/SET (to my
knowledge). Also I think adding info about the inserted rows might be
annoying, since I'd have to communicate it from within the trigger
function. I'm really hoping no one asks for this.
- Since PERIODs are a weird neither-fish-nor-foul thing (parsed a lot
like a column, but also behaving like a constraint), they add a lot of
tedious if-statements when they are used by an index or constraint. In
many places I've used a zero attnum to signal that a component is
really a PERIOD. (Range columns are easy since they really are a
column.) I feel this approach is pretty ugly, so I will probably
experiment a bit with a different way. If anyone else wants to take
this on though, I'm grateful for the help.
- It would be really cool if ON CONFLICT DO UPDATE had a temporal
variant so it would INSERT the missing durations and UPDATE the
existing ones. That's what Tom Johnston said the standard should have
required in *Bitemporal Data*, and it does make things a lot easier on
the client side. But that is something to do in a later patch. . . .
Yours,
Paul