Thread: Referential integrity doesn't work?

Referential integrity doesn't work?

From
Manuel Cano Muñoz
Date:
Hello.

I'm having a problem. I've created this SQL statements in a script, and
I "cat" the script to psql (cat script.sql | psql <database>).

[script begin]

BEGIN;
CREATE SEQUENCE conceptos_id_seq START 3 INCREMENT 1 MAXVALUE 2147483647
MINVALUE 1;

CREATE SEQUENCE contador_id_seq START 1 INCREMENT 1 MAXVALUE 2147483647
MINVALUE 1;

CREATE SEQUENCE tabla1_id_seq START 4 INCREMENT 1 MAXVALUE 2147483647
MINVALUE 1;

CREATE TABLE tabla1 (id int4 DEFAULT nextval('tabla1_id_seq'::text)
PRIMARY KEY, descripcion varchar(80), borrado boolean DEFAULT 'f' NOT
NULL);

CREATE TABLE conceptos (id int4 DEFAULT
nextval('conceptos_id_seq'::text) PRIMARY KEY, id_tabla1 int CONSTRAINT
conceptos_ref_id REFERENCES tabla1(id) ON UPDATE cascade ON DELETE
restrict DEFERRABLE INITIALLY DEFERRED, descripcion varchar(80), borrado
boolean DEFAULT 'f' NOT NULL);

CREATE TABLE contador (id int4 DEFAULT nextval('contador_id_seq'::text)
PRIMARY KEY, cuenta int4 DEFAULT '1' NOT NULL);

[script end]

As you can see there is a REFERENCES keyword, so it should prohibit
inserting data into "conceptos" table without a "id_tabla1" key, which
is the reference to "tabla_1".

Now there are some insert statements:

[script continues]

INSERT INTO tabla1 (id, descripcion, borrado) VALUES ('0', 'Este es el
primer registro', 'f');
INSERT INTO tabla1 (id, descripcion, borrado) VALUES ('1', 'Este es el
segundo registro', 'f');
INSERT INTO tabla1 (id, descripcion, borrado) VALUES ('2', 'Este es el
tercer registro', 'f');
INSERT INTO tabla1 (id, descripcion, borrado) VALUES ('3', 'Este es el
cuarto registro', 'f');
INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('0',
'', 'Este es el primer registro', 'f');
INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('1',
'', 'Este es el segundo registro', 'f');
INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('2',
'', 'Este es el tercer registro', 'f');
INSERT INTO contador (id, cuenta) VALUES ('0', '1');


[script end]

The "id_tabla1" field is left expreselly empty, but I think the
DEFERRABLE and INITIALLY DEFERRED keywords make this pass through. The
problem comes later:

[script continues]

CREATE OR REPLACE FUNCTION actualiza_cuenta()
RETURNS opaque
        AS '
BEGIN
        -- IF NEW.id_tabla = 0 THEN
        --      RAISE EXECPTION ''El campo id_tabla1 ha de tener un
valor'';
        -- END IF;
        UPDATE contador SET cuenta = cuenta + 1 WHERE id = 1;
        RETURN NEW;
END;

' LANGUAGE 'plpgsql';

CREATE TRIGGER actualiza_cuenta BEFORE INSERT OR UPDATE ON conceptos FOR
EACH row EXECUTE PROCEDURE actualiza_cuenta();

COMMIT;

[script end]

Note there are BEGIN and COMMIT statements in my script. This goes to
create the records without enforcing the referential integrity.


liman@linux:~/proyectos/cange> psql prueba
Welcome to psql, the PostgreSQL interactive terminal.

Type:  \copyright for distribution terms
       \h for help with SQL commands
       \? for help on internal slash commands
       \g or terminate with semicolon to execute query
       \q to quit

prueba=# select * from conceptos;
 id | id_tabla1 |         descripcion         | borrado
----+-----------+-----------------------------+---------
  0 |           | Este es el primer registro  | f
  1 |           | Este es el segundo registro | f
  2 |           | Este es el tercer registro  | f
(3 rows)


I've read that referential integrity is implemented with "hidden"
triggers. Can PostgreSQL have more than one trigger for the same table?
If so, what is going on here?

I've tested without trigger and still creates the records.

I've read the Joel Burton page on referential integrity:
http://techdocs.postgresql.org/techdocs/hackingreferentialintegrity.php

What I'm doing wrong?






Manuel Cano





Re: Referential integrity doesn't work?

From
Stephan Szabo
Date:
On 1 Aug 2002, Manuel Cano [ISO-8859-1] Mu�oz wrote:


> liman@linux:~/proyectos/cange> psql prueba
> Welcome to psql, the PostgreSQL interactive terminal.
>
> Type:  \copyright for distribution terms
>        \h for help with SQL commands
>        \? for help on internal slash commands
>        \g or terminate with semicolon to execute query
>        \q to quit
>
> prueba=# select * from conceptos;
>  id | id_tabla1 |         descripcion         | borrado
> ----+-----------+-----------------------------+---------
>   0 |           | Este es el primer registro  | f
>   1 |           | Este es el segundo registro | f
>   2 |           | Este es el tercer registro  | f
> (3 rows)

It looks to me that it's either treating id_tabla1 as
NULL (which passes the constraint) or 0 (which passes the
constraint).  What version are you using?



Re: Referential integrity doesn't work?

From
Manuel Cano Muñoz
Date:
El jue, 01-08-2002 a las 20:42, Stephan Szabo escribió:
>
> On 1 Aug 2002, Manuel Cano [ISO-8859-1] Muñoz wrote:
>
>
> > liman@linux:~/proyectos/cange> psql prueba
> > Welcome to psql, the PostgreSQL interactive terminal.
> >
> > Type:  \copyright for distribution terms
> >        \h for help with SQL commands
> >        \? for help on internal slash commands
> >        \g or terminate with semicolon to execute query
> >        \q to quit
> >
> > prueba=# select * from conceptos;
> >  id | id_tabla1 |         descripcion         | borrado
> > ----+-----------+-----------------------------+---------
> >   0 |           | Este es el primer registro  | f
> >   1 |           | Este es el segundo registro | f
> >   2 |           | Este es el tercer registro  | f
> > (3 rows)
>
> It looks to me that it's either treating id_tabla1 as
> NULL (which passes the constraint) or 0 (which passes the
> constraint).  What version are you using?
>
I don't understand you. Do you mean that if the id_tabla1 is
NULL or 0 the REFERENCE (not my trigger) constraint is not
enforced? That means that if I try to insert a record without
a value it will pass, and I think the referential integrity
is there to forbid just that.

Here is the insert statement that really insert a row even if
it provides no valid foreign key:

INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('0',
'', 'Este es el primer registro', 'f');
^ Empty foreign key.

There is a REFERENCE keyword that should prohibit this happening:

CREATE TABLE conceptos ( ...
id_tabla1 int CONSTRAINT conceptos_ref_id REFERENCES tabla1(id) ON
UPDATE cascade ON DELETE restrict DEFERRABLE INITIALLY DEFERRED, ...

This reference should force me to provide a valid id_table1 value,
but it doesn't.

Am I misunderstanding something?




Manuel Cano






Re: Referential integrity doesn't work?

From
Robert Treat
Date:
On Thu, 2002-08-01 at 16:59, Manuel Cano Muñoz wrote:
> > >
> > > prueba=# select * from conceptos;
> > >  id | id_tabla1 |         descripcion         | borrado
> > > ----+-----------+-----------------------------+---------
> > >   0 |           | Este es el primer registro  | f
> > >   1 |           | Este es el segundo registro | f
> > >   2 |           | Este es el tercer registro  | f
> > > (3 rows)
> >
> > It looks to me that it's either treating id_tabla1 as
> > NULL (which passes the constraint) or 0 (which passes the
> > constraint).  What version are you using?
> >
> I don't understand you. Do you mean that if the id_tabla1 is
> NULL or 0 the REFERENCE (not my trigger) constraint is not
> enforced? That means that if I try to insert a record without
> a value it will pass, and I think the referential integrity
> is there to forbid just that.
>
> Here is the insert statement that really insert a row even if
> it provides no valid foreign key:
>
> INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('0',
> '', 'Este es el primer registro', 'f');
> ^ Empty foreign key.
>
> There is a REFERENCE keyword that should prohibit this happening:
>
> CREATE TABLE conceptos ( ...
> id_tabla1 int CONSTRAINT conceptos_ref_id REFERENCES tabla1(id) ON
> UPDATE cascade ON DELETE restrict DEFERRABLE INITIALLY DEFERRED, ...
>
> This reference should force me to provide a valid id_table1 value,
> but it doesn't.
>
> Am I misunderstanding something?
>
>

I tried to duplicate your findings and two things of note occured.
First, on postgres 7.2 your function actualiza_cuenta() doesn't seem to
execute properly, which causes any inserts to fail by throwing an error.
This begs the question of what version your running. Second, when I
don't try and use your trigger, I find that postgres interprets your
insert

"INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES
('0', '', 'Este es el primer registro', 'f');"

AS

"INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES
('0', 0, 'Este es el primer registro', 'f');"

my assumption for this is that your trying to insert a blank '' into an
int field, which would not be valid, so postgres converts it to a 0 for
you. (perhaps becuase 0 = '' in some sense). Since you have an id of 0
in the tabla1 table, it allows the insert. If I change the '' to
something like 666, it fails. So AFAICT it is working as it should on
pg7.2.1, but you might be having trouble becuase your running an old
version, or there might be some syntax issues that might need to be
worked out in your script...

Robert Treat



Re: Referential integrity doesn't work?

From
"Nigel J. Andrews"
Date:
On 1 Aug 2002, Manuel Cano Muñoz wrote:

> El jue, 01-08-2002 a las 20:42, Stephan Szabo escribió:
> >
> > On 1 Aug 2002, Manuel Cano [ISO-8859-1] Muñoz wrote:
> >
> >
> > > liman@linux:~/proyectos/cange> psql prueba
> > > Welcome to psql, the PostgreSQL interactive terminal.
> > >
> > > Type:  \copyright for distribution terms
> > >        \h for help with SQL commands
> > >        \? for help on internal slash commands
> > >        \g or terminate with semicolon to execute query
> > >        \q to quit
> > >
> > > prueba=# select * from conceptos;
> > >  id | id_tabla1 |         descripcion         | borrado
> > > ----+-----------+-----------------------------+---------
> > >   0 |           | Este es el primer registro  | f
> > >   1 |           | Este es el segundo registro | f
> > >   2 |           | Este es el tercer registro  | f
> > > (3 rows)
> >
> > It looks to me that it's either treating id_tabla1 as
> > NULL (which passes the constraint) or 0 (which passes the
> > constraint).  What version are you using?
> >
> I don't understand you. Do you mean that if the id_tabla1 is
> NULL or 0 the REFERENCE (not my trigger) constraint is not
> enforced? That means that if I try to insert a record without
> a value it will pass, and I think the referential integrity
> is there to forbid just that.
>
> Here is the insert statement that really insert a row even if
> it provides no valid foreign key:
>
> INSERT INTO conceptos (id, id_tabla1, descripcion, borrado) VALUES ('0',
> '', 'Este es el primer registro', 'f');
> ^ Empty foreign key.
>
> There is a REFERENCE keyword that should prohibit this happening:
>
> CREATE TABLE conceptos ( ...
> id_tabla1 int CONSTRAINT conceptos_ref_id REFERENCES tabla1(id) ON
> UPDATE cascade ON DELETE restrict DEFERRABLE INITIALLY DEFERRED, ...
>
> This reference should force me to provide a valid id_table1 value,
> but it doesn't.
>
> Am I misunderstanding something?
>

I've tried this on my 7.2.1 system and I get '0' returned in the column you see
as empty, i.e. NULL. The full version information is:

testindex=# select version();
                            version
---------------------------------------------------------------
 PostgreSQL 7.2.1 on i686-pc-linux-gnu, compiled by GCC 2.95.2
(1 row)


Manuel, what version are you using?


My results seem to give wrong results also, even when I create the table having
removed the INITIALLY DEFERRED words from the column definition. So I would say
that Manuel was somehow seeing the empty string, '', translated into NULL but
then why am I seeing it changed into 0? Oh wait, my 0 is going to be the result
of atoi() on an empty string, which does pass the RI test. So I suppose the
question is which version is Manuel using and why is it giving NULL instead of
zero?

I've just tested this on my 7.3dev, which is a few weeks old now, but I get the
0 so I presume Manuel is using some older version.

One could say that this atoi() conversion of an empty string is an accident
waiting to happen when it comes to data integrity. Other than detecting it and
giving NULL, which I don't know how to even start working out how to do, then I
think the best that can be done is to have some mention in the documentation
somewhere that using 0 for a valid target item of a foriegn key should be
avoided.

Sorry for the length of this message, I couldn't really see anything in the
quoted section that was relevent and could therefore be cut and as usual I've
rambled on in what I've added.


--
Nigel J. Andrews
Director

---
Logictree Systems Limited
Computer Consultants


Re: Referential integrity doesn't work?

From
"Nigel J. Andrews"
Date:
On 1 Aug 2002, Robert Treat wrote:
>
> ...
> I tried to duplicate your findings and two things of note occured.
> First, on postgres 7.2 your function actualiza_cuenta() doesn't seem to
> execute properly, which causes any inserts to fail by throwing an error.
> This begs the question of what version your running...

I forgot to mention I tried the script having commented out the function and
trigger because I didn't have pl/pgsql loaded in the database I was using for
it.

However, the mention of the function failing raises a question for me. I don't
think I've seen this in the documentation, my apologies if it's there or a
blindingly obvious fact, so does the trigger get run if I do something like:

BEGIN;
INSERT INTO mytable VALUES (...);
CREATE TRIGGER mytrigger BEFORE INSERT OR UPDATE ON mytable blah, blah, blah...
COMMIT;

I would have said not but Robert's comment seems to be saying it is.


--
Nigel J. Andrews
Director

---
Logictree Systems Limited
Computer Consultants


Re: Referential integrity doesn't work?

From
Robert Treat
Date:
On Thu, 2002-08-01 at 17:04, Nigel J. Andrews wrote:
>
> On 1 Aug 2002, Robert Treat wrote:
> >
> > ...
> > I tried to duplicate your findings and two things of note occured.
> > First, on postgres 7.2 your function actualiza_cuenta() doesn't seem to
> > execute properly, which causes any inserts to fail by throwing an error.
> > This begs the question of what version your running...
>
> I forgot to mention I tried the script having commented out the function and
> trigger because I didn't have pl/pgsql loaded in the database I was using for
> it.
>
> However, the mention of the function failing raises a question for me. I don't
> think I've seen this in the documentation, my apologies if it's there or a
> blindingly obvious fact, so does the trigger get run if I do something like:
>
> BEGIN;
> INSERT INTO mytable VALUES (...);
> CREATE TRIGGER mytrigger BEFORE INSERT OR UPDATE ON mytable blah, blah, blah...
> COMMIT;
>
> I would have said not but Robert's comment seems to be saying it is.
>

I think not is correct. It only attempted to pull the trigger upon
subsequent inserts I tried (having already issued the commit).

Robert Treat