Foreign Key work for 7.3+

From: Stephan Szabo <sszabo(at)megazone23(dot)bigpanda(dot)com>
To: <pgsql-hackers(at)postgresql(dot)org>
Subject: Foreign Key work for 7.3+
Date: 2002-02-20 17:28:03
Message-ID: 20020220092428.K1524-100000@megazone23.bigpanda.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Here are the things I'd like to get feedback on doing to the foreign
key constraint triggers and support code. It's relatively high level
since I'm at the beginning stages of looking at the code that'd be
involved. This might therefore be a bit premature, but I figured I'd see
if anyone saw anything obviously wrong and there were a few places where
I'm uncertain what behavior is better or correct so I'm looking for
advice.

This isn't necessarily a list of things for 7.3 since I don't expect that
I'd be able to finish all of them unless 7.3's devel goes on like 7.2's.

* Rewrite foreign key triggers to remove dependencies on SPI
Use the index and heap scanning functions to check the table. This
should let us remove the ReferentialIntegritySnapshotOverride global
and give us more control over what the triggers are doing by doing
the checks we need directly. My current test code duplicates a bunch
of work (finding an index to scan if possible, etc) that other areas
of the code have to be doing, but that's mostly a matter of finding
what I can use from other sections. Hopefully this will also eventually
allow us to move away from the current locking method (although I haven't
looked at it yet)

* Fix remaining visibility problems
There are a couple of places where we have problems with this.
For pk updates and deletes with no action we need to make sure
that there wasn't a row updated or inserted with the old key values
that would satisfy the constraint. For fk inserts/updates we need
to make sure that we're not seeing an intermediate state of the
row (ie it was changed again before the check was done). Deferred
cascades have a few possible broken cases (I'm not sure if these are
actual errors or not, but they seem like it -- see questions below).
Simply checking that the row we're checking is still valid on the fk
inserts/updates causes some similar behavior.

* Switch over to oids and attnos for new constraints
Make the real functions for the constraints take oids and attnos.
We'll need to either hack around when we see the create constraint
trigger to use the new form or keep functions by the old names/oids which
take names, but make those effectively wrappers around the oid versions
(look up the numbers and pass those in to the new ones). In any case
new constraints will use the oids and attnos version.

* Make checking an entire table faster for Alter Table
Right now alter table runs the insert/update trigger on the fk table
once per existing fk row on alter table. If we don't care about the
possible disk space usage, we can make this faster by going through both
tables in sorted key order (the disk space would be if we had to go
to an explicit sort). The other option is to only do this form if we
had an index on each table we could use to scan in order.

* Change dumps to dump fk constraints as alter table statements by default
Make dumps write out alter table statements at the end rather than
create constraint triggers. We may wish to provide an option to allow
the older style create constraint trigger dumps for speed.

* Extend fk constraints to work with inheritance
Make fk constraints inherit properly with both fk and pk base tables.
This will probably mean making the appropriate triggers on the child
tables involved as well as scanning the additional tables when checks and
changes are needed. For right now, I'd say we'd want to require that the
child tables at least also have unique constraints across the key.

* Fixes on unique index checks for fk constraint checking
Right now I believe unique partial and functional indexes are considered
sufficient to be targets of an fk constraint. I think this is usually
wrong for partial indexes and that this may be wrong for functional
indexes. Any thoughts?

* Make a central place for storing fk constraint info?
Keep track of fk constraint info in a centralized place keeping a more
useable form of the tables and columns involved as well as the misc. data
(match type, deferrability). We'd probably also want to keep references
to the triggers involved in the constraint. This would make some of the
things (dumping fk constraints as alter table, inheritance) easier I
believe since we wouldn't have to match up constraint triggers to
determine the details of the constraint, but it would probably require a
hack for checking create constraint trigger statements to see if they're
fk constraints.

* Match partial
Match partial is rather complicated and could be even more so depending
on the answers to the behavioral questions in the notes below, but it'd
be good to do it if we can.

Behavioral questions:
* Is this current behavior correct or should fk contain a row with the
key value 1 at the end of the sequence?
create table pk(a int unique);
NOTICE: CREATE TABLE / UNIQUE will create implicit index 'pk_a_key' for
table

'pk'
NOTICE: CREATE TABLE / UNIQUE will create implicit index 'pk_a_key' for
table

'pk'
CREATE

create table fk(a int references pk(a) on delete cascade initially
deferred);
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
check(s)
NOTICE: CREATE TABLE will create implicit trigger(s) for FOREIGN KEY
check(s)
CREATE

insert into pk values (1);
INSERT 16645 1

begin;
BEGIN

delete from pk where a=1;
DELETE 1

insert into pk values (1);
INSERT 16646 1

insert into fk values (1);
INSERT 16647 1

end;
COMMIT

select * from pk;
a
---
1
(1 row)

select * from fk;
a
---
(0 rows)

* Using the tables from above. Should the following have no rows in fk
or a single row containing 2?
delete from fk;
DELETE 1

delete from pk;
DELETE 1

insert into pk values (1);
INSERT 16668 1

insert into fk values (1);
INSERT 16669 1

begin;
BEGIN

delete from pk where a=1;
DELETE 1

insert into pk values (2);
INSERT 16670 1

update fk set a=2;
UPDATE 1

end;
COMMIT

select * from pk;
a
---
2
(1 row)

select * from fk;
a
---
2
(1 row)

Responses

Browse pgsql-hackers by date

  From Date Subject
Next Message Cyril Samovskiy 2002-02-20 18:23:27 foreign key is from different tables - what to do?
Previous Message Jean-Michel POURE 2002-02-20 17:13:03 Re: [ODBC] UTF-8 data migration problem in Postgresql 7.2