Thread: integrity and inherit

integrity and inherit

From
Bo Lorentsen
Date:
Hi ...

I hope someone will be so kind to take the time to explain to me what I
have been doing wrong in this example.

I have used PosgreSQL now for a while, and I really like to begin to use
some more of its integrity checks. The database I have been creating
uses the inherit functionality of PG, and this works out beautiful :-)

But I can't make the combination work ! To point out the problem i have
made this small example :

CREATE TABLE base (
 id INTEGER PRIMARY KEY,
 name TEXT
);

CREATE TABLE derived (
 value INTEGER
) INHERITS( base );

CREATE TABLE test_ref (
 ref_id INTEGER REFERENCES base,
 name TEXT
);

This is the setup, and all I get is a few "NOTICE", about indirect check
and indexes, really nice. Time to use it :-)

INSERT INTO base (id, name) VALUES( 1, 'mother' );
INSERT INTO derived (id, name, value) VALUES( 2, 'child', 42 );

INSERT INTO test_ref (ref_id, name) VALUES( 1, 'mother' );

All this works, but this one dont :

INSERT INTO test_ref (ref_id, name) VALUES( 2, 'child' );

Here it says :

ERROR:  <unnamed> referential integrity violation - key referenced from
test_ref not found in base

But a quick :

    SELECT * FROM base;

shows :

 id |  name
----+--------
  1 | mother
  2 | child

So as fare as I can see, this isn't correct, the id value number 2 is in
"base" table.

Could someone please tell me what I have done wrong ?

/BL


Re: integrity and inherit

From
"Oliver Elphick"
Date:
Bo Lorentsen wrote:
  >I hope someone will be so kind to take the time to explain to me what I
  >have been doing wrong in this example.
 ...
  >CREATE TABLE base (
  > id INTEGER PRIMARY KEY,
  > name TEXT
  >);
  >
  >CREATE TABLE derived (
  > value INTEGER
  >) INHERITS( base );
  >
  >CREATE TABLE test_ref (
  > ref_id INTEGER REFERENCES base,
  > name TEXT
  >);
...
  >INSERT INTO base (id, name) VALUES( 1, 'mother' );
  >INSERT INTO derived (id, name, value) VALUES( 2, 'child', 42 );
  >
  >INSERT INTO test_ref (ref_id, name) VALUES( 1, 'mother' );
  >
  >All this works, but this one dont :
  >
  >INSERT INTO test_ref (ref_id, name) VALUES( 2, 'child' );
  >
  >Here it says :
  >
  >ERROR:  <unnamed> referential integrity violation - key referenced from
  >test_ref not found in base

Referential integrity doesn't support references to inheritance hierarchies.
You will have to redesign your database.

For example, have a trigger on each table in the hierarchy to update a
table of keys and use RI on that; it would also help you to maintain
primary keys -- you probably think that table derived inherits the
primary key constraint from base, but it doesn't.  That is another problem
with the current implementation of inheritance. :-(

--
Oliver Elphick                                Oliver.Elphick@lfix.co.uk
Isle of Wight                              http://www.lfix.co.uk/oliver
PGP: 1024R/32B8FAA1: 97 EA 1D 47 72 3F 28 47  6B 7E 39 CC 56 E4 C1 47
GPG: 1024D/3E1D0C1C: CA12 09E0 E8D5 8870 5839  932A 614D 4C34 3E1D 0C1C
                 ========================================
     "For God hath not appointed us to wrath, but to obtain
      salvation by our Lord Jesus Christ, Who died for us,
      that, whether we wake or sleep, we should live
      together with him."
                         I Thessalonians 5:9,10



Re: integrity and inherit

From
Bo Lorentsen
Date:
Oliver Elphick wrote:

Thanks for the quick answer !

> Referential integrity doesn't support references to inheritance hierarchies.
> You will have to redesign your database.

That is sad news, is this scheduled to be fixed in the near future ?

> For example, have a trigger on each table in the hierarchy to update a
> table of keys and use RI on that; it would also help you to maintain
> primary keys -- you probably think that table derived inherits the
> primary key constraint from base, but it doesn't.  That is another problem
> with the current implementation of inheritance. :-(

I'm not quite sure what you meen. Does this mean that I sould make a rule on
insert on every enherited table, that makes sure the base is updated ?

When I do my "SELECT * FROM base" and see the value id the ID, what kind of
value am I then looking at ?

/BL


Re: integrity and inherit

From
"Oliver Elphick"
Date:
Bo Lorentsen wrote:
  >Oliver Elphick wrote:
  >> Referential integrity doesn't support references to inheritance hierarchie
      >s.
  >> You will have to redesign your database.
  >
  >That is sad news, is this scheduled to be fixed in the near future ?

It's in the TODO list...

  >> For example, have a trigger on each table in the hierarchy to update a
  >> table of keys and use RI on that; it would also help you to maintain
  >> primary keys -- you probably think that table derived inherits the
  >> primary key constraint from base, but it doesn't.  That is another problem
  >> with the current implementation of inheritance. :-(
  >
  >I'm not quite sure what you meen. Does this mean that I sould make a rule on
  >insert on every enherited table, that makes sure the base is updated ?

There's an example below.  resources is the ancestor and has four
descendants, all at the same level.  A resource key must occur only
once in the whole hierarchy and needs to be available for RI. resource
is a deferred class; that is, every resource must be a member of one of
the four descendant classes, so resource itself should have no rows.

  >When I do my "SELECT * FROM base" and see the value id the ID, what kind of
  >value am I then looking at ?

You are seeing all the records from the hierarchy of base and all its
descendants; that part of inheritance works; but RI does not work (yet).


Example (see above):

-- Index of resources

CREATE TABLE resource_list (
       id    VARCHAR(4)    PRIMARY KEY,
       flag  CHAR(1)       NOT NULL
                           CONSTRAINT "valid flag"
                              CHECK (flag in ('B', 'M', 'L', 'T')),
       name  TEXT          NOT NULL UNIQUE
)
;


CREATE TABLE resource
(
   id        VARCHAR(4),
   ...
,
PRIMARY KEY (id),
CONSTRAINT "resource in resource_list"
   FOREIGN KEY (id) REFERENCES resource_list (id)
      ON UPDATE CASCADE
      ON DELETE RESTRICT
      DEFERRABLE

);


CREATE FUNCTION update_resource_list()
  RETURNS opaque
  AS '
     -- update resource_list when a resource is added or deleted
     DECLARE
        flag     CHAR(1);
        cnt      INTEGER := 0;
        listrec  RECORD;
     BEGIN
        IF TG_OP = ''DELETE'' THEN
           DELETE FROM resource_list WHERE resource = OLD.id;
        ELSE
           IF TG_RELNAME = ''tool'' THEN
              flag := ''T'';
           ELSIF TG_RELNAME = ''bench'' THEN
              flag := ''B'';
           ELSIF TG_RELNAME = ''machine'' THEN
              flag := ''M'';
           ELSIF TG_RELNAME = ''labour'' THEN
              flag := ''L'';
           ELSE
              RAISE ERROR ''resource_def() function called on unexpected table
%'', TG_RELNAME;
           END IF;
           FOR listrec IN EXECUTE ''SELECT * FROM resource_list WHERE id = ''
|| quote_literal(OLD.id) LOOP
              cnt := cnt + 1
              IF flag != listrec.flag OR id != OLD.id THEN
                 EXECUTE ''UPDATE resource_list SET id = '' ||
quote_literal(NEW.id) || '', flag = '' || quote_literal(flag) || '' WHERE id =
'' || quote_literal (OLD.id);
              END IF;
           END LOOP;
           IF cnt = 0 THEN
              EXECUTE ''INSERT INTO resource_list VALUES ('' ||
                 quote_literal(NEW.id) || '', '' ||
                 quote_literal(flag);
           END IF;
        END IF;
     END;
  ' LANGUAGE 'plpgsql';

-- resource is a deferred class; it cannot have any rows itself
CREATE RULE no_resource AS ON INSERT
       TO resource
       DO INSTEAD NOTHING;


CREATE TABLE labour
(
   pay          NUMERIC(12,2)   NOT NULL
                                CHECK (pay >= 0),
   ...
,
PRIMARY KEY (id),
CONSTRAINT "resource in resource_list"
   FOREIGN KEY (id) REFERENCES resource_list (id)
      ON UPDATE CASCADE
      ON DELETE RESTRICT
      DEFERRABLE

)
inherits (resource)
;


CREATE TRIGGER on_labour_change BEFORE INSERT OR UPDATE
       ON labour FOR EACH ROW
       EXECUTE PROCEDURE update_resource_list();


CREATE TABLE bench
(
   descr     TEXT,
   ...
,
PRIMARY KEY (id),
CONSTRAINT "resource in resource_list"
   FOREIGN KEY (id) REFERENCES resource_list (id)
      ON UPDATE CASCADE
      ON DELETE RESTRICT
      DEFERRABLE

)
   INHERITS (resource)
;


CREATE TRIGGER on_bench_change BEFORE INSERT OR UPDATE
       ON bench FOR EACH ROW
       EXECUTE PROCEDURE update_resource_list();


CREATE TABLE machine
(
   descrip      TEXT            NOT NULL,
  ...
,
PRIMARY KEY (id),
CONSTRAINT "resource in resource_list"
   FOREIGN KEY (id) REFERENCES resource_list (id)
      ON UPDATE CASCADE
      ON DELETE RESTRICT
      DEFERRABLE

)
   INHERITS (resource)
;


CREATE TRIGGER on_machineT_change BEFORE INSERT OR UPDATE
       ON machine FOR EACH ROW
       EXECUTE PROCEDURE update_resource_list();


CREATE TABLE tool
(
   maker       TEXT           NOT NULL,
  ...
,
PRIMARY KEY (id),
CONSTRAINT "resource in resource_list"
   FOREIGN KEY (id) REFERENCES resource_list (id)
      ON UPDATE CASCADE
      ON DELETE RESTRICT
      DEFERRABLE

)
INHERITS (resource)
;


CREATE TRIGGER on_tool_change BEFORE INSERT OR UPDATE
       ON tool FOR EACH ROW
       EXECUTE PROCEDURE update_resource_list();




--
Oliver Elphick                                Oliver.Elphick@lfix.co.uk
Isle of Wight                              http://www.lfix.co.uk/oliver
PGP: 1024R/32B8FAA1: 97 EA 1D 47 72 3F 28 47  6B 7E 39 CC 56 E4 C1 47
GPG: 1024D/3E1D0C1C: CA12 09E0 E8D5 8870 5839  932A 614D 4C34 3E1D 0C1C
                 ========================================
     "For God hath not appointed us to wrath, but to obtain
      salvation by our Lord Jesus Christ, Who died for us,
      that, whether we wake or sleep, we should live
      together with him."
                         I Thessalonians 5:9,10



Re: integrity and inherit

From
Bo Lorentsen
Date:
Oliver Elphick wrote:

> It's in the TODO list...

Are there any plans for whenever this will be fixed ? As fare as I can se, this is
an important feature if you need to use PostgreSQL as a real OO DB.

> There's an example below.  resources is the ancestor and has four
> descendants, all at the same level.  A resource key must occur only
> once in the whole hierarchy and needs to be available for RI. resource
> is a deferred class; that is, every resource must be a member of one of
> the four descendant classes, so resource itself should have no rows.

Sorry about the dumb question, but with this kind of knowledge, why haven't it
been fixed in the source code ?

The example you gave is somewhat complicated as I don't have much knowledge of the
internals in PosgreSQL. I hope someone will fix this soon, as this "tricker
solution" demands exact knowledge of the layout of postgresql.

If I need more of this hard core knowledge, where do I find this ? --- in the code
?

Thanks for taking you time to write this, I may be forced to use it later :-), but
as for now I will hope that I will be able to live with my non RI version, as I
will be using some kind for application server to access these.

/BL