Changes to functions and triggers

Started by Nonameover 25 years ago13 messages
#1Noname
darcy@druid.net

Something changed in 7.02 from 6.3. I used to do this:

CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date(edate, aniv, emon, eyear);

This no longer works. I looked and the docs and it seems that this
should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

But this gives me;

ERROR: CreateTrigger: function make_date() does not exist

Is this broken now or am I not understanding the documentation? Why
is it looking for a make_date that takes no args?

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#2Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#1)
Re: Changes to functions and triggers

darcy@druid.net (D'Arcy J.M. Cain) writes:

Something changed in 7.02 from 6.3. I used to do this:
CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date(edate, aniv, emon, eyear);

This no longer works.

Details?

I looked and the docs and it seems that this should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

No. Trigger procedures never take explicit arguments --- whatever
you may have stated in the CREATE TRIGGER command gets passed in
in the trigger data structure. (A pretty bizarre and ugly choice
if you ask me, but not worth breaking existing code to change...)

There's surely been a lot of changes in 7.0 that could have broken
user-written triggers, but you'll need to look to your C code to
find the problem. What you've shown us looks fine.

regards, tom lane

#3Thomas Lockhart
lockhart@alumni.caltech.edu
In reply to: Noname (#1)
Re: Changes to functions and triggers

This no longer works. I looked and the docs and it seems that this
should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

What if you leave out the quotes on the last line above, so the column
names are actually visible? It looks like the example in the docs might
be a poor choice since the function is intended to manipulate columns,
so the text representation of the column name is being passed in.

Don't know if that is the source of your trouble though...

- Thomas

#4Noname
darcy@druid.net
In reply to: Thomas Lockhart (#3)
Re: Changes to functions and triggers

Thus spake Thomas Lockhart

This no longer works. I looked and the docs and it seems that this
should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

What if you leave out the quotes on the last line above, so the column
names are actually visible? It looks like the example in the docs might
be a poor choice since the function is intended to manipulate columns,
so the text representation of the column name is being passed in.

Don't know if that is the source of your trouble though...

Nope. I added that after reading the web page but without them it still
has the problem.

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#5Noname
darcy@druid.net
In reply to: Tom Lane (#2)
Re: Changes to functions and triggers

Thus spake Tom Lane

darcy@druid.net (D'Arcy J.M. Cain) writes:

Something changed in 7.02 from 6.3. I used to do this:
CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date(edate, aniv, emon, eyear);

This no longer works.

Details?

Same error as I gave for the new version I wrote.

I looked and the docs and it seems that this should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

No. Trigger procedures never take explicit arguments --- whatever
you may have stated in the CREATE TRIGGER command gets passed in
in the trigger data structure. (A pretty bizarre and ugly choice
if you ask me, but not worth breaking existing code to change...)

Hmm. Are you saying that the above is wrong? I took it right from
the web page documentation.

There's surely been a lot of changes in 7.0 that could have broken
user-written triggers, but you'll need to look to your C code to
find the problem. What you've shown us looks fine.

Really? That code always worked before. Besides, it doesn't look to me
like my C code ever gets called. The failure seems to be at the SQL
level saying that there is no function to call.

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#5)
Re: Changes to functions and triggers

darcy@druid.net (D'Arcy J.M. Cain) writes:

I looked and the docs and it seems that this should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

No. Trigger procedures never take explicit arguments --- whatever
you may have stated in the CREATE TRIGGER command gets passed in
in the trigger data structure. (A pretty bizarre and ugly choice
if you ask me, but not worth breaking existing code to change...)

Hmm. Are you saying that the above is wrong?

Yes.

I took it right from the web page documentation.

What web page? http://www.postgresql.org/docs/postgres/triggers.htm
still says what it always has (complete with bad grammar ;-)):

The trigger function must be created before the trigger is
created as a function taking no arguments and returns opaque.

regards, tom lane

#7Noname
darcy@druid.net
In reply to: Tom Lane (#6)
Re: Changes to functions and triggers

Thus spake Tom Lane

darcy@druid.net (D'Arcy J.M. Cain) writes:

I looked and the docs and it seems that this should work instead.

CREATE FUNCTION make_date(date, int, int, int)
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';
CREATE TRIGGER make_edate
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date('edate', 'aniv', 'emon', 'eyear');

No. Trigger procedures never take explicit arguments --- whatever
you may have stated in the CREATE TRIGGER command gets passed in
in the trigger data structure. (A pretty bizarre and ugly choice
if you ask me, but not worth breaking existing code to change...)

Hmm. Are you saying that the above is wrong?

Yes.

I took it right from the web page documentation.

What web page? http://www.postgresql.org/docs/postgres/triggers.htm
still says what it always has (complete with bad grammar ;-)):

The trigger function must be created before the trigger is
created as a function taking no arguments and returns opaque.

OK, so I went back to basically what I had before.

CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';

CREATE TRIGGER make_dates
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date (edate, aniv, emon, eyear);

INSERT INTO bgroup (bname, client_id, actypid, aniv, emon, eyear, pmon, pyear)
VALUES ('guest', 1000, 1, 1, 1, 2000, 1, 2000);

And here is what I get.

ERROR: fmgr_info: function 24224: cache lookup failed

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#8Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#7)
Re: Changes to functions and triggers

darcy@druid.net (D'Arcy J.M. Cain) writes:

OK, so I went back to basically what I had before.

CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';

CREATE TRIGGER make_dates
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date (edate, aniv, emon, eyear);

INSERT INTO bgroup (bname, client_id, actypid, aniv, emon, eyear, pmon, pyear)
VALUES ('guest', 1000, 1, 1, 1, 2000, 1, 2000);

Looks OK to me.

And here is what I get.
ERROR: fmgr_info: function 24224: cache lookup failed

You sure you didn't fall into the same old trap of you-must-create-
the-trigger-after-the-function? If you drop and recreate the function,
it has a new OID, so you have to drop and recreate the trigger because
it links to the function by OID.

(Someday we ought to make that work better.)

regards, tom lane

#9Noname
darcy@druid.net
In reply to: Tom Lane (#8)
Re: Changes to functions and triggers

Thus spake Tom Lane

darcy@druid.net (D'Arcy J.M. Cain) writes:

OK, so I went back to basically what I had before.

CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';

CREATE TRIGGER make_dates
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date (edate, aniv, emon, eyear);

INSERT INTO bgroup (bname, client_id, actypid, aniv, emon, eyear, pmon, pyear)
VALUES ('guest', 1000, 1, 1, 1, 2000, 1, 2000);

Looks OK to me.

And here is what I get.
ERROR: fmgr_info: function 24224: cache lookup failed

You sure you didn't fall into the same old trap of you-must-create-
the-trigger-after-the-function? If you drop and recreate the function,
it has a new OID, so you have to drop and recreate the trigger because
it links to the function by OID.

Positive. I dropped both then did the above in the order shown.

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#10Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#9)
Re: Changes to functions and triggers

darcy@druid.net (D'Arcy J.M. Cain) writes:

You sure you didn't fall into the same old trap of you-must-create-
the-trigger-after-the-function? If you drop and recreate the function,
it has a new OID, so you have to drop and recreate the trigger because
it links to the function by OID.

Positive. I dropped both then did the above in the order shown.

Hm. Is the OID shown in the error message the correct OID for your
trigger function, or not? (Try "select oid,* from pg_proc where
proname = 'make_date'", maybe also "select * from pg_proc where
oid = 24224")

There are trigger examples in the regress tests that are hardly
different from your example, so the trigger feature is surely not
broken completely. Maybe a platform-specific problem? Which
version were you running again, exactly, and what configuration
options? BTW, do the regress tests pass for you?

regards, tom lane

#11Noname
darcy@druid.net
In reply to: Noname (#7)
Re: Changes to functions and triggers

Thus spake D'Arcy J.M. Cain

OK, so I went back to basically what I had before.

CREATE FUNCTION make_date()
RETURNS opaque
AS '/usr/pgsql/modules/make_date.so'
LANGUAGE 'c';

CREATE TRIGGER make_dates
BEFORE INSERT OR UPDATE ON bgroup
FOR EACH ROW
EXECUTE PROCEDURE make_date (edate, aniv, emon, eyear);

INSERT INTO bgroup (bname, client_id, actypid, aniv, emon, eyear, pmon, pyear)
VALUES ('guest', 1000, 1, 1, 1, 2000, 1, 2000);

And here is what I get.

ERROR: fmgr_info: function 24224: cache lookup failed

I must have done this wrong. The actual error I get when I start from
scratch is this:

ERROR: make_date (bgroup): 0 args

That message comes from my program at the start of the function.

if (!CurrentTriggerData)
elog(ERROR, "make_date: triggers are not initialized");
if (TRIGGER_FIRED_FOR_STATEMENT(CurrentTriggerData->tg_event))
elog(ERROR, "make_date: can't process STATEMENT events");
if (TRIGGER_FIRED_AFTER(CurrentTriggerData->tg_event))
elog(ERROR, "make_date: must be fired before event");

if (TRIGGER_FIRED_BY_INSERT(CurrentTriggerData->tg_event))
rettuple = CurrentTriggerData->tg_trigtuple;
else if (TRIGGER_FIRED_BY_UPDATE(CurrentTriggerData->tg_event))
rettuple = CurrentTriggerData->tg_newtuple;
else
elog(ERROR, "make_date: can't process DELETE events");

rel = CurrentTriggerData->tg_relation;
relname = SPI_getrelname(rel);

trigger = CurrentTriggerData->tg_trigger;

nargs = trigger->tgnargs;
if (nargs != 4)
elog(ERROR, "make_date (%s): %d args", relname, nargs);

All I have before that is the declarations.

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.
#12Tom Lane
tgl@sss.pgh.pa.us
In reply to: Noname (#11)
Re: Changes to functions and triggers

darcy@druid.net (D'Arcy J.M. Cain) writes:

I must have done this wrong. The actual error I get when I start from
scratch is this:
ERROR: make_date (bgroup): 0 args

That message comes from my program at the start of the function.

[ blah blah blah ]

trigger = CurrentTriggerData->tg_trigger;

nargs = trigger->tgnargs;
if (nargs != 4)
elog(ERROR, "make_date (%s): %d args", relname, nargs);

Hmm. Not sure if this is the root of the problem or not, but it's
*real* dangerous to assume that the global CurrentTriggerData stays
set (the same way!) throughout your function. You should copy
CurrentTriggerData into a local TriggerData * variable immediately
upon being called and then just use that variable. I wonder if you
could be losing because some other trigger is getting invoked before
your routine returns...

CurrentTriggerData doesn't even exist anymore in current sources,
so doing it that way will also ease the pain of updating to 7.1 ;-)

Another thing to keep in mind is that the data structures pointed to
by CurrentTriggerData had better be treated as read-only.

regards, tom lane

#13Noname
darcy@druid.net
In reply to: Tom Lane (#12)
Re: Changes to functions and triggers

Thus spake Zeugswetter Andreas

darcy@druid.net (D'Arcy J.M. Cain) writes:

nargs = trigger->tgnargs;
if (nargs != 4)
elog(ERROR, "make_date (%s): %d args", relname, nargs);

The simple answer is, that your procedure does not take four arguments.
The old and new tuple are passed implicitly to your procedure,
they don't show up in the argument list.

Right. That's why the function takes void as its parameter list. (I hadn't
shown that in my message.) The code above finds the args from the global
environment.

In fact, my problem was, I think, that I needed to use the SPI_connect() and
SPI_finish() functions which I don't think were available when I first wrote
the function. I added those, recompiled and all now works. It just took
me a while to realize that the problem was in the C code and not the SQL
statements to use it.

This is also the reason your code works if you add four "dummy" string
arguments (you can test that by supplying random values not the column names
in your create trigger statement).

Nope. Without the fix above it didn't work no matter what I tried.

Thanks for everyone's help. Now I can move on to the operator defining
problem but that's a subject for another message.

It's funny but the two areas inthe new version that I am having trouble
with are the two that I originally helped document. :-)

-- 
D'Arcy J.M. Cain <darcy@{druid|vex}.net>   |  Democracy is three wolves
http://www.druid.net/darcy/                |  and a sheep voting on
+1 416 425 1212     (DoD#0082)    (eNTP)   |  what's for dinner.