Skip to content

Commit 1a84275

Browse files
committed
plpython improvements:
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 Hannu Krosing Sven Suursoho
1 parent f66a3ca commit 1a84275

File tree

1 file changed

+231
-10
lines changed

1 file changed

+231
-10
lines changed

src/pl/plpython/plpython.c

Lines changed: 231 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**********************************************************************
22
* plpython.c - python as a procedural language for PostgreSQL
33
*
4-
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.77 2006/04/04 19:35:37 tgl Exp $
4+
* $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.78 2006/04/27 01:05:05 momjian Exp $
55
*
66
*********************************************************************
77
*/
@@ -19,6 +19,7 @@
1919
#include "catalog/pg_type.h"
2020
#include "commands/trigger.h"
2121
#include "executor/spi.h"
22+
#include "funcapi.h"
2223
#include "fmgr.h"
2324
#include "nodes/makefuncs.h"
2425
#include "parser/parse_type.h"
@@ -108,6 +109,11 @@ typedef struct PLyProcedure
108109
bool fn_readonly;
109110
PLyTypeInfo result; /* also used to store info for trigger tuple
110111
* type */
112+
bool is_setof; /* true, if procedure returns result set */
113+
PyObject *setof; /* contents of result set. */
114+
int setof_count; /* numbef of items to return in result set */
115+
int setof_current; /* current item in result set */
116+
char **argnames; /* Argument names */
111117
PLyTypeInfo args[FUNC_MAX_ARGS];
112118
int nargs;
113119
PyObject *code; /* compiled procedure code */
@@ -184,6 +190,7 @@ static Datum PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure *);
184190
static HeapTuple PLy_trigger_handler(FunctionCallInfo fcinfo, PLyProcedure *);
185191

186192
static PyObject *PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure *);
193+
static void PLy_function_delete_args(PLyProcedure *);
187194
static PyObject *PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *,
188195
HeapTuple *);
189196
static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
@@ -218,6 +225,7 @@ static PyObject *PLyFloat_FromString(const char *);
218225
static PyObject *PLyInt_FromString(const char *);
219226
static PyObject *PLyLong_FromString(const char *);
220227
static PyObject *PLyString_FromString(const char *);
228+
static HeapTuple PLyDict_ToTuple(PLyTypeInfo *, PyObject *);
221229

222230

223231
/* global data */
@@ -726,11 +734,17 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
726734

727735
PG_TRY();
728736
{
729-
plargs = PLy_function_build_args(fcinfo, proc);
730-
plrv = PLy_procedure_call(proc, "args", plargs);
731-
732-
Assert(plrv != NULL);
733-
Assert(!PLy_error_in_progress);
737+
if (!proc->is_setof || proc->setof_count == -1)
738+
{
739+
/* python function not called yet, do it */
740+
plargs = PLy_function_build_args(fcinfo, proc);
741+
plrv = PLy_procedure_call(proc, "args", plargs);
742+
if (!proc->is_setof)
743+
/* SETOF function parameters are deleted when called last row is returned */
744+
PLy_function_delete_args(proc);
745+
Assert(plrv != NULL);
746+
Assert(!PLy_error_in_progress);
747+
}
734748

735749
/*
736750
* Disconnect from SPI manager and then create the return values datum
@@ -741,6 +755,76 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
741755
if (SPI_finish() != SPI_OK_FINISH)
742756
elog(ERROR, "SPI_finish failed");
743757

758+
if (proc->is_setof)
759+
{
760+
bool is_done = false;
761+
ReturnSetInfo *rsi = (ReturnSetInfo *)fcinfo->resultinfo;
762+
763+
if (proc->setof_current == -1)
764+
{
765+
/* first time -- do checks and setup */
766+
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
767+
(rsi->allowedModes & SFRM_ValuePerCall) == 0)
768+
{
769+
ereport(ERROR,
770+
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
771+
errmsg("only value per call is allowed")));
772+
}
773+
rsi->returnMode = SFRM_ValuePerCall;
774+
775+
/* fetch information about returned object */
776+
proc->setof = plrv;
777+
plrv = NULL;
778+
if (PyList_Check(proc->setof))
779+
/* SETOF as list */
780+
proc->setof_count = PyList_GET_SIZE(proc->setof);
781+
else if (PyIter_Check(proc->setof))
782+
/* SETOF as iterator, unknown number of items */
783+
proc->setof_current = proc->setof_count = 0;
784+
else
785+
{
786+
ereport(ERROR,
787+
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
788+
errmsg("SETOF must be returned as list or iterator")));
789+
}
790+
}
791+
792+
Assert(proc->setof != NULL);
793+
794+
/* Fetch next of SETOF */
795+
if (PyList_Check(proc->setof))
796+
{
797+
is_done = ++proc->setof_current == proc->setof_count;
798+
if (!is_done)
799+
plrv = PyList_GET_ITEM(proc->setof, proc->setof_current);
800+
}
801+
else if (PyIter_Check(proc->setof))
802+
{
803+
plrv = PyIter_Next(proc->setof);
804+
is_done = plrv == NULL;
805+
}
806+
807+
if (!is_done)
808+
{
809+
rsi->isDone = ExprMultipleResult;
810+
}
811+
else
812+
{
813+
rsi->isDone = ExprEndResult;
814+
proc->setof_count = proc->setof_current = -1;
815+
Py_DECREF(proc->setof);
816+
proc->setof = NULL;
817+
818+
Py_XDECREF(plargs);
819+
Py_XDECREF(plrv);
820+
Py_XDECREF(plrv_so);
821+
822+
PLy_function_delete_args(proc);
823+
fcinfo->isnull = true;
824+
return (Datum)NULL;
825+
}
826+
}
827+
744828
/*
745829
* If the function is declared to return void, the Python
746830
* return value must be None. For void-returning functions, we
@@ -767,6 +851,26 @@ PLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc)
767851
proc->result.out.d.typioparam,
768852
-1);
769853
}
854+
else if (proc->result.is_rowtype >= 1)
855+
{
856+
HeapTuple tuple;
857+
858+
/* returning composite type */
859+
if (!PyDict_Check(plrv))
860+
elog(ERROR, "tuple must be returned as dictionary");
861+
862+
tuple = PLyDict_ToTuple(&proc->result, plrv);
863+
if (tuple != NULL)
864+
{
865+
fcinfo->isnull = false;
866+
rv = HeapTupleGetDatum(tuple);
867+
}
868+
else
869+
{
870+
fcinfo->isnull = true;
871+
rv = (Datum) NULL;
872+
}
873+
}
770874
else
771875
{
772876
fcinfo->isnull = false;
@@ -893,6 +997,7 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
893997
* FIXME -- error check this
894998
*/
895999
PyList_SetItem(args, i, arg);
1000+
PyDict_SetItemString(proc->globals, proc->argnames[i], arg);
8961001
arg = NULL;
8971002
}
8981003
}
@@ -909,6 +1014,16 @@ PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc)
9091014
}
9101015

9111016

1017+
static void
1018+
PLy_function_delete_args(PLyProcedure *proc)
1019+
{
1020+
int i;
1021+
1022+
for (i = 0; i < proc->nargs; i++)
1023+
PyDict_DelItemString(proc->globals, proc->argnames[i]);
1024+
}
1025+
1026+
9121027
/*
9131028
* PLyProcedure functions
9141029
*/
@@ -979,6 +1094,9 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
9791094
bool isnull;
9801095
int i,
9811096
rv;
1097+
Datum argnames;
1098+
Datum *elems;
1099+
int nelems;
9821100

9831101
procStruct = (Form_pg_proc) GETSTRUCT(procTup);
9841102

@@ -1010,6 +1128,10 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
10101128
proc->nargs = 0;
10111129
proc->code = proc->statics = NULL;
10121130
proc->globals = proc->me = NULL;
1131+
proc->is_setof = procStruct->proretset;
1132+
proc->setof = NULL;
1133+
proc->setof_count = proc->setof_current = -1;
1134+
proc->argnames = NULL;
10131135

10141136
PG_TRY();
10151137
{
@@ -1046,9 +1168,11 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
10461168
}
10471169

10481170
if (rvTypeStruct->typtype == 'c')
1049-
ereport(ERROR,
1050-
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1051-
errmsg("plpython functions cannot return tuples yet")));
1171+
{
1172+
/* Tuple: set up later, during first call to PLy_function_handler */
1173+
proc->result.out.d.typoid = procStruct->prorettype;
1174+
proc->result.is_rowtype = 2;
1175+
}
10521176
else
10531177
PLy_output_datum_func(&proc->result, rvTypeTup);
10541178

@@ -1071,6 +1195,21 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
10711195
* arguments.
10721196
*/
10731197
proc->nargs = fcinfo->nargs;
1198+
proc->argnames = NULL;
1199+
if (proc->nargs)
1200+
{
1201+
argnames = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_proargnames, &isnull);
1202+
if (!isnull)
1203+
{
1204+
deconstruct_array(DatumGetArrayTypeP(argnames), TEXTOID, -1, false, 'i',
1205+
&elems, NULL, &nelems);
1206+
if (nelems != proc->nargs)
1207+
elog(ERROR,
1208+
"proargnames must have the same number of elements "
1209+
"as the function has arguments");
1210+
proc->argnames = (char **) PLy_malloc(sizeof(char *)*proc->nargs);
1211+
}
1212+
}
10741213
for (i = 0; i < fcinfo->nargs; i++)
10751214
{
10761215
HeapTuple argTypeTup;
@@ -1099,8 +1238,11 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
10991238
proc->args[i].is_rowtype = 2; /* still need to set I/O funcs */
11001239

11011240
ReleaseSysCache(argTypeTup);
1102-
}
11031241

1242+
/* Fetch argument name */
1243+
if (proc->argnames)
1244+
proc->argnames[i] = PLy_strdup(DatumGetCString(DirectFunctionCall1(textout, elems[i])));
1245+
}
11041246

11051247
/*
11061248
* get the text of the function.
@@ -1236,13 +1378,19 @@ PLy_procedure_delete(PLyProcedure * proc)
12361378
if (proc->pyname)
12371379
PLy_free(proc->pyname);
12381380
for (i = 0; i < proc->nargs; i++)
1381+
{
12391382
if (proc->args[i].is_rowtype == 1)
12401383
{
12411384
if (proc->args[i].in.r.atts)
12421385
PLy_free(proc->args[i].in.r.atts);
12431386
if (proc->args[i].out.r.atts)
12441387
PLy_free(proc->args[i].out.r.atts);
12451388
}
1389+
if (proc->argnames && proc->argnames[i])
1390+
PLy_free(proc->argnames[i]);
1391+
}
1392+
if (proc->argnames)
1393+
PLy_free(proc->argnames);
12461394
}
12471395

12481396
/* conversion functions. remember output from python is
@@ -1501,6 +1649,78 @@ PLyDict_FromTuple(PLyTypeInfo * info, HeapTuple tuple, TupleDesc desc)
15011649
return dict;
15021650
}
15031651

1652+
1653+
static HeapTuple
1654+
PLyDict_ToTuple(PLyTypeInfo *info, PyObject *dict)
1655+
{
1656+
TupleDesc desc;
1657+
HeapTuple tuple;
1658+
Datum *values;
1659+
char *nulls;
1660+
int i;
1661+
1662+
desc = CreateTupleDescCopy(lookup_rowtype_tupdesc(info->out.d.typoid, -1));
1663+
1664+
/* Set up tuple type, if neccessary */
1665+
if (info->is_rowtype == 2)
1666+
{
1667+
PLy_output_tuple_funcs(info, desc);
1668+
info->is_rowtype = 1;
1669+
}
1670+
Assert(info->is_rowtype == 1);
1671+
1672+
/* Build tuple */
1673+
values = palloc(sizeof(Datum)*desc->natts);
1674+
nulls = palloc(sizeof(char)*desc->natts);
1675+
for (i = 0; i < desc->natts; ++i)
1676+
{
1677+
char *key;
1678+
PyObject *value,
1679+
*so;
1680+
1681+
key = NameStr(desc->attrs[i]->attname);
1682+
value = so = NULL;
1683+
PG_TRY();
1684+
{
1685+
value = PyDict_GetItemString(dict, key);
1686+
if (value != Py_None && value != NULL)
1687+
{
1688+
char *valuestr;
1689+
1690+
so = PyObject_Str(value);
1691+
valuestr = PyString_AsString(so);
1692+
values[i] = InputFunctionCall(&info->out.r.atts[i].typfunc
1693+
, valuestr
1694+
, info->out.r.atts[i].typioparam
1695+
, -1);
1696+
Py_DECREF(so);
1697+
value = so = NULL;
1698+
nulls[i] = ' ';
1699+
}
1700+
else
1701+
{
1702+
value = NULL;
1703+
values[i] = (Datum) NULL;
1704+
nulls[i] = 'n';
1705+
}
1706+
}
1707+
PG_CATCH();
1708+
{
1709+
Py_XDECREF(value);
1710+
Py_XDECREF(so);
1711+
PG_RE_THROW();
1712+
}
1713+
PG_END_TRY();
1714+
}
1715+
1716+
tuple = heap_formtuple(desc, values, nulls);
1717+
FreeTupleDesc(desc);
1718+
pfree(values);
1719+
pfree(nulls);
1720+
1721+
return tuple;
1722+
}
1723+
15041724
/* initialization, some python variables function declared here */
15051725

15061726
/* interface to postgresql elog */
@@ -2644,3 +2864,4 @@ PLy_free(void *ptr)
26442864
{
26452865
free(ptr);
26462866
}
2867+
/* vim: set noexpandtab nosmarttab shiftwidth=8 cinoptions=l1j1: */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy