On 05/18/04:20/2, Tom Lane wrote: > Hm? That functionality works for any function, whether it returns a set > or not.
Okay, then I think I found a bug: SELECT * FROM aFunction(); Gives fcinfo->resultinfo != NULL, regardless of the type of return. SELECT aFunction(); Gives fcinfo->resultinfo != NULL, ONLY IF it is a SRF.(fn_retset != 0) I think the culprit is in the function ExecMakeFunctionResult in file execQual.c, line ~1230: :e execQual.c /retset ....... /* * If function returns set, prepare a resultinfo node for * communication */ if (fcache->func.fn_retset) { fcinfo.resultinfo = (Node *) &rsinfo; ........ And to be nagging: Utility functions like OidFunctionCall# don't setup resultinfo, and probably rightfully so in some regards, but ISTM that there should be a mechanism that is independent of the executor. Maybe an explicit requirement to call a "FunctionCallCleanup(fcinfo)", or, dare I say, free hooks on pointers? :) I attached a simple patch that seems to make it work, but I'm not sure if there will be any unwanted side effects, as I'm barely familiar with the executor.... Here's a bunch of data that I collected, probably not very enlightening after the preceding summary(Discovered the pattern after gathering all this data).. ----------------------- uname -a FreeBSD void 4.10-PRERELEASE FreeBSD 4.10-PRERELEASE #5: Wed Apr 28 06:12:01 MST 2004 [EMAIL PROTECTED]:/files/src/freebsd/RELENG_4/sys/compile/void i386 backend> SELECT version(); 1: version = "PostgreSQL 7.4.1 on i386-portbld-freebsd4.9, compiled by GCC 2.95.4" All this data is retrieved as soon as it hits my handler: -------------------------------------------- Regular function: -------------------------------------------- CREATE FUNCTION count() RETURNS numeric LANGUAGE plpy AS ' i = 0L while True: i += 1 yield i '; backend> SELECT count(), * FROM someTable; Breakpoint 1, pl_handler (fcinfo=0xbfbff154) at src/pl.c:468 (gdb) print *fcinfo $2 = { flinfo = 0x841d234, context = 0x0, resultinfo = 0x0, <---------------------- :( isnull = 0 '\000', nargs = 0, arg = {0 <repeats 32 times>}, argnull = '\000' <repeats 31 times> } (gdb) print *fcinfo->flinfo $4 = { fn_addr = 0x285dc118 <pl_handler>, fn_oid = 554021, fn_nargs = 0, fn_strict = 0 '\000', fn_retset = 0 '\000', fn_extra = 0x0, fn_mcxt = 0x82ab858, fn_expr = 0x8422838 } (gdb) bt #0 pl_handler (fcinfo=0xbfbff154) at src/pl.c:468 #1 0x80f116e in ExecMakeFunctionResult () <--------------- #2 0x80f177a in ExecMakeTableFunctionResult () #3 0x80f2911 in ExecEvalExpr () #4 0x80f34d4 in ExecCleanTargetListLength () #5 0x80f36d2 in ExecProject () #6 0x80f37ac in ExecScan () #7 0x80fa432 in ExecSeqScan () #8 0x80efd11 in ExecProcNode () #9 0x80eea48 in ExecEndPlan () #10 0x80edff4 in ExecutorRun () #11 0x8153d56 in PortalRun () #12 0x8153c56 in PortalRun () #13 0x8150d87 in pg_plan_queries () #14 0x815310f in PostgresMain () #15 0x8107096 in main () #16 0x806d772 in _start () I also tried a simpler SELECT count();, it gave a NULL resultinfo as well. backend> SELECT * FROM count(); -- gives a not null resultinfo, and puts me: #0 pl_handler (fcinfo=0xbfbff1f4) at src/pl.c:468 #1 0x80f13a3 in ExecMakeTableFunctionResult () #2 0x80f9d47 in ExecReScanNestLoop () #3 0x80f3758 in ExecScan () #4 0x80f9e0a in ExecFunctionScan () #5 0x80efd51 in ExecProcNode () #6 0x80eea48 in ExecEndPlan () #7 0x80edff4 in ExecutorRun () #8 0x8153d56 in PortalRun () #9 0x8153c56 in PortalRun () #10 0x8150d87 in pg_plan_queries () #11 0x815310f in PostgresMain () #12 0x8107096 in main () #13 0x806d772 in _start () -------------------------------------------------- Composite type returning, on the other hand: -------------------------------------------------- CREATE FUNCTION Composite() RETURNS someTable LANGUAGE plpy AS 'return [0L]'; backend> SELECT * FROM Composite(); Breakpoint 1, pl_handler (fcinfo=0xbfbff1f4) at src/pl.c:468 (gdb) print *fcinfo $5 = { flinfo = 0x841d31c, context = 0x0, resultinfo = 0xbfbff1d4, isnull = 0 '\000', nargs = 0, arg = {0 <repeats 32 times>}, argnull = '\000' <repeats 31 times> } (gdb) print *fcinfo->flinfo $6 = { fn_addr = 0x285dc118 <pl_handler>, fn_oid = 554025, fn_nargs = 0, fn_strict = 0 '\000', fn_retset = 0 '\000', fn_extra = 0x0, fn_mcxt = 0x82ab858, fn_expr = 0x8313a60 } (gdb) print *((ReturnSetInfo *)fcinfo->resultinfo) $8 = { type = T_ReturnSetInfo, econtext = 0x841d1b0, expectedDesc = 0x841d258, allowedModes = 3, returnMode = SFRM_ValuePerCall, isDone = ExprSingleResult, setResult = 0x0, setDesc = 0x0 } (gdb) bt #0 pl_handler (fcinfo=0xbfbff1f4) at src/pl.c:468 #1 0x80f13a3 in ExecMakeTableFunctionResult () #2 0x80f9d47 in ExecReScanNestLoop () #3 0x80f3758 in ExecScan () #4 0x80f9e0a in ExecFunctionScan () #5 0x80efd51 in ExecProcNode () #6 0x80eea48 in ExecEndPlan () #7 0x80edff4 in ExecutorRun () #8 0x8153d56 in PortalRun () #9 0x8153c56 in PortalRun () #10 0x8150d87 in pg_plan_queries () #11 0x815310f in PostgresMain () #12 0x8107096 in main () #13 0x806d772 in _start () NOTE: If I SELECT Composite(); resultinfo is NULL.. -------------------------------------------------- And finally, an actual SRF: -------------------------------------------------- CREATE FUNCTION SRF() RETURNS SETOF someTable LANGUAGE plpy AS 'return [[0L]]'; backend> SELECT * FROM SRF(); Breakpoint 1, pl_handler (fcinfo=0xbfbff1f4) at src/pl.c:468 (gdb) print *fcinfo $11 = { flinfo = 0x842631c, context = 0x0, resultinfo = 0xbfbff1d4, isnull = 0 '\000', nargs = 0, arg = {0 <repeats 32 times>}, argnull = '\000' <repeats 31 times> } (gdb) print *fcinfo->flinfo $12 = { fn_addr = 0x285dc118 <pl_handler>, fn_oid = 554026, fn_nargs = 0, fn_strict = 0 '\000', fn_retset = 1 '\001', fn_extra = 0x0, fn_mcxt = 0x82ab858, fn_expr = 0x8313a60 } (gdb) print *((ReturnSetInfo *)fcinfo->resultinfo) $13 = { type = T_ReturnSetInfo, econtext = 0x84261b0, expectedDesc = 0x8426258, allowedModes = 3, returnMode = SFRM_ValuePerCall, isDone = ExprSingleResult, setResult = 0x0, setDesc = 0x0 } (gdb) bt #0 pl_handler (fcinfo=0xbfbff1f4) at src/pl.c:468 #1 0x80f13a3 in ExecMakeTableFunctionResult () #2 0x80f9d47 in ExecReScanNestLoop () #3 0x80f3758 in ExecScan () #4 0x80f9e0a in ExecFunctionScan () #5 0x80efd51 in ExecProcNode () #6 0x80eea48 in ExecEndPlan () #7 0x80edff4 in ExecutorRun () #8 0x8153d56 in PortalRun () #9 0x8153c56 in PortalRun () #10 0x8150d87 in pg_plan_queries () #11 0x815310f in PostgresMain () #12 0x8107096 in main () #13 0x806d772 in _start () Probably more info than you need/want, but gdb is fun, so here's lots! 8) Regards, James William Pye
Index: execQual.c =================================================================== RCS file: /projects/cvsroot/pgsql-server/src/backend/executor/execQual.c,v retrieving revision 1.159 diff -u -r1.159 execQual.c --- execQual.c 10 May 2004 22:44:43 -0000 1.159 +++ execQual.c 20 May 2004 11:28:20 -0000 @@ -894,21 +894,20 @@ } /* - * If function returns set, prepare a resultinfo node for - * communication + * Prepare a resultinfo node for communication. We always do this + * even if not expecting a set result, so that we can pass + * expectedDesc. In the generic-expression case, the expression + * doesn't actually get to see the resultinfo, but set it up anyway + * because we use some of the fields as our own state variables. */ - if (fcache->func.fn_retset) - { - fcinfo.resultinfo = (Node *) &rsinfo; - rsinfo.type = T_ReturnSetInfo; - rsinfo.econtext = econtext; - rsinfo.expectedDesc = NULL; - rsinfo.allowedModes = (int) SFRM_ValuePerCall; - rsinfo.returnMode = SFRM_ValuePerCall; - /* isDone is filled below */ - rsinfo.setResult = NULL; - rsinfo.setDesc = NULL; - } + fcinfo.resultinfo = (Node *) &rsinfo; + rsinfo.type = T_ReturnSetInfo; + rsinfo.econtext = econtext; + rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize); + rsinfo.returnMode = SFRM_ValuePerCall; + /* isDone is filled below */ + rsinfo.setResult = NULL; + rsinfo.setDesc = NULL; /* * now return the value gotten by calling the function manager,
pgpBPHh9Vf1HP.pgp
Description: PGP signature