Sorry, I have to revert this patch because it is causing crashes in the
plpython regression tests. Would you please run those tests, fix the
bug, and resubmit. Thanks.
---------------------------------------------------------------------------
pgman wrote:
>
> Patch applied. Thanks.
>
> ---------------------------------------------------------------------------
>
>
> Sven Suursoho wrote:
> > Hi,
> >
> >
> > Mon, 17 Apr 2006 19:20:38 +0300, Bruce Momjian <[email protected]>:
> >
> > >> > Hannu Krosing wrote:
> > >> >
> > >> >> > 1) named parameters additionally to args[]
> > >> >> > 2) return composite-types from plpython as dictionary
> > >> >> > 3) return result-set from plpython as list, iterator or generator
> > >> >> >
> > >> >> > Test script attached (patch-test.sql) but not integrated to
> > >> >> > plpython test-suite.
> > >> >>
> > >> >> If you wonder why you can't apply the patch, it is against postgres
> > >> >> 8.0.7.
> > >> >
> > >> > Can I apply it by merging it in manually, or are you working on a
> > >> > version against CVS HEAD?
> >
> > Attached patch for CVS HEAD.
> > Testscripts remained same (obviously :) but still not integrated.
> >
> >
> > --
> > Sven Suursoho
>
> [ Attachment, skipping... ]
>
> >
> > ---------------------------(end of broadcast)---------------------------
> > TIP 4: Have you searched our list archives?
> >
> > http://archives.postgresql.org
>
> --
> Bruce Momjian http://candle.pha.pa.us
> EnterpriseDB http://www.enterprisedb.com
>
> + If your life is a hard drive, Christ can be your backup. +
--
Bruce Momjian http://candle.pha.pa.us
EnterpriseDB http://www.enterprisedb.com
+ If your life is a hard drive, Christ can be your backup. +
Index: plpython.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpython/plpython.c,v
retrieving revision 1.77
retrieving revision 1.78
diff -c -c -r1.77 -r1.78
*** plpython.c 4 Apr 2006 19:35:37 -0000 1.77
--- plpython.c 27 Apr 2006 01:05:05 -0000 1.78
***************
*** 19,24 ****
--- 19,25 ----
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
+ #include "funcapi.h"
#include "fmgr.h"
#include "nodes/makefuncs.h"
#include "parser/parse_type.h"
***************
*** 108,113 ****
--- 109,119 ----
bool fn_readonly;
PLyTypeInfo result; /* also used to store info for
trigger tuple
* type */
+ bool is_setof; /* true, if procedure returns result
set */
+ PyObject *setof; /* contents of result set. */
+ int setof_count; /* numbef of items to return in result
set */
+ int setof_current; /* current item in result set */
+ char **argnames; /* Argument names */
PLyTypeInfo args[FUNC_MAX_ARGS];
int nargs;
PyObject *code; /* compiled procedure code */
***************
*** 184,189 ****
--- 190,196 ----
static HeapTuple PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *);
static PyObject *PLy_function_build_args(FunctionCallInfo fcinfo,
PLyProcedure *);
+ static void PLy_function_delete_args(PLyProcedure *);
static PyObject *PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure
*,
HeapTuple *);
static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
***************
*** 218,223 ****
--- 225,231 ----
static PyObject *PLyInt_FromString(const char *);
static PyObject *PLyLong_FromString(const char *);
static PyObject *PLyString_FromString(const char *);
+ static HeapTuple PLyDict_ToTuple(PLyTypeInfo *, PyObject *);
/* global data */
***************
*** 726,736 ****
PG_TRY();
{
! plargs = PLy_function_build_args(fcinfo, proc);
! plrv = PLy_procedure_call(proc, "args", plargs);
!
! Assert(plrv != NULL);
! Assert(!PLy_error_in_progress);
/*
* Disconnect from SPI manager and then create the return
values datum
--- 734,750 ----
PG_TRY();
{
! if (!proc->is_setof || proc->setof_count == -1)
! {
! /* python function not called yet, do it */
! plargs = PLy_function_build_args(fcinfo, proc);
! plrv = PLy_procedure_call(proc, "args", plargs);
! if (!proc->is_setof)
! /* SETOF function parameters are deleted when
called last row is returned */
! PLy_function_delete_args(proc);
! Assert(plrv != NULL);
! Assert(!PLy_error_in_progress);
! }
/*
* Disconnect from SPI manager and then create the return
values datum
***************
*** 741,746 ****
--- 755,830 ----
if (SPI_finish() != SPI_OK_FINISH)
elog(ERROR, "SPI_finish failed");
+ if (proc->is_setof)
+ {
+ bool is_done = false;
+ ReturnSetInfo *rsi = (ReturnSetInfo
*)fcinfo->resultinfo;
+
+ if (proc->setof_current == -1)
+ {
+ /* first time -- do checks and setup */
+ if (!rsi || !IsA(rsi, ReturnSetInfo) ||
+ (rsi->allowedModes &
SFRM_ValuePerCall) == 0)
+ {
+ ereport(ERROR,
+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("only value per
call is allowed")));
+ }
+ rsi->returnMode = SFRM_ValuePerCall;
+
+ /* fetch information about returned object */
+ proc->setof = plrv;
+ plrv = NULL;
+ if (PyList_Check(proc->setof))
+ /* SETOF as list */
+ proc->setof_count =
PyList_GET_SIZE(proc->setof);
+ else if (PyIter_Check(proc->setof))
+ /* SETOF as iterator, unknown number of
items */
+ proc->setof_current = proc->setof_count
= 0;
+ else
+ {
+ ereport(ERROR,
+
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+ errmsg("SETOF must be
returned as list or iterator")));
+ }
+ }
+
+ Assert(proc->setof != NULL);
+
+ /* Fetch next of SETOF */
+ if (PyList_Check(proc->setof))
+ {
+ is_done = ++proc->setof_current ==
proc->setof_count;
+ if (!is_done)
+ plrv = PyList_GET_ITEM(proc->setof,
proc->setof_current);
+ }
+ else if (PyIter_Check(proc->setof))
+ {
+ plrv = PyIter_Next(proc->setof);
+ is_done = plrv == NULL;
+ }
+
+ if (!is_done)
+ {
+ rsi->isDone = ExprMultipleResult;
+ }
+ else
+ {
+ rsi->isDone = ExprEndResult;
+ proc->setof_count = proc->setof_current = -1;
+ Py_DECREF(proc->setof);
+ proc->setof = NULL;
+
+ Py_XDECREF(plargs);
+ Py_XDECREF(plrv);
+ Py_XDECREF(plrv_so);
+
+ PLy_function_delete_args(proc);
+ fcinfo->isnull = true;
+ return (Datum)NULL;
+ }
+ }
+
/*
* If the function is declared to return void, the Python
* return value must be None. For void-returning functions, we
***************
*** 767,772 ****
--- 851,876 ----
proc->result.out.d.typioparam,
-1);
}
+ else if (proc->result.is_rowtype >= 1)
+ {
+ HeapTuple tuple;
+
+ /* returning composite type */
+ if (!PyDict_Check(plrv))
+ elog(ERROR, "tuple must be returned as
dictionary");
+
+ tuple = PLyDict_ToTuple(&proc->result, plrv);
+ if (tuple != NULL)
+ {
+ fcinfo->isnull = false;
+ rv = HeapTupleGetDatum(tuple);
+ }
+ else
+ {
+ fcinfo->isnull = true;
+ rv = (Datum) NULL;
+ }
+ }
else
{
fcinfo->isnull = false;
***************
*** 893,898 ****
--- 997,1003 ----
* FIXME -- error check this
*/
PyList_SetItem(args, i, arg);
+ PyDict_SetItemString(proc->globals, proc->argnames[i],
arg);
arg = NULL;
}
}
***************
*** 909,914 ****
--- 1014,1029 ----
}
+ static void
+ PLy_function_delete_args(PLyProcedure *proc)
+ {
+ int i;
+
+ for (i = 0; i < proc->nargs; i++)
+ PyDict_DelItemString(proc->globals, proc->argnames[i]);
+ }
+
+
/*
* PLyProcedure functions
*/
***************
*** 979,984 ****
--- 1094,1102 ----
bool isnull;
int i,
rv;
+ Datum argnames;
+ Datum *elems;
+ int nelems;
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
***************
*** 1010,1015 ****
--- 1128,1137 ----
proc->nargs = 0;
proc->code = proc->statics = NULL;
proc->globals = proc->me = NULL;
+ proc->is_setof = procStruct->proretset;
+ proc->setof = NULL;
+ proc->setof_count = proc->setof_current = -1;
+ proc->argnames = NULL;
PG_TRY();
{
***************
*** 1046,1054 ****
}
if (rvTypeStruct->typtype == 'c')
! ereport(ERROR,
!
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
! errmsg("plpython functions cannot
return tuples yet")));
else
PLy_output_datum_func(&proc->result, rvTypeTup);
--- 1168,1178 ----
}
if (rvTypeStruct->typtype == 'c')
! {
! /* Tuple: set up later, during first call to
PLy_function_handler */
! proc->result.out.d.typoid =
procStruct->prorettype;
! proc->result.is_rowtype = 2;
! }
else
PLy_output_datum_func(&proc->result, rvTypeTup);
***************
*** 1071,1076 ****
--- 1195,1215 ----
* arguments.
*/
proc->nargs = fcinfo->nargs;
+ proc->argnames = NULL;
+ if (proc->nargs)
+ {
+ argnames = SysCacheGetAttr(PROCOID, procTup,
Anum_pg_proc_proargnames, &isnull);
+ if (!isnull)
+ {
+ deconstruct_array(DatumGetArrayTypeP(argnames),
TEXTOID, -1, false, 'i',
+ &elems, NULL, &nelems);
+ if (nelems != proc->nargs)
+ elog(ERROR,
+ "proargnames must have
the same number of elements "
+ "as the function has
arguments");
+ proc->argnames = (char **)
PLy_malloc(sizeof(char *)*proc->nargs);
+ }
+ }
for (i = 0; i < fcinfo->nargs; i++)
{
HeapTuple argTypeTup;
***************
*** 1099,1106 ****
proc->args[i].is_rowtype = 2; /* still need
to set I/O funcs */
ReleaseSysCache(argTypeTup);
- }
/*
* get the text of the function.
--- 1238,1248 ----
proc->args[i].is_rowtype = 2; /* still need
to set I/O funcs */
ReleaseSysCache(argTypeTup);
+ /* Fetch argument name */
+ if (proc->argnames)
+ proc->argnames[i] =
PLy_strdup(DatumGetCString(DirectFunctionCall1(textout, elems[i])));
+ }
/*
* get the text of the function.
***************
*** 1236,1241 ****
--- 1378,1384 ----
if (proc->pyname)
PLy_free(proc->pyname);
for (i = 0; i < proc->nargs; i++)
+ {
if (proc->args[i].is_rowtype == 1)
{
if (proc->args[i].in.r.atts)
***************
*** 1243,1248 ****
--- 1386,1396 ----
if (proc->args[i].out.r.atts)
PLy_free(proc->args[i].out.r.atts);
}
+ if (proc->argnames && proc->argnames[i])
+ PLy_free(proc->argnames[i]);
+ }
+ if (proc->argnames)
+ PLy_free(proc->argnames);
}
/* conversion functions. remember output from python is
***************
*** 1501,1506 ****
--- 1649,1726 ----
return dict;
}
+
+ static HeapTuple
+ PLyDict_ToTuple(PLyTypeInfo *info, PyObject *dict)
+ {
+ TupleDesc desc;
+ HeapTuple tuple;
+ Datum *values;
+ char *nulls;
+ int i;
+
+ desc = CreateTupleDescCopy(lookup_rowtype_tupdesc(info->out.d.typoid,
-1));
+
+ /* Set up tuple type, if neccessary */
+ if (info->is_rowtype == 2)
+ {
+ PLy_output_tuple_funcs(info, desc);
+ info->is_rowtype = 1;
+ }
+ Assert(info->is_rowtype == 1);
+
+ /* Build tuple */
+ values = palloc(sizeof(Datum)*desc->natts);
+ nulls = palloc(sizeof(char)*desc->natts);
+ for (i = 0; i < desc->natts; ++i)
+ {
+ char *key;
+ PyObject *value,
+ *so;
+
+ key = NameStr(desc->attrs[i]->attname);
+ value = so = NULL;
+ PG_TRY();
+ {
+ value = PyDict_GetItemString(dict, key);
+ if (value != Py_None && value != NULL)
+ {
+ char *valuestr;
+
+ so = PyObject_Str(value);
+ valuestr = PyString_AsString(so);
+ values[i] =
InputFunctionCall(&info->out.r.atts[i].typfunc
+ , valuestr
+ , info->out.r.atts[i].typioparam
+ , -1);
+ Py_DECREF(so);
+ value = so = NULL;
+ nulls[i] = ' ';
+ }
+ else
+ {
+ value = NULL;
+ values[i] = (Datum) NULL;
+ nulls[i] = 'n';
+ }
+ }
+ PG_CATCH();
+ {
+ Py_XDECREF(value);
+ Py_XDECREF(so);
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+ }
+
+ tuple = heap_formtuple(desc, values, nulls);
+ FreeTupleDesc(desc);
+ pfree(values);
+ pfree(nulls);
+
+ return tuple;
+ }
+
/* initialization, some python variables function declared here */
/* interface to postgresql elog */
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?
http://www.postgresql.org/docs/faq