Thread: Immutable attributes?

Immutable attributes?

From
Stephane Bortzmeyer
Date:
To protect the database from programming errors (there is a team
working on the project and some beginners may produce bugs), I would
like to flag some attributes as immutable, meaning non modifiable in
an UPDATE. (Typical examples are ID or creation time.)

Currently, I use triggers:

CREATE OR REPLACE FUNCTION check_immutable() RETURNS TRIGGER
    AS 'BEGIN
              IF NEW.id != OLD.id OR NEW.created != OLD.created THEN
                   RAISE EXCEPTION ''Change not allowed in that table'';
              END IF;
          RETURN NEW;
        END;'
    LANGUAGE PLPGSQL;

CREATE TRIGGER check_immutable
   BEFORE UPDATE ON MyTable
   FOR EACH ROW
   EXECUTE PROCEDURE check_immutable();

It is quite painful, since I need a function (with the list of
immutable attributes) and a trigger per table. If I INHERITS from a
table, triggers on the parent table are not called if I update the
child table.

Is there a better way? I do not find a constraint IMMUTABLE for
attributes.

PostgreSQL 7.4, switching to 8.0 would be difficult.


Re: Immutable attributes?

From
David Fetter
Date:
On Sun, Apr 24, 2005 at 09:26:49PM +0200, Stephane Bortzmeyer wrote:
> To protect the database from programming errors (there is a team
> working on the project and some beginners may produce bugs), I would
> like to flag some attributes as immutable, meaning non modifiable in
> an UPDATE. (Typical examples are ID or creation time.)
>
> Currently, I use triggers:

You could use RULEs instead, but the effect would be similar.

> PostgreSQL 7.4, switching to 8.0 would be difficult.

Now is easier than later.

Cheers,
D
--
David Fetter david@fetter.org http://fetter.org/
phone: +1 510 893 6100   mobile: +1 415 235 3778

Remember to vote!

Re: Immutable attributes?

From
Michael Fuhr
Date:
On Sun, Apr 24, 2005 at 09:26:49PM +0200, Stephane Bortzmeyer wrote:
>
> To protect the database from programming errors (there is a team
> working on the project and some beginners may produce bugs), I would
> like to flag some attributes as immutable, meaning non modifiable in
> an UPDATE. (Typical examples are ID or creation time.)
>
> Currently, I use triggers:
...
> It is quite painful, since I need a function (with the list of
> immutable attributes) and a trigger per table. If I INHERITS from a
> table, triggers on the parent table are not called if I update the
> child table.

This wouldn't solve all your problems, but you could write a generic
trigger function in a language like PL/Tcl or PL/Python (or PL/Perl
in 8.0 and later) and pass the column name(s) as arguments.  Here's
a minimally-tested example -- if it doesn't work exactly the way
you want then at least it should serve as inspiration:

CREATE FUNCTION check_immutable() RETURNS trigger AS '
for col in TD["args"]:
    if TD["new"][col] != TD["old"][col]:
        plpy.error(''attribute "%s" is immutable'' % col)
return "OK"
' LANGUAGE plpythonu;

CREATE TABLE foo (
    id       serial PRIMARY KEY,
    created  date NOT NULL DEFAULT current_date,
    name     text NOT NULL
);

CREATE TRIGGER check_immutable BEFORE UPDATE ON foo
    FOR EACH ROW EXECUTE PROCEDURE check_immutable('id', 'created');

INSERT INTO foo (name) VALUES ('name 1');
SELECT * FROM foo;
 id |  created   |  name
----+------------+--------
  1 | 2005-04-24 | name 1
(1 row)

UPDATE foo SET created = '2005-05-01';
ERROR:  plpython: function "check_immutable" failed
DETAIL:  plpy.Error: ('attribute "created" is immutable',)

UPDATE foo SET id = 2;
ERROR:  plpython: function "check_immutable" failed
DETAIL:  plpy.Error: ('attribute "id" is immutable',)

UPDATE foo SET name = 'name 2', created = current_date;
SELECT * FROM foo;
 id |  created   |  name
----+------------+--------
  1 | 2005-04-24 | name 2
(1 row)

I was going to suggest using contrib/noupdate, but it doesn't appear
to work, at least not the way I was expecting:

CREATE TABLE foo (
    id    serial PRIMARY KEY,
    name  text NOT NULL
);

CREATE TRIGGER check_immutable BEFORE UPDATE ON foo
  FOR EACH ROW EXECUTE PROCEDURE noup('id');

INSERT INTO foo (name) VALUES ('name 1');

UPDATE foo SET id = 2;
WARNING:  id: update not allowed
UPDATE 0

UPDATE foo SET name = 'name 2';
WARNING:  id: update not allowed
UPDATE 0

I expected the second update to succeed since we're not changing
the value of id.  I'm wondering if that's a bug in the module.

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

Re: Immutable attributes?

From
Stephane Bortzmeyer
Date:
On Sun, Apr 24, 2005 at 02:53:51PM -0700,
 David Fetter <david@fetter.org> wrote
 a message of 21 lines which said:

> > PostgreSQL 7.4, switching to 8.0 would be difficult.
>
> Now is easier than later.

Do you mean that PostgreSQL 8 has immutable attributes ? I do not find
that.


Re: Immutable attributes?

From
Stephane Bortzmeyer
Date:
On Sun, Apr 24, 2005 at 10:05:53PM -0600,
 Michael Fuhr <mike@fuhr.org> wrote
 a message of 85 lines which said:

> CREATE FUNCTION check_immutable() RETURNS trigger AS '
> for col in TD["args"]:

Ah, yes, much better than mine. Thanks.


Re: Immutable attributes?

From
Michael Fuhr
Date:
On Mon, Apr 25, 2005 at 09:18:16AM +0200, Stephane Bortzmeyer wrote:
> On Sun, Apr 24, 2005 at 02:53:51PM -0700, David Fetter <david@fetter.org> wrote
> > >
> > > PostgreSQL 7.4, switching to 8.0 would be difficult.
> >
> > Now is easier than later.
>
> Do you mean that PostgreSQL 8 has immutable attributes ? I do not find
> that.

I think that was a comment about upgrading in general, not an
implication that 8.0 supports a particular feature.

--
Michael Fuhr
http://www.fuhr.org/~mfuhr/

Re: Immutable attributes?

From
Stephane Bortzmeyer
Date:
On Sun, Apr 24, 2005 at 10:05:53PM -0600,
 Michael Fuhr <mike@fuhr.org> wrote
 a message of 85 lines which said:

> This wouldn't solve all your problems, but you could write a generic
> trigger function in a language like PL/Tcl or PL/Python (or PL/Perl
> in 8.0 and later) and pass the column name(s) as arguments.  Here's
> a minimally-tested example

Tested and it works fine. Thanks.

Re: Immutable attributes?

From
Mario Soto Cordones
Date:
Hi for all ,  plese a question ,this function can be write in pl/pgsql ???????

thank for all

2005/4/24, Stephane Bortzmeyer <bortzmeyer@nic.fr>:
> To protect the database from programming errors (there is a team
> working on the project and some beginners may produce bugs), I would
> like to flag some attributes as immutable, meaning non modifiable in
> an UPDATE. (Typical examples are ID or creation time.)
>
> Currently, I use triggers:
>
> CREATE OR REPLACE FUNCTION check_immutable() RETURNS TRIGGER
>     AS 'BEGIN
>               IF NEW.id != OLD.id OR NEW.created != OLD.created THEN
>                    RAISE EXCEPTION ''Change not allowed in that table'';
>               END IF;
>               RETURN NEW;
>         END;'
>     LANGUAGE PLPGSQL;
>
> CREATE TRIGGER check_immutable
>    BEFORE UPDATE ON MyTable
>    FOR EACH ROW
>    EXECUTE PROCEDURE check_immutable();
>
> It is quite painful, since I need a function (with the list of
> immutable attributes) and a trigger per table. If I INHERITS from a
> table, triggers on the parent table are not called if I update the
> child table.
>
> Is there a better way? I do not find a constraint IMMUTABLE for
> attributes.
>
> PostgreSQL 7.4, switching to 8.0 would be difficult.
>
> ---------------------------(end of broadcast)---------------------------
> TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org
>


--
cordialmente,

Ing. Mario Soto Cordones

Re: Immutable attributes?

From
Stephane Bortzmeyer
Date:
On Tue, Apr 26, 2005 at 09:49:05AM -0400,
 Mario Soto Cordones <msotocl@gmail.com> wrote
 a message of 45 lines which said:

> Hi for all ,  plese a question ,this function can be write in pl/pgsql ???????

I do not understand, it IS pl/pgsql.

> > CREATE OR REPLACE FUNCTION check_immutable() RETURNS TRIGGER
> >     AS 'BEGIN
> >               IF NEW.id != OLD.id OR NEW.created != OLD.created THEN
> >                    RAISE EXCEPTION ''Change not allowed in that table'';
> >               END IF;
> >               RETURN NEW;
> >         END;'
> >     LANGUAGE PLPGSQL;

Re: Immutable attributes?

From
Pavel Stehule
Date:
On Tue, 26 Apr 2005, Mario Soto Cordones wrote:

> Hi for all ,  plese a question ,this function can be write in pl/pgsql ???????

No, there is no possibility write it in pl/pgsql.

Pavel Stehule

>


Re: Immutable attributes? Correction

From
Pavel Stehule
Date:
On Tue, 26 Apr 2005, Mario Soto Cordones wrote:

> Hi for all ,  plese a question ,this function can be write in pl/pgsql ???????
>

I am sorry. Universal handler for immutable attributes is not possible in
plpgsql. Only if you know names of immutable columns, than you can use
plpgsql. You have to use plperl, pltcl else.

Regards
Pavel Stehule