comparing NEW and OLD (any good this way?)

Started by Willy-Bas Loosalmost 17 years ago33 messagesgeneral
Jump to latest
#1Willy-Bas Loos
willybas@gmail.com

Hi,

My colleage Geard Troost and I found a handy way of comparing OLD and
NEW in a trigger function.
Normally this does not work (if anyone can tell me why, that'd be
great), but once you cast them to text, it does.

Is there anything to say against this, or can i go ahead and recommend
this to everyone who wants to check if anything changed before doing
what their update triggers do?

Cheers,

WBL

Here's the code:

drop table test;
create table test (id integer primary key, value integer);
insert into test values (1,1);
insert into test values (2,1);
insert into test values (3,1);
insert into test values (4,1);
insert into test values (5,1);
insert into test values (6,1);

create or replace function bla() returns trigger as $$
begin
IF (NEW::TEXT = OLD::TEXT) THEN raise notice 'changed';
END IF;
return NEW;
end
$$
language plpgsql;

CREATE TRIGGER test_bla BEFORE UPDATE
ON test FOR EACH ROW
EXECUTE PROCEDURE public.bla();

update test set value =NULL where id= 1;
update test set value =NULL where id= 1;

--
"Patriotism is the conviction that your country is superior to all
others because you were born in it." -- George Bernard Shaw

#2Pavel Stehule
pavel.stehule@gmail.com
In reply to: Willy-Bas Loos (#1)
Re: comparing NEW and OLD (any good this way?)

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

regards
Pavel Stehule

2009/7/23 Willy-Bas Loos <willybas@gmail.com>:

Show quoted text

Hi,

My colleage Geard Troost and I found a handy way of comparing OLD and
NEW in a trigger function.
Normally this does not work (if anyone can tell me why, that'd be
great), but once you cast them to text, it does.

Is there anything to say against this, or can i go ahead and recommend
this to everyone who wants to check if anything changed before doing
what their update triggers do?

Cheers,

WBL

Here's the code:

drop table test;
create table test (id integer primary key, value integer);
insert into test values (1,1);
insert into test values (2,1);
insert into test values (3,1);
insert into test values (4,1);
insert into test values (5,1);
insert into test values (6,1);

create or replace function bla() returns trigger as $$
begin
IF (NEW::TEXT = OLD::TEXT) THEN raise notice 'changed';
END IF;
return NEW;
end
$$
language plpgsql;

CREATE TRIGGER test_bla BEFORE UPDATE
  ON test FOR EACH ROW
  EXECUTE PROCEDURE public.bla();

update test set value =NULL where id= 1;
update test set value =NULL where id= 1;

--
"Patriotism is the conviction that your country is superior to all
others because you were born in it." -- George Bernard Shaw

--
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general

#3Thomas Kellerer
spam_eater@gmx.net
In reply to: Pavel Stehule (#2)
Re: comparing NEW and OLD (any good this way?)

Pavel Stehule, 23.07.2009 13:45:

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

regards
Pavel Stehule

That collection of tips is really nice.

Why isn't there a link from the Postgres Wiki to your page?

Regards
Thomas

#4Andreas Wenk
a.wenk@netzmeister-st-pauli.de
In reply to: Pavel Stehule (#2)
Re: comparing NEW and OLD (any good this way?)

Pavel Stehule schrieb:

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

regards
Pavel Stehule

Pavel, this trick-list is awesome ;-) Thanks for the tip!

Cheers

Andy

P.S.: a link to that would be nice ;-)

#5Pavel Stehule
pavel.stehule@gmail.com
In reply to: Andreas Wenk (#4)
Re: comparing NEW and OLD (any good this way?)

2009/7/23 Andreas Wenk <a.wenk@netzmeister-st-pauli.de>:

Pavel Stehule schrieb:

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

regards
Pavel Stehule

Pavel, this trick-list is awesome ;-) Thanks for the tip!

Cheers

Andy

P.S.: a link to that would be nice ;-)

look on http://wiki.postgresql.org/wiki/Category:Snippets

There are link on tricks page.

Pavel

#6Thomas Kellerer
spam_eater@gmx.net
In reply to: Pavel Stehule (#5)
Re: comparing NEW and OLD (any good this way?)

Pavel Stehule, 23.07.2009 14:50:

look on http://wiki.postgresql.org/wiki/Category:Snippets

That page is not accessible from the Wiki's main page (at least I can't find an easy way to navigate there)

I think there should be a prominent link right at the start page that links to that page and your excellent collection.

Regards
Thomas

#7Merlin Moncure
mmoncure@gmail.com
In reply to: Pavel Stehule (#2)
Re: comparing NEW and OLD (any good this way?)

On Thu, Jul 23, 2009 at 7:45 AM, Pavel Stehule<pavel.stehule@gmail.com> wrote:

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

note: in PostgreSQL 8.4, you can compare record variables directly
with standard boolean operators.

merlin

#8Sam Mason
sam@samason.me.uk
In reply to: Pavel Stehule (#2)
Re: comparing NEW and OLD (any good this way?)

On Thu, Jul 23, 2009 at 01:45:36PM +0200, Pavel Stehule wrote:

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks

Just had a quick flick through your list and one of the early ones stuck
out:

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Attention_on_IS_NULL_and_IS_NOT_NULL_operators_for_composite_types

is scary; even worse is that it was changed to be like this in 8.2
because the standard says it should behave this way. What on earth were
they thinking when they defined the standard this way?

--
Sam http://samason.me.uk/

#9Pavel Stehule
pavel.stehule@gmail.com
In reply to: Merlin Moncure (#7)
Re: comparing NEW and OLD (any good this way?)

2009/7/23 Merlin Moncure <mmoncure@gmail.com>:

On Thu, Jul 23, 2009 at 7:45 AM, Pavel Stehule<pavel.stehule@gmail.com> wrote:

Hello

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Fast_compare_variables_NEW_and_OLD_in_trigger.27s_body

note: in PostgreSQL 8.4, you can compare record variables directly
with standard boolean operators.

actualised

Thank You
Pavel

Show quoted text

merlin

#10Joshua Tolley
eggyknap@gmail.com
In reply to: Willy-Bas Loos (#1)
Re: comparing NEW and OLD (any good this way?)

On Thu, Jul 23, 2009 at 01:40:45PM +0200, Willy-Bas Loos wrote:

Is there anything to say against this, or can i go ahead and recommend
this to everyone who wants to check if anything changed before doing
what their update triggers do?

Perhaps 8.4's suppress_redundant_updates_trigger() could be helpful in this
case:

http://www.postgresql.org/docs/8.4/interactive/functions-trigger.html

--
Joshua Tolley / eggyknap
End Point Corporation
http://www.endpoint.com

#11Alvaro Herrera
alvherre@2ndquadrant.com
In reply to: Thomas Kellerer (#6)
Re: comparing NEW and OLD (any good this way?)

Thomas Kellerer wrote:

Pavel Stehule, 23.07.2009 14:50:

look on http://wiki.postgresql.org/wiki/Category:Snippets

That page is not accessible from the Wiki's main page (at least I
can't find an easy way to navigate there)

I think there should be a prominent link right at the start page that
links to that page and your excellent collection.

Agreed, just added one.

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

#12Jasen Betts
jasen@xnet.co.nz
In reply to: Willy-Bas Loos (#1)
Re: comparing NEW and OLD (any good this way?)

On 2009-07-23, Sam Mason <sam@samason.me.uk> wrote:

On Thu, Jul 23, 2009 at 01:45:36PM +0200, Pavel Stehule wrote:

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks

Just had a quick flick through your list and one of the early ones stuck
out:

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Attention_on_IS_NULL_and_IS_NOT_NULL_operators_for_composite_types

is scary; even worse is that it was changed to be like this in 8.2
because the standard says it should behave this way. What on earth were
they thinking when they defined the standard this way?

since any comparson involving those tuples will return NULL true is the
correct value for IS NULL

if you are bothered by this behavior you are misusing NULL.

#13Sam Mason
sam@samason.me.uk
In reply to: Jasen Betts (#12)
Re: comparing NEW and OLD (any good this way?)

On Wed, Jul 29, 2009 at 01:15:27PM +0000, Jasen Betts wrote:

On 2009-07-23, Sam Mason <sam@samason.me.uk> wrote:

http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Attention_on_IS_NULL_and_IS_NOT_NULL_operators_for_composite_types

is scary; even worse is that it was changed to be like this in 8.2
because the standard says it should behave this way. What on earth were
they thinking when they defined the standard this way?

since any comparson involving those tuples will return NULL true is the
correct value for IS NULL

I think you missed the point:

SELECT r IS NULL, r IS NOT NULL
FROM (VALUES (1,NULL)) r(a,b);

returns FALSE for *both* columns. How can a row be both NULL *and*
non-NULL?

if you are bothered by this behavior you are misusing NULL.

I understand that this is the specified behavior, and hence PG is
correctly following the spec--but it still bothers me.

--
Sam http://samason.me.uk/

#14Merlin Moncure
mmoncure@gmail.com
In reply to: Sam Mason (#13)
Re: comparing NEW and OLD (any good this way?)

On Wed, Jul 29, 2009 at 9:40 AM, Sam Mason<sam@samason.me.uk> wrote:

On Wed, Jul 29, 2009 at 01:15:27PM +0000, Jasen Betts wrote:

On 2009-07-23, Sam Mason <sam@samason.me.uk> wrote:

  http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Attention_on_IS_NULL_and_IS_NOT_NULL_operators_for_composite_types

is scary; even worse is that it was changed to be like this in 8.2
because the standard says it should behave this way.  What on earth were
they thinking when they defined the standard this way?

since any comparson involving those tuples will return NULL true is the
correct value for IS NULL

I think you missed the point:

 SELECT r IS NULL, r IS NOT NULL
 FROM (VALUES (1,NULL)) r(a,b);

returns FALSE for *both* columns.  How can a row be both NULL *and*
non-NULL?

if you are bothered by this behavior you are misusing NULL.

I understand that this is the specified behavior, and hence PG is
correctly following the spec--but it still bothers me.

not only that, but while pg's treats composite types with null members
as null according to the 'is null' operator (in accordance with the
spec), but as not null everywhere else. thus, for example, a 'null'
composite type is counted in the count() aggregate function. how
funky is that?

merlin

#15Willy-Bas Loos
willybas@gmail.com
In reply to: Sam Mason (#13)
Re: comparing NEW and OLD (any good this way?)

SELECT r IS NULL, r IS NOT NULL
FROM (VALUES (1,NULL)) r(a,b);

returns FALSE for *both* columns. How can a row be both NULL *and*
non-NULL?

Actually, the value is neither NULL, nor non-NULL.
Part of it is NULL and part of it isn't so neither "IS NULL" is true,
nor is "IS NOT NULL"

cheers,
WBL

On Wed, Jul 29, 2009 at 3:40 PM, Sam Mason<sam@samason.me.uk> wrote:

On Wed, Jul 29, 2009 at 01:15:27PM +0000, Jasen Betts wrote:

On 2009-07-23, Sam Mason <sam@samason.me.uk> wrote:

  http://www.postgres.cz/index.php/PostgreSQL_SQL_Tricks#Attention_on_IS_NULL_and_IS_NOT_NULL_operators_for_composite_types

is scary; even worse is that it was changed to be like this in 8.2
because the standard says it should behave this way.  What on earth were
they thinking when they defined the standard this way?

since any comparson involving those tuples will return NULL true is the
correct value for IS NULL

I think you missed the point:

 SELECT r IS NULL, r IS NOT NULL
 FROM (VALUES (1,NULL)) r(a,b);

returns FALSE for *both* columns.  How can a row be both NULL *and*
non-NULL?

if you are bothered by this behavior you are misusing NULL.

I understand that this is the specified behavior, and hence PG is
correctly following the spec--but it still bothers me.

--
 Sam  http://samason.me.uk/

--
Sent via pgsql-general mailing list (pgsql-general@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-general

--
"Patriotism is the conviction that your country is superior to all
others because you were born in it." -- George Bernard Shaw

#16Sam Mason
sam@samason.me.uk
In reply to: Willy-Bas Loos (#15)
Re: comparing NEW and OLD (any good this way?)

On Wed, Aug 12, 2009 at 10:51:04AM +0200, Willy-Bas Loos wrote:

SELECT r IS NULL, r IS NOT NULL
FROM (VALUES (1,NULL)) r(a,b);

returns FALSE for *both* columns. How can a row be both NULL *and*
non-NULL?

Actually, the value is neither NULL, nor non-NULL.
Part of it is NULL and part of it isn't so neither "IS NULL" is true,
nor is "IS NOT NULL"

Nope, I still don't get it. Why treat rows specially? If this was
true, then what should:

SELECT a IS NULL, a IS NOT NULL
FROM (SELECT ARRAY [1,NULL]) x(a);

evaluate to? As "part of it" is NULL and part isn't then, by your
reasoning, it should return TRUE for both. PG doesn't and I think this
is much more useful behavior. The value itself is not unknown, it just
happens to contain some unknown values. Having a row that consists
entirely of NULL values being treated as NULL is OK, but some weird
halfway house is horrible. Standards' conforming, but still horrible.

--
Sam http://samason.me.uk/

#17Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#16)
Re: comparing NEW and OLD (any good this way?)

Sam Mason wrote:

Nope, I still don't get it. Why treat rows specially? If this was
true, then what should:

SELECT a IS NULL, a IS NOT NULL
FROM (SELECT ARRAY [1,NULL]) x(a);

evaluate to? As "part of it" is NULL and part isn't then, by your
reasoning, it should return TRUE for both. PG doesn't and I think this
is much more useful behavior.

But IS NULL applied to an array is useless to test if there are null values
inside, whereas this is apparently the whole point of IS NULL applied to
rows.
I mean:
select a is null from (select array[null]) x(a);
returns false, as well as:
select a is null from (select array[1]) x(a);

When applied to rows, if you consider that:
- is null applied to a row means that all columns are null
- is not null applied to a row means that all columns are not null
which is what the standard seems to dictate, then these operators make sense
and are probably useful in some situations.

Now there is the unfortunate consequence that (r is null) is not equivalent
to (not (r is not null)), yet it's not the standard's fault if "not all
values are null" is not the same as "all values are not null", that's just
set logic.

Maybe they could have made this easier for us by naming the operators
differently, such as "is entirely null" and "is entirely not null"

Best regards,
--
Daniel
PostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org

#18Merlin Moncure
mmoncure@gmail.com
In reply to: Daniel Verite (#17)
Re: comparing NEW and OLD (any good this way?)

On Wed, Aug 12, 2009 at 10:14 AM, Daniel Verite<daniel@manitou-mail.org> wrote:

       Sam Mason wrote:

Nope, I still don't get it.  Why treat rows specially?  If this was
true, then what should:

  SELECT a IS NULL, a IS NOT NULL
  FROM (SELECT ARRAY [1,NULL]) x(a);

evaluate to?  As "part of it" is NULL and part isn't then, by your
reasoning, it should return TRUE for both.  PG doesn't and I think this
is much more useful behavior.

But IS NULL applied to an array is useless to test if there are null values
inside, whereas this is apparently the whole point of IS NULL applied to
rows.
I mean:
 select a is null from (select array[null]) x(a);
returns false, as well as:
 select a is null from (select array[1]) x(a);

When applied to rows, if you consider that:
- is null applied to a row means that all columns are null
- is not null applied to a row means that all columns are not null
which is what the standard seems to dictate, then these operators make sense
and are probably useful in some situations.

Now there is the unfortunate consequence that (r is null) is not equivalent
to (not (r is not null)), yet it's not the standard's fault if "not all
values are null" is not the same as "all values are not null", that's just
set logic.

Maybe they could have made this easier for us by naming the operators
differently, such as "is entirely null" and "is entirely not null"

IMO, the standard really blew it.

PostgreSQL's approach is ok, minimal standards compliance balanced out
with practical considerations. This leads to some strange behaviors
as noted upthread, but it's workable if you know the tricks. I guess
it's not very well documented....

merlin

#19Sam Mason
sam@samason.me.uk
In reply to: Daniel Verite (#17)
Re: comparing NEW and OLD (any good this way?)

On Wed, Aug 12, 2009 at 04:14:31PM +0200, Daniel Verite wrote:

But IS NULL applied to an array is useless to test if there are null values
inside, whereas this is apparently the whole point of IS NULL applied to
rows.
I mean:
select a is null from (select array[null]) x(a);
returns false, as well as:
select a is null from (select array[1]) x(a);

Yes, I know. But it seems to be a somewhat arbitrary choice to handle
IS NULL for rows differently from everything else.

When applied to rows, if you consider that:
- is null applied to a row means that all columns are null
- is not null applied to a row means that all columns are not null
which is what the standard seems to dictate, then these operators make sense
and are probably useful in some situations.

Yes, I understand what it's specified to do and that it's consistent
with SQL spec. I just think (and Merlin seems to agree) that the spec
has specified the "wrong" behavior.

Now there is the unfortunate consequence that (r is null) is not equivalent
to (not (r is not null)), yet it's not the standard's fault if "not all
values are null" is not the same as "all values are not null", that's just
set logic.

Yes; but this means the user now has to be aware of exactly which type
their code is using as the behavior of various things will magically
change in rare circumstances.

Maybe they could have made this easier for us by naming the operators
differently, such as "is entirely null" and "is entirely not null"

Yes, this would be *much* more preferable. For people aware of it this
it's obviously an easy translation to make, but it's a nasty waiting for
those who aren't and especially for anybody doing anything formal. I.e.
when reasoning about operator semantics you suddenly have to know the
type of data you're dealing with before you can say useful things about
the result. There will of course be ways of avoiding the general case
of an exponential increase in complexity, but it's still nasty.

Anybody else think this thread is past it's bed time and should be put
to rest?

--
Sam http://samason.me.uk/

#20Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#19)
Re: comparing NEW and OLD (any good this way?)

Sam Mason wrote:

But it seems to be a somewhat arbitrary choice to handle
IS NULL for rows differently from everything else.

For scalar or array types, "is null" means that the value happens to be that
special value that we call null. No conceptual problem here.
But for rows, there is no such thing. You can't assign null to a row, it
makes no sense and actually causes an error.
Starting from that point, what consistency can we expect for the "is null"
operator across row types and other types?

Yes, I understand what it's specified to do and that it's consistent
with SQL spec. I just think (and Merlin seems to agree) that the spec
has specified the "wrong" behavior.

So for you guys, what would be the "right" behavior?

Best regards,
--
Daniel
PostgreSQL-powered mail user agent and storage: http://www.manitou-mail.org

#21Sam Mason
sam@samason.me.uk
In reply to: Daniel Verite (#20)
#22Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#21)
#23Sam Mason
sam@samason.me.uk
In reply to: Daniel Verite (#22)
#24Pavel Stehule
pavel.stehule@gmail.com
In reply to: Sam Mason (#23)
#25Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#23)
#26Bruce Momjian
bruce@momjian.us
In reply to: Daniel Verite (#25)
#27Sam Mason
sam@samason.me.uk
In reply to: Bruce Momjian (#26)
#28Daniel Verite
daniel@manitou-mail.org
In reply to: Bruce Momjian (#26)
#29Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#23)
#30Sam Mason
sam@samason.me.uk
In reply to: Daniel Verite (#28)
#31Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#30)
#32Sam Mason
sam@samason.me.uk
In reply to: Daniel Verite (#31)
#33Daniel Verite
daniel@manitou-mail.org
In reply to: Sam Mason (#32)