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
