Обсуждение: BUG #4380: Comparison of OLD and NEW columns in trigger does not always work

Поиск
Список
Период
Сортировка

BUG #4380: Comparison of OLD and NEW columns in trigger does not always work

От
"Daryl Joubert"
Дата:
The following bug has been logged online:

Bug reference:      4380
Logged by:          Daryl Joubert
Email address:      daryl@ftcxpress.com
PostgreSQL version: 8.3.3
Operating system:   WinXP SP2
Description:        Comparison of OLD and NEW columns in trigger does not
always work
Details:

When comparing NEW.column_name to OLD.column_name in 'PG_UPDATE' operation
in a trigger, I get unpredictable results. Use the following script to
create the "Business Partner" table and its trigger, then add a few rows of
data to "First Name", "Middle Name" and "Last Name" using pgAdmin, and watch
how "Name" is built up for the former 3 columns.

Then: change the "Middle Name" column of an existing row repeatedly and you
should see that sometimes "Name" is updated, sometimes not. I can repeat the
problem here within 5 to 10 changes of "Middle Name". Am I doing something
wrong? Here is the script:

CREATE TABLE "Business Partner"
(
  "Partner Number" serial NOT NULL,
  "First Name" character varying,
  "Middle Name" character varying,
  "Last Name" character varying,
  "Name" character varying,
  CONSTRAINT "PK Business Partner" PRIMARY KEY ("Partner Number")
)
WITH (OIDS=FALSE);

CREATE FUNCTION t_business_partner()
  RETURNS trigger AS
$BODY$
DECLARE
BEGIN
  IF (TG_OP = 'INSERT') THEN
    IF NEW."Name" IS NULL OR NEW."Name" = '' THEN
      IF NEW."Middle Name" IS NULL OR NEW."Middle Name" = '' THEN
        NEW."Name" := NEW."First Name" || ' ' || NEW."Last Name";
      ELSE
        NEW."Name" := NEW."First Name" || ' ' || NEW."Middle Name" || ' ' ||
NEW."Last Name";
      END IF;
    END IF;
    NEW."Name" = initcap(NEW."Name");
    NEW."First Name" = initcap(NEW."First Name");
    NEW."Middle Name" = initcap(NEW."Middle Name");
    NEW."Last Name" = initcap(NEW."Last Name");
    RETURN NEW;
  END IF;

  IF (TG_OP = 'UPDATE') THEN
    IF NEW."Name" IS NULL OR NEW."Name" = ''
    OR NEW."First Name" <> OLD."First Name"
    OR NEW."Middle Name" <> OLD."Middle Name"
    OR NEW."Last Name" <> OLD."Last Name" THEN
      IF NEW."Middle Name" IS NULL OR NEW."Middle Name" = '' THEN
        NEW."Name" = NEW."First Name" || ' ' || NEW."Last Name";
      ELSE
        NEW."Name" = NEW."First Name" || ' ' || NEW."Middle Name" || ' ' ||
NEW."Last Name";
      END IF;
      NEW."Name" = initcap(NEW."Name");
      NEW."First Name" = initcap(NEW."First Name");
      NEW."Middle Name" = initcap(NEW."Middle Name");
      NEW."Last Name" = initcap(NEW."Last Name");
    END IF;
    RETURN NEW;
  END IF;

  IF (TG_OP = 'DELETE') THEN
    RETURN OLD;
  END IF;
END;
$BODY$
  LANGUAGE 'plpgsql' VOLATILE
  COST 100;

CREATE TRIGGER "TR Business Partner"
  BEFORE INSERT OR UPDATE OR DELETE
  ON "Business Partner"
  FOR EACH ROW
  EXECUTE PROCEDURE t_business_partner();

Re: BUG #4380: Comparison of OLD and NEW columns in trigger does not always work

От
Stephan Szabo
Дата:
On Wed, 27 Aug 2008, Daryl Joubert wrote:

>
> The following bug has been logged online:
>
> Bug reference:      4380
> Logged by:          Daryl Joubert
> Email address:      daryl@ftcxpress.com
> PostgreSQL version: 8.3.3
> Operating system:   WinXP SP2
> Description:        Comparison of OLD and NEW columns in trigger does not
> always work
> Details:
>
> When comparing NEW.column_name to OLD.column_name in 'PG_UPDATE' operation
> in a trigger, I get unpredictable results. Use the following script to
> create the "Business Partner" table and its trigger, then add a few rows of
> data to "First Name", "Middle Name" and "Last Name" using pgAdmin, and watch
> how "Name" is built up for the former 3 columns.
>
> Then: change the "Middle Name" column of an existing row repeatedly and you
> should see that sometimes "Name" is updated, sometimes not. I can repeat the
> problem here within 5 to 10 changes of "Middle Name". Am I doing something
> wrong? Here is the script:

Well, the function does the wrong thing if you set a field to NULL or
change a field that was previously NULL to a non-NULL value since a <>
NULL is unknown not true. And once that happens you might need to do a
couple of updates to get out of the odd state.

Is it possible that the updates that are failing fit that pattern? If so,
using IS DISTINCT FROM rather than <> may work.