Thread: RI within PLPGSQL

RI within PLPGSQL

From
"cnliou"
Date:
Hi!

It looks to me a referential integrity problem (only?)
within PLPGSQL. Plesase see the test result below.

Thank you!
CN
=======
CREATE TABLE test1(c1 INTEGER PRIMARY KEY) WITHOUT OIDS;

CREATE TABLE test2
( c1 INTEGER,
  c2 INTEGER,
  PRIMARY KEY (c1,c2),
  CONSTRAINT ctest2 FOREIGN KEY (c1) REFERENCES test1 (c1)
ON UPDATE CASCADE ON DELETE CASCADE
) WITHOUT OIDS;

CREATE OR REPLACE FUNCTION f1(int) RETURNS VOID AS '
BEGIN
  DELETE FROM test1 WHERE c1= $1;

  INSERT INTO test1 VALUES($1);
  INSERT INTO test2 VALUES($1,2);
  RETURN;
END' LANGUAGE 'plpgsql' STABLE;

CREATE OR REPLACE FUNCTION f2(int) RETURNS VOID AS '
BEGIN
  DELETE FROM test2 WHERE c1= $1;
  DELETE FROM test1 WHERE c1= $1;

  INSERT INTO test1 VALUES($1);
  INSERT INTO test2 VALUES($1,2);
  RETURN;
END' LANGUAGE 'plpgsql' STABLE;

db1=# select f1(1);
 f1
----

(1 row)

db1=# select * from test1; select * from test2;
 c1
----
  1
(1 row)

 c1 | c2
----+----
  1 |  2
(1 row)

db1=# select f1(1);
ERROR:  duplicate key violates unique constraint
"test2_pkey"
CONTEXT:  PL/pgSQL function "f1" line 5 at SQL statement
db1=# select * from test1; select * from test2;
 c1
----
  1
(1 row)

 c1 | c2
----+----
  1 |  2
(1 row)

db1=# select f2(1);
 f2
----

(1 row)

db1=# select * from test1; select * from test2;
 c1
----
  1
(1 row)

 c1 | c2
----+----
(0 rows)

Re: RI within PLPGSQL

From
Stephan Szabo
Date:
On Fri, 12 Dec 2003, cnliou wrote:

> It looks to me a referential integrity problem (only?)
> within PLPGSQL. Plesase see the test result below.

There have been discussions in the past about when cascade events
should occur.  The code currently does what I believe was last
agreed upon, although its behavior is fairly wierd for deferred
constraints and functions.  Right now the cascade happens at the
end of the full statement (in this case the call to the function)
which is why you get a key constraint error in the second call to f1
and why the later inserted row is removed in f2.

Re: RI within PLPGSQL

From
"cnliou"
Date:
Thank you very much for your explanation!

¡° Include¡m"Stephan Szabo" <sszabo@megazone.bigpanda.com>¡n
wrote:
>There have been discussions in the past about when cascade
events
>should occur.  The code currently does what I believe was
last
>agreed upon, although its behavior is fairly wierd for
deferred
>constraints and functions.  Right now the cascade happens
at the
>end of the full statement (in this case the call to the
function)
>which is why you get a key constraint error in the second
call to f1
>and why the later inserted row is removed in f2.

It sounds to me that the only solution to my case is
executing

DELETE FROM referenced_table

and

INSERT INTO referencing_table

in seperate transactions. Please correct me if I am wrong.

I also feel it might be a good idea to include an example
like the one in my previous message in the documentation so
that this question hopefully will not be asked repeatedly.

Regards,

CN

Re: RI within PLPGSQL

From
Stephan Szabo
Date:
On Sat, 13 Dec 2003, cnliou wrote:

> Thank you very much for your explanation!
>
> =A1=B0 Include=A1m"Stephan Szabo" <sszabo@megazone.bigpanda.com>=A1n
> wrote:
> >There have been discussions in the past about when cascade
> events
> >should occur.  The code currently does what I believe was
> last
> >agreed upon, although its behavior is fairly wierd for
> deferred
> >constraints and functions.  Right now the cascade happens
> at the
> >end of the full statement (in this case the call to the
> function)
> >which is why you get a key constraint error in the second
> call to f1
> >and why the later inserted row is removed in f2.
>
> It sounds to me that the only solution to my case is
> executing
>
> DELETE FROM referenced_table
>
> and
>
> INSERT INTO referencing_table
>
> in seperate transactions. Please correct me if I am wrong.

I think they only need to be in separate outer statements for non-deferred
triggers.  It's just that the full set of triggered actions for the
function count as part of the one statement that calls it.

So, from psql, sending separate statements
DELETE FROM ... ;
INSERT INTO ... ;
should work, but a function body
'DELETE FROM ...;
 INSERT INTO ...;'
counts as one statement and so the delete action happens after the insert.

> I also feel it might be a good idea to include an example
> like the one in my previous message in the documentation so
> that this question hopefully will not be asked repeatedly.

I think the behavior of this hasn't entirely solidified yet. It's still
possible that it'll change as although we came to a behavior set, if it
can be shown to break the spec's requirements, it'll be changed.