Обсуждение: How return a row from a function so it is recognized as such by caller?
On version:
PostgreSQL 9.1.2 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6), 64-bit
I get this error (all code at end of post) in pgAdmin:
NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:41:20.338239+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"CONTEXT: PL/pgSQL function "bp_alert_init" line 7 at SQL statement********** Error **********ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"SQL state: 22007Context: PL/pgSQL function "bp_alert_init" line 7 at SQL statement
Note that I have eliminated the complexity of the callback and simply call the desired initializer directly. FWIW, using the desired EXECUTE statement produces exactly the same error.
If I declare the receiving variable to be a record, pgAdmin shows me this:
NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:46:22.62478+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: caller got bpa ("(""2012-04-27 16:46:22.62478+00"",now_plus_30)") <-----------------ERROR: record "bpa" has no field "warn_time"CONTEXT: SQL statement "SELECT bpa.warn_time"PL/pgSQL function "bp_alert_init" line 9 at RAISE
So it looks as if I have to "unwrap" or eval the return value (or change the way I am returning it). But the callee is declared as returning a bp_alert and returns a variable of type bp_alert, so I am not sure what more I can do in the callee. The caller is selecting into a variable of type bp_alert, so that too seems clear.
Hints welcome, code next.
ken
-- code starts here -------------------------------------------------------
set search_path to public;
drop table if exists bp_alert cascade;
CREATE TABLE bp_alert (
warn_time timestamp WITH TIME ZONE,
warn_time_init text
)
CREATE OR REPLACE FUNCTION now_plus_30(bpa bp_alert)
RETURNS bp_alert AS
$BODY$
declare
begin
raise notice 'bpa inbound %', bpa;
bpa.warn_time = now() + interval '30 days';
raise notice 'warn time in input row = %', bpa;
return bpa;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
CREATE OR REPLACE FUNCTION bp_alert_init()
RETURNS trigger AS
$BODY$
declare
bpa bp_alert; -- make this a record and the "warn time in caller" raise fails on bpa not having warn_time
begin
-- no difference: execute 'select ' || NEW.warn_time_init || '($1)' using NEW into bpa;
select now_plus_30(NEW) into bpa;
raise notice 'caller got bpa %', bpa;
raise notice 'warn time in caller now %', bpa.warn_time;
return bpa;
end;
$BODY$
LANGUAGE plpgsql VOLATILE;
drop trigger if exists bp_alert on bp_alert;
CREATE TRIGGER bp_alert
BEFORE INSERT
ON bp_alert
FOR EACH ROW
EXECUTE PROCEDURE bp_alert_init();
insert into bp_alert (warn_time_init) values ('now_plus_30');
First, apologies for being too succinct. I should have reiterated the message subject to provide the context: I am just trying to return a row from a function and have the caller understand it. Oh, and I am a nooby so it is probably something daft.
Second, I just tried returning the row as an out variable and got the same result. I'll try messing with the caller...
-kt
On Wed, Mar 28, 2012 at 12:54 PM, Kenneth Tilton <ktilton@mcna.net> wrote:
On version:PostgreSQL 9.1.2 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6), 64-bitI get this error (all code at end of post) in pgAdmin:NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:41:20.338239+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"CONTEXT: PL/pgSQL function "bp_alert_init" line 7 at SQL statement********** Error **********ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"SQL state: 22007Context: PL/pgSQL function "bp_alert_init" line 7 at SQL statementNote that I have eliminated the complexity of the callback and simply call the desired initializer directly. FWIW, using the desired EXECUTE statement produces exactly the same error.If I declare the receiving variable to be a record, pgAdmin shows me this:NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:46:22.62478+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: caller got bpa ("(""2012-04-27 16:46:22.62478+00"",now_plus_30)") <-----------------ERROR: record "bpa" has no field "warn_time"CONTEXT: SQL statement "SELECT bpa.warn_time"PL/pgSQL function "bp_alert_init" line 9 at RAISESo it looks as if I have to "unwrap" or eval the return value (or change the way I am returning it). But the callee is declared as returning a bp_alert and returns a variable of type bp_alert, so I am not sure what more I can do in the callee. The caller is selecting into a variable of type bp_alert, so that too seems clear.Hints welcome, code next.ken-- code starts here -------------------------------------------------------set search_path to public;drop table if exists bp_alert cascade;CREATE TABLE bp_alert (warn_time timestamp WITH TIME ZONE,warn_time_init text)CREATE OR REPLACE FUNCTION now_plus_30(bpa bp_alert)RETURNS bp_alert AS$BODY$declarebeginraise notice 'bpa inbound %', bpa;bpa.warn_time = now() + interval '30 days';raise notice 'warn time in input row = %', bpa;return bpa;end;$BODY$LANGUAGE plpgsql VOLATILE;CREATE OR REPLACE FUNCTION bp_alert_init()RETURNS trigger AS$BODY$declarebpa bp_alert; -- make this a record and the "warn time in caller" raise fails on bpa not having warn_timebegin-- no difference: execute 'select ' || NEW.warn_time_init || '($1)' using NEW into bpa;select now_plus_30(NEW) into bpa;raise notice 'caller got bpa %', bpa;raise notice 'warn time in caller now %', bpa.warn_time;return bpa;end;$BODY$LANGUAGE plpgsql VOLATILE;drop trigger if exists bp_alert on bp_alert;CREATE TRIGGER bp_alertBEFORE INSERTON bp_alertFOR EACH ROWEXECUTE PROCEDURE bp_alert_init();insert into bp_alert (warn_time_init) values ('now_plus_30');
On Wed, Mar 28, 2012 at 1:52 PM, Kenneth Tilton <ktilton@mcna.net> wrote:
First, apologies for being too succinct. I should have reiterated the message subject to provide the context: I am just trying to return a row from a function and have the caller understand it. Oh, and I am a nooby so it is probably something daft.Second, I just tried returning the row as an out variable and got the same result. I'll try messing with the caller...
OK, this works in re getting the row back:
bpa := now_plus_30(NEW);
But I need to execute an arbitrary function passed in as text, and I now realize EXECUTE is for SQL and I am trying to use it to "eval" plpgsql and those are different animals.
I see no plpgsql equivalent of EXECUTE, ie where I can build up a plpgsql statement like this:
execute 'bpa := ' || function_name || '($1)' using NEW into bpa;
Ideas welcome.
--kt
-ktOn Wed, Mar 28, 2012 at 12:54 PM, Kenneth Tilton <ktilton@mcna.net> wrote:On version:PostgreSQL 9.1.2 on x86_64-unknown-linux-gnu, compiled by gcc (GCC) 4.4.5 20110214 (Red Hat 4.4.5-6), 64-bitI get this error (all code at end of post) in pgAdmin:NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:41:20.338239+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"CONTEXT: PL/pgSQL function "bp_alert_init" line 7 at SQL statement********** Error **********ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 16:41:20.338239+00",now_plus_30)"SQL state: 22007Context: PL/pgSQL function "bp_alert_init" line 7 at SQL statementNote that I have eliminated the complexity of the callback and simply call the desired initializer directly. FWIW, using the desired EXECUTE statement produces exactly the same error.If I declare the receiving variable to be a record, pgAdmin shows me this:NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: warn time in input row = ("2012-04-27 16:46:22.62478+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30(NEW)"PL/pgSQL function "bp_alert_init" line 7 at SQL statementNOTICE: caller got bpa ("(""2012-04-27 16:46:22.62478+00"",now_plus_30)") <-----------------ERROR: record "bpa" has no field "warn_time"CONTEXT: SQL statement "SELECT bpa.warn_time"PL/pgSQL function "bp_alert_init" line 9 at RAISESo it looks as if I have to "unwrap" or eval the return value (or change the way I am returning it). But the callee is declared as returning a bp_alert and returns a variable of type bp_alert, so I am not sure what more I can do in the callee. The caller is selecting into a variable of type bp_alert, so that too seems clear.Hints welcome, code next.ken-- code starts here -------------------------------------------------------set search_path to public;drop table if exists bp_alert cascade;CREATE TABLE bp_alert (warn_time timestamp WITH TIME ZONE,warn_time_init text)CREATE OR REPLACE FUNCTION now_plus_30(bpa bp_alert)RETURNS bp_alert AS$BODY$declarebeginraise notice 'bpa inbound %', bpa;bpa.warn_time = now() + interval '30 days';raise notice 'warn time in input row = %', bpa;return bpa;end;$BODY$LANGUAGE plpgsql VOLATILE;CREATE OR REPLACE FUNCTION bp_alert_init()RETURNS trigger AS$BODY$declarebpa bp_alert; -- make this a record and the "warn time in caller" raise fails on bpa not having warn_timebegin-- no difference: execute 'select ' || NEW.warn_time_init || '($1)' using NEW into bpa;select now_plus_30(NEW) into bpa;raise notice 'caller got bpa %', bpa;raise notice 'warn time in caller now %', bpa.warn_time;return bpa;end;$BODY$LANGUAGE plpgsql VOLATILE;drop trigger if exists bp_alert on bp_alert;CREATE TRIGGER bp_alertBEFORE INSERTON bp_alertFOR EACH ROWEXECUTE PROCEDURE bp_alert_init();insert into bp_alert (warn_time_init) values ('now_plus_30');
On Wed, Mar 28, 2012 at 1:11 PM, Kenneth Tilton <ktilton@mcna.net> wrote: > > > On Wed, Mar 28, 2012 at 1:52 PM, Kenneth Tilton <ktilton@mcna.net> wrote: >> >> First, apologies for being too succinct. I should have reiterated the >> message subject to provide the context: I am just trying to return a row >> from a function and have the caller understand it. Oh, and I am a nooby so >> it is probably something daft. >> >> Second, I just tried returning the row as an out variable and got the same >> result. I'll try messing with the caller... > > > OK, this works in re getting the row back: > > bpa := now_plus_30(NEW); > > But I need to execute an arbitrary function passed in as text, and I now > realize EXECUTE is for SQL and I am trying to use it to "eval" plpgsql and > those are different animals. > > I see no plpgsql equivalent of EXECUTE, ie where I can build up a plpgsql > statement like this: > > execute 'bpa := ' || function_name || '($1)' using NEW into bpa; If all you are doing is assignment into a variable, you can use EXECUTE...INTO...USING. That should work. merlin
On Wed, Mar 28, 2012 at 2:36 PM, Merlin Moncure <mmoncure@gmail.com> wrote:
On Wed, Mar 28, 2012 at 1:11 PM, Kenneth Tilton <ktilton@mcna.net> wrote:If all you are doing is assignment into a variable, you can use
>
>
> On Wed, Mar 28, 2012 at 1:52 PM, Kenneth Tilton <ktilton@mcna.net> wrote:
>>
>> First, apologies for being too succinct. I should have reiterated the
>> message subject to provide the context: I am just trying to return a row
>> from a function and have the caller understand it. Oh, and I am a nooby so
>> it is probably something daft.
>>
>> Second, I just tried returning the row as an out variable and got the same
>> result. I'll try messing with the caller...
>
>
> OK, this works in re getting the row back:
>
> bpa := now_plus_30(NEW);
>
> But I need to execute an arbitrary function passed in as text, and I now
> realize EXECUTE is for SQL and I am trying to use it to "eval" plpgsql and
> those are different animals.
>
> I see no plpgsql equivalent of EXECUTE, ie where I can build up a plpgsql
> statement like this:
>
> execute 'bpa := ' || function_name || '($1)' using NEW into bpa;
EXECUTE...INTO...USING. That should work.
Thanks, Merlin. Maybe I have some subtle detail wrong. When NEW.warn_time_init is 'now_plus_30' and I have this as my execute statement:
execute NEW.warn_time_init || '($1)' into bpa using NEW;
...I get:
ERROR: syntax error at or near "now_plus_30"
LINE 1: now_plus_30($1)
^
QUERY: now_plus_30($1)
CONTEXT: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
********** Error **********
ERROR: syntax error at or near "now_plus_30"
SQL state: 42601
Context: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
Using a more conventional syntax I am back to where I started:
execute 'select ' || NEW.warn_time_init || '($1)' into bpa using NEW;
....produces:
NOTICE: bpa inbound (,now_plus_30)CONTEXT: SQL statement "select now_plus_30($1)"PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statementNOTICE: warn time in input row = ("2012-04-27 19:04:37.793835+00",now_plus_30)CONTEXT: SQL statement "select now_plus_30($1)"PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statementERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 19:04:37.793835+00",now_plus_30)"CONTEXT: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement********** Error **********ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 19:04:37.793835+00",now_plus_30)"SQL state: 22007Context: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
cheers, ken
On Wed, Mar 28, 2012 at 2:08 PM, Kenneth Tilton <ktilton@mcna.net> wrote: > Thanks, Merlin. Maybe I have some subtle detail wrong. When > NEW.warn_time_init is 'now_plus_30' and I have this as my execute statement: > > execute NEW.warn_time_init || '($1)' into bpa using NEW; > > ...I get: > your'e missing a 'select' in there. functions must be called through select (just like you can't call a function by name in psql without it). merlin
On Wed, Mar 28, 2012 at 3:40 PM, Merlin Moncure <mmoncure@gmail.com> wrote:
On Wed, Mar 28, 2012 at 2:08 PM, Kenneth Tilton <ktilton@mcna.net> wrote:your'e missing a 'select' in there. functions must be called through
> Thanks, Merlin. Maybe I have some subtle detail wrong. When
> NEW.warn_time_init is 'now_plus_30' and I have this as my execute statement:
>
> execute NEW.warn_time_init || '($1)' into bpa using NEW;
>
> ...I get:
>
select (just like you can't call a function by name in psql without
it).
Well then I have the other error. With this code:
execute 'select ' || NEW.warn_time_init || '($1)' into bpa using NEW;
I get:
NOTICE: bpa inbound (,now_plus_30)
CONTEXT: SQL statement "select now_plus_30($1)"
PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
NOTICE: warn time in input row = ("2012-04-27 19:04:37.793835+00",now_plus_30)
CONTEXT: SQL statement "select now_plus_30($1)"
PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 19:04:37.793835+00",now_plus_30)"
CONTEXT: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
********** Error **********
ERROR: invalid input syntax for type timestamp with time zone: "("2012-04-27 19:04:37.793835+00",now_plus_30)"
SQL state: 22007
Context: PL/pgSQL function "bp_alert_init" line 6 at EXECUTE statement
I guess you are right, though. This is the error I have to resolve.
Thx,
-kt
On Wed, Mar 28, 2012 at 2:49 PM, Kenneth Tilton <ktilton@mcna.net> wrote: > Well then I have the other error. With this code: > > execute 'select ' || NEW.warn_time_init || '($1)' into bpa using NEW; try this: select * from now_plus_30(NEW) into bpa; :-D merlin
On Wed, Mar 28, 2012 at 4:02 PM, Merlin Moncure <mmoncure@gmail.com> wrote:
On Wed, Mar 28, 2012 at 2:49 PM, Kenneth Tilton <ktilton@mcna.net> wrote:try this:
> Well then I have the other error. With this code:
>
> execute 'select ' || NEW.warn_time_init || '($1)' into bpa using NEW;
select * from now_plus_30(NEW) into bpa;
Yer a genius. Thx!
-kt