Thread: Detecting changes to certain fields in 'before update' trigger functions

Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Hi list,

First steps in trigger functions and PL/pgSQL so please bear with me...

How can one detect changes to certain fields in before update trigger
functions?

 IF (NEW.<column-name> != OLD.<column-name>) THEN ...

doesn't work, so obviously my understanding of the values of the
varriables NEW and OLD in before update trigger functions is wrong; I
had thought that OLD holds the record as it was before the update, and
that NEW holds the record as it is since the update (but before the
update has been committed)?

How should one go about detecting changes to certain fields in before
update trigger functions?

Any help/advice much appreciated.

Sebastian

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Adrian Klaver
Date:
On Monday 01 December 2008 7:18:51 am Sebastian Tennant wrote:
> Hi list,
>
> First steps in trigger functions and PL/pgSQL so please bear with me...
>
> How can one detect changes to certain fields in before update trigger
> functions?
>
>  IF (NEW.<column-name> != OLD.<column-name>) THEN ...
>
> doesn't work, so obviously my understanding of the values of the
> varriables NEW and OLD in before update trigger functions is wrong; I
> had thought that OLD holds the record as it was before the update, and
> that NEW holds the record as it is since the update (but before the
> update has been committed)?
>
> How should one go about detecting changes to certain fields in before
> update trigger functions?
>
> Any help/advice much appreciated.
>
> Sebastian

It works here. Can you be more specific? Full function code, table schema,etc.


Thanks,
--
Adrian Klaver
aklaver@comcast.net

Re: Detecting changes to certain fields in 'before update' trigger functions

From
"Richard Broersma"
Date:
On Mon, Dec 1, 2008 at 7:18 AM, Sebastian Tennant
<sebyte@smolny.plus.com> wrote:

>  IF (NEW.<column-name> != OLD.<column-name>) THEN ...

 The != operator doesn't work the way you might think when nulls are
thrown into the mix.  I asked a similar question a while back and was
kindly pointed to the following syntax:

IF( NEW.* IS DISTINCT FROM OLD.* ) THEN ...


--
Regards,
Richard Broersma Jr.

Visit the Los Angeles PostgreSQL Users Group (LAPUG)
http://pugs.postgresql.org/lapug

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth Adrian Klaver <aklaver@comcast.net>:
> On Monday 01 December 2008 7:18:51 am Sebastian Tennant wrote:
>> I had thought that OLD holds the record as it was before the update,
>> and that NEW holds the record as it is since the update (but before
>> the update has been committed)?

'42.10 Trigger Procedures' seems to confirm this:

"`NEW'
      Data type `RECORD'; variable holding the new database row for
      `INSERT'/`UPDATE' operations in row-level triggers. This variable
      is `NULL' in statement-level triggers.

 `OLD'
      Data type `RECORD'; variable holding the old database row for
      `UPDATE'/`DELETE' operations in row-level triggers. This variable
      is `NULL' in statement-level triggers."

> It works here. Can you be more specific? Full function code, table schema,etc.

Of course.

######## timestamper.sql starts here ########
 -- \i ./timestamper.sql

 DROP TABLE IF EXISTS tt;
 CREATE TEMP TABLE tt (username character varying(12),
                       delisted boolean,
                       created_at timestamp(0) without time zone,
                       updated_at timestamp(0) without time zone,
                       delisted_at timestamp(0) without time zone);

 CREATE OR REPLACE FUNCTION timestamper() RETURNS TRIGGER AS $$
   BEGIN
   IF (TG_OP = 'INSERT') THEN NEW.created_at := current_timestamp(0); END IF;
   IF (TG_OP = 'UPDATE') THEN
     NEW.updated_at := current_timestamp(0);
     IF ((NEW.delisted = true) AND (NEW.delisted != OLD.delisted)) THEN
       NEW.delisted_at := current_timestamp(0); END IF;
     END IF;
   RETURN NEW;
   END;
 $$ LANGUAGE plpgsql;

 CREATE TRIGGER timestamper_before_insert BEFORE INSERT ON tt FOR EACH ROW
   EXECUTE PROCEDURE timestamper();

 CREATE TRIGGER timestamper_before_update BEFORE UPDATE ON tt FOR EACH ROW
   EXECUTE PROCEDURE timestamper();

 -- DROP FUNCTION timestamper() CASCADE;
 -- no need to drop temporary tables

######## timesatmper.sql ends here ########

 testdb=> \i ./timestamper.sql
 DROP TABLE
 CREATE TABLE
 CREATE FUNCTION
 CREATE TRIGGER
 CREATE TRIGGER
 testdb=> insert into tt values (foo');
 INSERT 0 1
 testdb=> select * from tt;
 -[ RECORD 1 ]--------------------
 username    | foo
 delisted    |
 created_at  | 2008-12-01 16:17:37
 updated_at  |
 delisted_at |

 testdb=> update tt set username=bar';
 UPDATE 1
 testdb=> select * from tt;
 -[ RECORD 1 ]--------------------
 username    | bar
 delisted    |
 created_at  | 2008-12-01 16:17:37
 updated_at  | 2008-12-01 16:18:27
 delisted_at |

 testdb=> update tt set delisted=true where username='bar';
 UPDATE 1
 testdb=> select * from tt;
 -[ RECORD 1 ]--------------------
 username    | bar
 delisted    | t
 created_at  | 2008-12-01 16:17:37
 updated_at  | 2008-12-01 16:19:01
 delisted_at |


The triggers for the initial insert and the first update do what I want
them to, but the second update (that marks 'foo' as delisted) fails to
update the delisted_at timestamp.

Sebastian

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth "Richard Broersma" <richard.broersma@gmail.com>:
> On Mon, Dec 1, 2008 at 7:18 AM, Sebastian Tennant
> <sebyte@smolny.plus.com> wrote:
>
>>  IF (NEW.<column-name> != OLD.<column-name>) THEN ...
>
>  The != operator doesn't work the way you might think when nulls are
> thrown into the mix.  I asked a similar question a while back and was
> kindly pointed to the following syntax:
>
> IF( NEW.* IS DISTINCT FROM OLD.* ) THEN ...

That's it!  Thanks very much Richard.

I sometimes think this kind of gotcha is purposely buried, or not
addressed at all, in order to force users to read the manual.  I wasn't
planning on spending four hours doing just that, but now I suppose I'm
almost glad I did.

Sebastian

Re: Detecting changes to certain fields in 'before update' trigger functions

From
"Richard Broersma"
Date:
On Mon, Dec 1, 2008 at 8:35 AM, Sebastian Tennant
<sebyte@smolny.plus.com> wrote:
>>>  IF (NEW.<column-name> != OLD.<column-name>) THEN ...
>>
>> IF( NEW.* IS DISTINCT FROM OLD.* ) THEN ...
>
> I sometimes think this kind of gotcha is purposely buried, or not
> addressed at all, in order to force users to read the manual.

I wouldn't say it is intentionally buried.  I would say that the
PostgreSQL manual focuses primarily is on "What are the PG features".
 While the manual may at times document some of the good/best
practices to use by combining various PG features,  I wouldn't say
that its intention isn't to be an authoritative source on "How to use
PG features."

On the other hand, there are many ANSI-SQL books that focus on good
practices.   For example, the need for the "IS DISTINCT FROM" when
dealing with nulls would be discussed in an SQL book.  Once you have
the theory down, you can turn to the PostgreSQL manual to find out how
PostgreSQL implements this functionality.


--
Regards,
Richard Broersma Jr.

Visit the Los Angeles PostgreSQL Users Group (LAPUG)
http://pugs.postgresql.org/lapug

Sebastian Tennant <sebyte@smolny.plus.com> writes:
> I sometimes think this kind of gotcha is purposely buried, or not
> addressed at all, in order to force users to read the manual.

Where exactly do you think we should document it, if not in the manual?
In any case it's SQL-standard behavior that any book about SQL will
tell you.

            regards, tom lane

pg_dump restore as transaction?

From
Owen Hartnett
Date:
If my perusal of the sql generated by pg_dump is correct, then it
doesn't appear that it's wrapped in a transaction, and thus might be
able to only complete a partial restore?

Or does

psql myDatabase <mypg_dumpfile

wrap the file stream in a transaction?

If not, is there a reason why it can't be done so (some process that
cannot be run as a transaction inside the file)?

Or should I just add begin and commit statements at the beginning and
end of file?

I want to provide a mechanized daily update of one schema into a
differently named database, and I'd like it to rollback if if fails.

-Owen

Re: pg_dump restore as transaction?

From
Tom Lane
Date:
Owen Hartnett <owen@clipboardinc.com> writes:
> If my perusal of the sql generated by pg_dump is correct, then it
> doesn't appear that it's wrapped in a transaction, and thus might be
> able to only complete a partial restore?

That's correct, and intentional.  You can use pg_restore's -1 switch
or add begin/end manually if you don't want it to work that way.

            regards, tom lane

Re: pg_dump restore as transaction?

From
Alvaro Herrera
Date:
Owen Hartnett wrote:

> If my perusal of the sql generated by pg_dump is correct, then it
> doesn't appear that it's wrapped in a transaction, and thus might be
> able to only complete a partial restore?

You're right, it is not.  Try pg_restore --single-transaction.  (You'll
need pg_dump -Fc though.)


--
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

Re: pg_dump restore as transaction?

From
Peter Billen
Date:
Quoting Owen Hartnett <owen@clipboardinc.com>:

>
> If my perusal of the sql generated by pg_dump is correct, then it
> doesn't appear that it's wrapped in a transaction, and thus might be
> able to only complete a partial restore?
>
> Or does
>
> psql myDatabase <mypg_dumpfile
>

Try to use pg_restore with the following option:

  -1, --single-transaction
                           restore as a single transaction

Or psql with the following option:

  -1 ("one")      execute command file as a single transaction

Kind regards,

Peter



Re: pg_dump restore as transaction?

From
Owen Hartnett
Date:
At 12:37 PM -0500 12/1/08, Tom Lane wrote:
>Owen Hartnett <owen@clipboardinc.com> writes:
>>  If my perusal of the sql generated by pg_dump is correct, then it
>>  doesn't appear that it's wrapped in a transaction, and thus might be
>>  able to only complete a partial restore?
>
>That's correct, and intentional.  You can use pg_restore's -1 switch
>or add begin/end manually if you don't want it to work that way.
>
>            regards, tom lane

Thanks to everybody for their help.  You guys are great.

-Owen

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth "Richard Broersma" <richard.broersma@gmail.com>:
> On Mon, Dec 1, 2008 at 8:35 AM, Sebastian Tennant
> <sebyte@smolny.plus.com> wrote:
>>>>  IF (NEW.<column-name> != OLD.<column-name>) THEN ...
>>>
>>> IF( NEW.* IS DISTINCT FROM OLD.* ) THEN ...
>>>
>> I sometimes think this kind of gotcha is purposely buried, or not
>> addressed at all, in order to force users to read the manual.
>
> I wouldn't say it is intentionally buried.  I would say that the
> PostgreSQL manual focuses primarily is on "What are the PG features".
> While the manual may at times document some of the good/best practices
> to use by combining various PG features, I wouldn't say that its
> intention isn't to be an authoritative source on "How to use PG
> features."
>
> On the other hand, there are many ANSI-SQL books that focus on good
> practices.   For example, the need for the "IS DISTINCT FROM" when
> dealing with nulls would be discussed in an SQL book.  Once you have
> the theory down, you can turn to the PostgreSQL manual to find out how
> PostgreSQL implements this functionality.

That's sound advice and I take your point about the manual focussing on
Postgre features rather than SQL per se.  I have read one or two SQL
books but I'm very much a learn by doing person... and the fact is, I
haven't done much doing, until now.

May I wriggle out a little by saying that I didn't really mean what I
said, or rather, I failed to say what I really meant; that it sometimes
feels as if a gotcha has been buried in order to make you read the
manual.

Sebastian

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth Tom Lane <tgl@sss.pgh.pa.us>:
> Sebastian Tennant <sebyte@smolny.plus.com> writes:
>> I sometimes think this kind of gotcha is purposely buried, or not
>> addressed at all, in order to force users to read the manual.
>
> Where exactly do you think we should document it, if not in the
> manual?

I clearly didn't express myself very well.  Let me set the record
straight by saying that my experience with PostgreSQL over the past
three months or so has been fantastic, thanks in no small part to the
clear and comprehensive accompanying manual.

All I meant was that it sometimes _feels_ as if a vital piece of
information has been buried in the manual in order to make you read it.

(I wasn't making a serious point and I didn't expect it to be taken
literally).

Sebastian

P.S.  Emacs users of PostgreSQL might like to know that there's a
      texinfo version of the manual (version 8.3.3) available for
      download from here:

        http://www.emacswiki.org/PostGreSQL


Re: Detecting changes to certain fields in 'before update' trigger functions

From
Alvaro Herrera
Date:
Sebastian Tennant wrote:

> P.S.  Emacs users of PostgreSQL might like to know that there's a
>       texinfo version of the manual (version 8.3.3) available for
>       download from here:
>
>         http://www.emacswiki.org/PostGreSQL

Hmm, we did have a patch to add a texinfo target to the docs Makefile ...
apparently it was never applied.  Maybe that's a good idea?

--
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth Alvaro Herrera <alvherre@commandprompt.com>:
> Sebastian Tennant wrote:
>
>> P.S.  Emacs users of PostgreSQL might like to know that there's a
>>       texinfo version of the manual (version 8.3.3) available for
>>       download from here:
>>
>>         http://www.emacswiki.org/PostGreSQL
>
> Hmm, we did have a patch to add a texinfo target to the docs Makefile ...
> apparently it was never applied.  Maybe that's a good idea?

It's a very good idea IMHO.

I would love to see a texinfo target in the docs Makefile.  Nothing
beats Info for convenience.

Sebastian

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Alvaro Herrera
Date:
Sebastian Tennant wrote:
> Quoth Alvaro Herrera <alvherre@commandprompt.com>:
> > Sebastian Tennant wrote:
> >
> >> P.S.  Emacs users of PostgreSQL might like to know that there's a
> >>       texinfo version of the manual (version 8.3.3) available for
> >>       download from here:
> >>
> >>         http://www.emacswiki.org/PostGreSQL
> >
> > Hmm, we did have a patch to add a texinfo target to the docs Makefile ...
> > apparently it was never applied.  Maybe that's a good idea?
>
> It's a very good idea IMHO.

Hmm, actually now that I look closer, it is there (make postgres.info
does the trick).  The build process throws a worrying number of warnings
though.

--
Alvaro Herrera                                http://www.CommandPrompt.com/
The PostgreSQL Company - Command Prompt, Inc.

Re: Detecting changes to certain fields in 'before update' trigger functions

From
Sebastian Tennant
Date:
Quoth Alvaro Herrera <alvherre@commandprompt.com>:
> Sebastian Tennant wrote:
>> Quoth Alvaro Herrera <alvherre@commandprompt.com>:
>> > Sebastian Tennant wrote:
>> >
>> >> P.S.  Emacs users of PostgreSQL might like to know that there's a
>> >>       texinfo version of the manual (version 8.3.3) available for
>> >>       download from here:
>> >>
>> >>         http://www.emacswiki.org/PostGreSQL
>> >
>> > Hmm, we did have a patch to add a texinfo target to the docs Makefile ...
>> > apparently it was never applied.  Maybe that's a good idea?
>>
>> It's a very good idea IMHO.
>
> Hmm, actually now that I look closer, it is there (make postgres.info
> does the trick).  The build process throws a worrying number of warnings
> though.

Warnings are better than errors :-)

I'll download the source and have a go myself.

Many thanks Alvaro.

Sebastian