Re: SQL:2011 application time

From: Paul Jungwirth <pj(at)illuminatedcomputing(dot)com>
To: Peter Eisentraut <peter(at)eisentraut(dot)org>, jian he <jian(dot)universality(at)gmail(dot)com>
Cc: PostgreSQL Hackers <pgsql-hackers(at)lists(dot)postgresql(dot)org>
Subject: Re: SQL:2011 application time
Date: 2023-11-17 18:39:58
Message-ID: af62a16b-9f88-4746-8e87-fdf0c242b266@illuminatedcomputing.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

On 11/9/23 05:47, Peter Eisentraut wrote:
> I went over the patch v17-0001-Add-temporal-PRIMARY-KEY-and-UNIQUE-constraints.patch in more
> detail.  Attached is a fixup patch that addresses a variety of cosmetic issues.

Thanks for the review! This all looks great to me, and it's applied in the attached patch (with one
typo correction in a C comment). The patch addresses some of jian he's feedback too but I'll reply
to those emails separately.

> Two areas that could be improved:
>
> 1) In src/backend/commands/indexcmds.c, get_index_attr_temporal_operator() has this comment:
>
> +    * This seems like a hack
> +    * but I can't find any existing lookup function
> +    * that knows about pseudotypes.
>
> This doesn't see very confident. ;-)  I don't quite understand this.  Is this a gap in the currently
> available APIs, do we need to improve something here, or does this need more research?

I've improved this a bit but I'm still concerned about part of it.

First the improved part: I realized I should be calling get_opclass_opfamily_and_input_type first
and passing the opcintype to get_opfamily_member, which solves the problem of having a concrete
rangetype but needing an operator that targets anyrange. We do the same thing with partition keys.

But I feel the overall approach is wrong: originally I used hardcoded "=" and "&&" operators, and
you asked me to look them up by strategy number instead. But that leads to trouble with core gist
types vs btree_gist types. The core gist opclasses use RT*StrategyNumbers, but btree_gist creates
opclasses with BT*StrategyNumbers. I don't see any way to ask ahead of time which class of strategy
numbers are used by a given opclass. So I have code like this:

*strat = RTEqualStrategyNumber;
opname = "equality";
*opid = get_opfamily_member(opfamily, opcintype, opcintype, *strat);

/*
* For the non-overlaps key elements,
* try both RTEqualStrategyNumber and BTEqualStrategyNumber.
* If you're using btree_gist then you'll need the latter.
*/
if (!OidIsValid(*opid))
{
*strat = BTEqualStrategyNumber;
*opid = get_opfamily_member(opfamily, opcintype, opcintype, *strat);
}

I do a similar thing for foreign keys.

But that can't be right. I added a scary comment there in this patch, but I'll explain here too:

It's only by luck that RTEqualStrategyNumber (18) is bigger than any BT*StrategyNumber. If I checked
in the reverse order, I would always find an operator---it would just sometimes be the wrong one!
And what if someone has defined a new type+opclass with totally different strategy numbers? As far
as I can tell, the gist AM doesn't require an opclass have any particular operators, only support
functions, so the strategy numbers are "private" and can vary between opclasses.

What we want is a way to ask which operators mean equality & overlaps for a given opclass. But the
strategy numbers aren't meaningful terms to ask the question.

So I think asking for "=" and "&&" is actually better here. Those will be correct for both core &
btree_gist, and they should also match user expectations for custom types. They are what you'd use
in a roll-your-own temporal constraint via EXCLUDE. We can also document that we implement WITHOUT
OVERLAPS with those operator names, so people can get the right behavior from custom types.

(This also maybe lets us implement WITHOUT OVERLAPS for more than rangetypes, as you suggested. See
below for more about that.)

It's taken me a while to grok the am/opclass/opfamily/amop interaction, and maybe I'm still missing
something here. Let me know if that's the case!

> 2) In src/backend/parser/parse_utilcmd.c, transformIndexConstraint(), there is too much duplication
> between the normal and the if (constraint->without_overlaps) case, like the whole not-null
> constraints stuff at the end.  This should be one code block with a few conditionals inside.  Also,
> the normal case deals with things like table inheritance, which the added parts do not.  Is this all
> complete?

Cleaned things up here. I agree it's much better now.

And you're right, now you should be able to use an inherited column in a temporal PK/UQ constraint.
I think I need a lot more test coverage for how this feature combines with inherited tables, so I'll
work on that.

> I'm not sure the validateWithoutOverlaps() function is needed at this point in the code.

Agreed, I removed it and moved the is-it-a-rangetype check into the caller.

> We don't even need to
> restrict this to range types.  Consider for example, it's possible that a type does not have a btree
> equality operator.  We don't check that here either, but let the index code later check it.

That is very interesting. Perhaps we allow anything with equals and overlaps then?

Note that we need more for FOR PORTION OF, foreign keys, and foreign keys with CASCADE/SET. So it
might be confusing if a type works with temporal PKs but not those other things. But if we
documented what operators you need for each feature then you could implement as much as you liked.

I like this direction a lot. It matches what I suggested in the conversation about multiple WITHOUT
OVERLAPS/PERIOD columns: rather than having foreign keys and FOR PORTION OF know how to find
n-dimensional "leftovers" we could leave it up the type, and just call a documented operator. (We
would need to add that operator for rangetypes btw, one that calls range_leftover_internal. It
should return an array (not a multirange!) of the untouched parts of the record.) This makes it easy
to support bi/tri/n-temporal, spatial, multiranges, etc.

(For spatial you probably want PostGIS instead, and I'm wary of over-abstracting here, but I like
how this "leaves the door open" for PostGIS to eventually support spatial PKs/FKs.)

Please let me know what you think!

Yours,

--
Paul ~{:-)
pj(at)illuminatedcomputing(dot)com

Attachment Content-Type Size
v18-0001-Add-temporal-PRIMARY-KEY-and-UNIQUE-constraints.patch text/x-patch 73.8 KB
v18-0002-Add-temporal-FOREIGN-KEYs.patch text/x-patch 132.3 KB
v18-0003-Add-UPDATE-DELETE-FOR-PORTION-OF.patch text/x-patch 133.2 KB
v18-0004-Add-CASCADE-SET-NULL-SET-DEFAULT-for-temporal-fo.patch text/x-patch 112.0 KB
v18-0005-Add-PERIODs.patch text/x-patch 129.5 KB

In response to

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Jeff Davis 2023-11-17 18:42:54 simplehash: preserve consistency in case of OOM
Previous Message Bruce Momjian 2023-11-17 18:39:46 Re: mxid_age() and age(xid) appear undocumented