Re: Help with transactions - Mailing list pgsql-general

From Stephen Howie
Subject Re: Help with transactions
Date
Msg-id 423EEF23.300@centwire.com
Whole thread Raw
In response to Re: Help with transactions  (Michael Fuhr <mike@fuhr.org>)
Responses Re: Help with transactions
List pgsql-general
Thanks for the reply.

I've notice a couple things.  I ran a couple test and at first I
couldn't duplicate my error on some test tables.  But I then added
inheritance to one of the tables and thats when I got the error again.
It looks like there is an error when obtaining the seq id (nextval) from
the original table and using it on a table that has a foreign key to the
original table by inserting it into the table that inherits the original
table, within a transaction.  What I think is happening is since the
insert is on the inherited table the foreign key doesn't see the insert
into the original table until the transaction is committed.

Here is a sample of how I duplicated my error.

By running

SELECT insert_data('A123456789','A','A2345');

on the below schema layout I get this error

ERROR:  insert or update on table "table2" violates foreign key
constraint "table2_fk_id_fk"

=====START========
CREATE TABLE table1 (
    id serial NOT NULL,
    data character(10) NOT NULL
);

CREATE TABLE table2 (
    id serial NOT NULL,
    fk_id integer NOT NULL,
    more_data character(5) NOT NULL
);

CREATE TABLE inherit_table (
    even_more_data character(1) NOT NULL
)
INHERITS (table1);

ALTER TABLE ONLY table1
    ADD CONSTRAINT table1_pkey PRIMARY KEY (id);

ALTER TABLE ONLY table2
    ADD CONSTRAINT table2_pkey PRIMARY KEY (id);

ALTER TABLE ONLY table2
    ADD CONSTRAINT table2_fk_id_fk FOREIGN KEY (fk_id) REFERENCES
table1(id) ON UPDATE RESTRICT ON DELETE RESTRICT;

CREATE VIEW view_table1 AS
SELECT table1.id, table1.data
FROM table1;

CREATE VIEW view_table2 AS
SELECT table2.id, table2.fk_id, table2.more_data
FROM table2;

CREATE VIEW view_inherit_table AS
SELECT inherit_table.id, inherit_table.data, inherit_table.even_more_data
FROM inherit_table;

CREATE RULE view_table1_insert AS ON INSERT TO view_table1 DO INSTEAD
INSERT INTO table1 (id, data) VALUES (new.id, new.data);
CREATE RULE view_table2_insert AS ON INSERT TO view_table2 DO INSTEAD
INSERT INTO table2 (id, fk_id, more_data) VALUES (new.id, new.fk_id,
new.more_data);
CREATE RULE view_inherit_table_insert AS ON INSERT TO view_inherit_table
DO INSTEAD INSERT INTO inherit_table (id, data, even_more_data) VALUES
(new.id, new.data, new.even_more_data);

CREATE FUNCTION insert_table2 (integer, character) RETURNS integer
    AS '
DECLARE

       table2_id INTEGER;
       table1_id ALIAS FOR $1;
       newdata ALIAS FOR $2;

BEGIN

       table2_id = nextval(''table2_id_seq'');
       INSERT INTO view_table2 (id, fk_id, more_data) VALUES (table2_id,
table1_id, newdata);

       RETURN table2_id;

END;
'
    LANGUAGE plpgsql SECURITY DEFINER;


CREATE FUNCTION insert_inherit_table (character, character) RETURNS integer
    AS '
DECLARE

       table1_id INTEGER;
       newdata ALIAS FOR $1;
       new_even_more_data ALIAS FOR $2;

BEGIN

       table1_id = nextval(''public.table1_id_seq'');
       INSERT INTO view_inherit_table (id, data, even_more_data) VALUES
(table1_id, newdata, new_even_more_data);

       RETURN table1_id;
END;
'
    LANGUAGE plpgsql SECURITY DEFINER;

CREATE FUNCTION insert_data (character, character, character) RETURNS
boolean
    AS '
DECLARE

       newdata1 ALIAS FOR $1;
       newdata2 ALIAS FOR $2;
       newdata3 ALIAS FOR $3;
       table1_id INTEGER = 0;
       table2_id INTEGER = 0;

BEGIN

       table1_id = insert_inherit_table(newdata1, newdata2 );

       RAISE LOG ''Table1 ID: %'', table1_id;

       table2_id = insert_table2(table1_id, newdata3);

       IF table2_id > 0 THEN
           RETURN TRUE;
       ELSE
           RETURN FALSE;
       END IF;

END;
'
    LANGUAGE plpgsql SECURITY DEFINER;
========END===========

Also, in my original schema I'm getting an increment of 2 every time I
run nextval.  I can't duplicate this yet but I'm looking into it.
Possibly my error somewhere in the function.

Thanks

-------------------------
Stephen Howie




Michael Fuhr wrote:

>On Fri, Mar 18, 2005 at 09:22:52AM -0500, Stephen Howie wrote:
>
>
>
>>I have a java program that excepts print streams and inserts in into a
>>spool table as a bytea.  This fires a pl/pgsql trigger that passes the
>>bytea to a pl/perl function to process the bytea and spits the results
>>as an array back.  It then proceeds to insert the data into multiple
>>tables.  Problem is that two of the tables data is inserted into inside
>>this transaction, one has a foreign key to the other.  As you can guess
>>I get a foreign key violation because the transaction is not committed
>>
>>
>
>A transaction doesn't need to be committed for operations to see
>the effects of previous operations in the same transaction, but
>there could be visibility problems related to what happens when.
>
>Could you post the simplest self-contained example that demonstrates
>the problem?  It'll be easier to understand the interactions if we
>can see the exact code.  In simple tests I successfully did what
>you describe, so apparently my experiment didn't duplicate what
>you're doing.
>
>What version of PostgreSQL are you using?
>
>
>
>>and as far as I understand PostgreSQL does not support dirty reads or
>>nested transactions.
>>
>>
>
>You probably need neither.  PostgreSQL 8.0 supports nested transactions
>in the form of savepoints.
>
>
>
>>1) what is there another way to handle this transaction that would
>>resolve this violation without using dirty reads and
>>
>>
>
>Let's see what you're doing before thinking dirty reads would be a
>solution.  Or perhaps you're thinking about them in a slightly
>different sense than transaction literature usually describes.
>
>
>
>>2) It looks like running the trigger after insert on a table does
>>not run as a separate transaction.
>>
>>
>
>Why would it?
>
>
>
>>Is the insert to that table suppose to fail if the trigger fails?
>>To me that defeats the purpose of having a trigger after insert.
>>
>>
>
>An after trigger is about visibility.  Here's an excerpt from the
>"Triggers" chapter in the documentation:
>
>  Row after triggers are most sensibly used to propagate the updates
>  to other tables, or make consistency checks against other tables.
>  The reason for this division of labor is that an after trigger
>  can be certain it is seeing the final value of the row, while a
>  before trigger cannot; there might be other before triggers firing
>  after it.
>
>An operation ain't over 'til it's over: if an after trigger doesn't
>like what it sees, it can still abort the operation by raising an
>exception.  That doesn't defeat the purpose -- it's part of the
>purpose.
>
>
>

Attachment

pgsql-general by date:

Previous
From: Richard Huxton
Date:
Subject: Re: Time Stamp
Next
From: Hrishikesh Deshmukh
Date:
Subject: Re: Time Stamp