Обсуждение: Bug or feature in AFTER INSERT trigger?

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

Bug or feature in AFTER INSERT trigger?

От
Martin Edlman
Дата:
Hello,

today I encountered strange behaviour in PgSQL 9.0 (tried in 9.3 with
same effect).

There is a table and an AFTER INSERT trigger which call a function which
counts a number of records in the same table. But the newly inserted
record is not selected and counted.

When I delete a record and the very same AFTER trigger calls the very
same function, selected and counted records are already without the
deleted one.

I supposed that after insert the record is already in the database,
isn't it true?!

The documentation doesn't mention this. (or I didn't find it).

Can someone confirm it as a bug or explain why it works this way.

Regards,
Martin Edlman



EXAMPLE:

-- FUNCTION

CREATE OR REPLACE FUNCTION tmp.email_service(contrid integer) RETURNS integer AS
$BODY$
DECLAREsid  integer := 119;rec  record;vfrom date;vto date;cmnt text;cnt integer := 0;
BEGINvfrom := date_trunc('month', now());vto := date_trunc('month', now() + interval '1 month') - interval '1
day';RAISENOTICE 'sid %, from %, to %', sid, vfrom, vto;FOR rec IN    SELECT ma.contract_id, count(ma.*) as unitage,
string_agg(ma.email,',
 
' order by ma.email) as emails    FROM tmp.mail_account as ma    WHERE contract_id = contrid    AND
coalesce(ma.valid_from,'-infinity') < now()    AND coalesce(ma.valid_to, 'infinity') > now()    GROUP BY 1LOOP    RAISE
NOTICE'number of mails: %, mails: %', rec.unitage, rec.emails;    cnt := cnt + 1;    -- here is some code which inserts
orupdates    -- services ...END LOOP;RETURN cnt;
 
END
$BODY$ LANGUAGE plpgsql VOLATILE COST 100;
ALTER FUNCTION tmp.email_service(integer) OWNER TO postgres;
GRANT EXECUTE ON FUNCTION tmp.email_service(integer) TO public;
GRANT EXECUTE ON FUNCTION tmp.email_service(integer) TO postgres;




-- TRIGGER FUNCTION
CREATE OR REPLACE FUNCTION tmp.email_service() RETURNS trigger AS
$BODY$
BEGINIF TG_OP = 'INSERT' THEN    RAISE NOTICE '% % email %@% inserted, setting services for id %',
TG_WHEN, TG_OP, NEW.username, NEW.domain, NEW.contract_id;    -- call a function    PERFORM
tmp.email_service(NEW.contract_id);   RETURN NEW;END IF;
 
IF TG_OP = 'UPDATE' THEN    --    RETURN NEW;END IF;
IF TG_OP = 'DELETE' THEN    PERFORM tmp.email_service(OLD.contract_id);    RETURN OLD;END IF;
RETURN NULL;
END
$BODY$ LANGUAGE plpgsql VOLATILE COST 100;
ALTER FUNCTION tmp.email_service() OWNER TO edlman;
GRANT EXECUTE ON FUNCTION tmp.email_service() TO public;
GRANT EXECUTE ON FUNCTION tmp.email_service() TO edlman;




-- TABLE
CREATE TABLE tmp.mail_account
( id serial NOT NULL, contract_id integer NOT NULL, username character varying(50) NOT NULL, domain character
varying(100)NOT NULL, email character varying(255) NOT NULL, valid_from timestamp without time zone DEFAULT now(),
valid_totimestamp without time zone, CONSTRAINT mail_account_pkey PRIMARY KEY (id)
 
)
WITH ( OIDS=FALSE
);
ALTER TABLE tmp.mail_account OWNER TO postgres;

CREATE UNIQUE INDEX mail_account_email ON tmp.mail_account USING btree (email COLLATE pg_catalog."default");
CREATE UNIQUE INDEX mail_account_email_idx ON tmp.mail_account USING btree (username COLLATE pg_catalog."default",
domainCOLLATE
 
pg_catalog."default");
CREATE INDEX mail_account_username_idx ON tmp.mail_account USING btree (username COLLATE pg_catalog."default");
CREATE TRIGGER email_service AFTER INSERT OR UPDATE OR DELETE ON tmp.mail_account FOR EACH ROW EXECUTE PROCEDURE
tmp.email_service();




Re: Bug or feature in AFTER INSERT trigger?

От
hubert depesz lubaczewski
Дата:
<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px
0px0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> There is a table and an AFTER INSERT trigger which
calla function which<br /> counts a number of records in the same table. But the newly inserted<br /> record is not
selectedand counted.<br /></blockquote></div><br /></div><div class="gmail_extra">The problem is with your function,
notPg logic.<br /><br /></div><div class="gmail_extra">Namely you have this condition:<br /><br />        AND
coalesce(ma.valid_from,'-infinity') < now()<br />        AND coalesce(ma.valid_to, 'infinity') > now()<br /><br
/></div><divclass="gmail_extra">Let's assume you didn't fill in values for valid_from/valid_to. Valid_from, due to
"default"becomes now(). and valid_to null.<br /><br /></div><div class="gmail_extra">The thing is now() doesn't change
withintransaction.<br /><br /></div><div class="gmail_extra">So the value of now() that your where compares is
*exactly*the same as the one inserted into row.<br /><br />So, the condition: coalesce(ma.valid_from, '-infinity)
<now()returns false, because it is = now(), and not < now().<br /><br /></div><div class="gmail_extra">If you'd
insertliteral NULL value, for example by doing:<br /><br />INSERT INTO tmp.mail_account(contract_id, username, domain,
email,valid_from, valid_to) VALUES (123, 'depesz', '<a href="http://depesz.com">depesz.com</a>', '<a
href="mailto:depesz@gmail.com">depesz@gmail.com</a>',NULL, NULL);<br /><br /></div><div class="gmail_extra">Then, the
columnwould be null, and coalesce() would return '-infinity', which would give true when comparing with now().<br /><br
/>Butif you insert data like:<br /><br />INSERT INTO tmp.mail_account(contract_id, username, domain, email) VALUES
(123,'depesz', '<a href="http://depesz.com">depesz.com</a>', '<a
href="mailto:depesz@gmail.com">depesz@gmail.com</a>');<br/><br /></div><div class="gmail_extra">Then the valid_from
getsvalue from default expression.<br /><br /></div><div class="gmail_extra">depesz<br /></div></div>