/* *Ifitreturnsaset,calltheiteratortogetthenextreturnitem. *WestayintheSPIcontextwhiledoingthis,becausePyIter_Next() *callsbackintoPythoncodewhichmightcontainSPIcalls.
*/ if (is_setof)
{ if (srfstate->iter == NULL)
{ /* first time -- do checks and setup */
ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo;
if (!rsi || !IsA(rsi, ReturnSetInfo) ||
(rsi->allowedModes & SFRM_ValuePerCall) == 0)
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("unsupported set function return mode"),
errdetail("PL/Python set-returning functions only support returning one value per call.")));
}
rsi->returnMode = SFRM_ValuePerCall;
/* Make iterator out of returned object */
srfstate->iter = PyObject_GetIter(plrv);
Py_DECREF(plrv);
plrv = NULL;
if (srfstate->iter == NULL)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("returned object cannot be iterated"),
errdetail("PL/Python set-returning functions must return an iterable object.")));
}
/* Fetch next from iterator */
plrv = PyIter_Next(srfstate->iter); if (plrv == NULL)
{ /* Iterator is exhausted or error happened */ bool has_error = (PyErr_Occurred() != NULL);
Py_DECREF(srfstate->iter);
srfstate->iter = NULL;
if (has_error)
PLy_elog(ERROR, "error fetching next item from iterator");
/* Pass a null through the data-returning steps below */
Py_INCREF(Py_None);
plrv = Py_None;
} else
{ /* *Thiswon'tbelastcall,sosaveargumentvalues.Wedo *thisagaineachtimeincasetheiteratorischangingthose *values.
*/
srfstate->savedargs = PLy_function_save_args(proc);
}
}
/* *Foraprocedureorfunctiondeclaredtoreturnvoid,thePython *returnvaluemustbeNone.Forvoid-returningfunctions,wealso *treataNonereturnvalueasaspecial"voiddatum"ratherthan *NULL(asisthecasefornon-void-returningfunctions).
*/ if (proc->result.typoid == VOIDOID)
{ if (plrv != Py_None)
{ if (proc->is_procedure)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("PL/Python procedure did not return None"))); else
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("PL/Python function with return type \"void\" did not return None")));
}
if (get_call_result_type(fcinfo, NULL, &desc) != TYPEFUNC_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning record called in context " "that cannot accept type record")));
PLy_output_setup_record(&proc->result, desc, proc);
}
rv = PLy_output_convert(&proc->result, plrv,
&fcinfo->isnull);
}
}
PG_CATCH();
{ /* Pop old arguments from the stack if they were pushed above */
PLy_global_args_pop(proc);
Py_XDECREF(plargs);
Py_XDECREF(plrv);
/* *IftherewasanerrorwithinaSRF,theiteratormightnothave *beenexhaustedyet.Clearitsothenextinvocationofthe *functionwillstarttheiterationagain.(Thiscodeisprobably *unnecessarynow;plpython_srf_cleanup_callbackshouldtakecareof *cleanup.Butitdoesn'thurtanythingtodoithere.)
*/ if (srfstate)
{
Py_XDECREF(srfstate->iter);
srfstate->iter = NULL; /* And drop any saved args; we won't need them */ if (srfstate->savedargs)
PLy_function_drop_args(srfstate->savedargs);
srfstate->savedargs = NULL;
}
PG_RE_THROW();
}
PG_END_TRY();
error_context_stack = plerrcontext.previous;
/* Pop old arguments from the stack if they were pushed above */
PLy_global_args_pop(proc);
Py_XDECREF(plargs);
Py_DECREF(plrv);
if (srfstate)
{ /* We're in a SRF, exit appropriately */ if (srfstate->iter == NULL)
{ /* Iterator exhausted, so we're done */
SRF_RETURN_DONE(funcctx);
} elseif (fcinfo->isnull)
SRF_RETURN_NEXT_NULL(funcctx); else
SRF_RETURN_NEXT(funcctx, rv);
}
/* Plain function, just return the Datum value (possibly null) */ return rv;
}
/* saved args are always allocated in procedure's context */
result = (PLySavedArgs *)
MemoryContextAllocZero(proc->mcxt,
offsetof(PLySavedArgs, namedargs) +
proc->nargs * sizeof(PyObject *));
result->nargs = proc->nargs;
/* Fetch the "args" list */
result->args = PyDict_GetItemString(proc->globals, "args");
Py_XINCREF(result->args);
/* If it's a trigger, also save "TD" */ if (proc->is_trigger)
{
result->td = PyDict_GetItemString(proc->globals, "TD");
Py_XINCREF(result->td);
}
/* Fetch all the named arguments */ if (proc->argnames)
{ int i;
for (i = 0; i < result->nargs; i++)
{ if (proc->argnames[i])
{
result->namedargs[i] = PyDict_GetItemString(proc->globals,
proc->argnames[i]);
Py_XINCREF(result->namedargs[i]);
}
}
}
return result;
}
/* *Restoreprocedure'sargumentsfromaPLySavedArgsstruct, *thenfreethestruct.
*/ staticvoid
PLy_function_restore_args(PLyProcedure *proc, PLySavedArgs *savedargs)
{ /* Restore named arguments into their slots in the globals dict */ if (proc->argnames)
{ int i;
for (i = 0; i < savedargs->nargs; i++)
{ if (proc->argnames[i] && savedargs->namedargs[i])
{
PyDict_SetItemString(proc->globals, proc->argnames[i],
savedargs->namedargs[i]);
Py_DECREF(savedargs->namedargs[i]);
}
}
}
/* Restore the "args" object, too */ if (savedargs->args)
{
PyDict_SetItemString(proc->globals, "args", savedargs->args);
Py_DECREF(savedargs->args);
}
/* Restore the "TD" object, too */ if (savedargs->td)
{
PyDict_SetItemString(proc->globals, "TD", savedargs->td);
Py_DECREF(savedargs->td);
}
/* And free the PLySavedArgs struct */
pfree(savedargs);
}
/* *FreeaPLySavedArgsstructwithoutrestoringthevalues.
*/ staticvoid
PLy_function_drop_args(PLySavedArgs *savedargs)
{ int i;
/* Drop references for named args */ for (i = 0; i < savedargs->nargs; i++)
{
Py_XDECREF(savedargs->namedargs[i]);
}
/* Drop refs to the "args" and "TD" objects, too */
Py_XDECREF(savedargs->args);
Py_XDECREF(savedargs->td);
/* And free the PLySavedArgs struct */
pfree(savedargs);
}
/* *Saveawayanyexistingargumentsforthegivenprocedure,sothatwecan *installnewvaluesforarecursivecall.Thisshouldbeinvokedbefore *doingPLy_function_build_args()orPLy_trigger_build_args(). * *NB:callersmustensurethatPLy_global_args_popgetsinvokedonce,and *onlyonce,persuccessfulcompletionofPLy_global_args_push.Otherwise *we'llendupout-of-syncbetweentheactualcallstackandthecontents *ofproc->argstack.
*/ staticvoid
PLy_global_args_push(PLyProcedure *proc)
{ /* We only need to push if we are already inside some active call */ if (proc->calldepth > 0)
{
PLySavedArgs *node;
/* Build a struct containing current argument values */
node = PLy_function_save_args(proc);
/* *Popoldargumentswhenexitingarecursivecall. * *Note:theideahereistoadjusttheproc'scallstackstatebeforedoing *anythingthatcouldpossiblyfail.Ineventofanyerror,wewantthe *callstacktolooklikewe'vedonethepop.Leakingabitofmemoryis *tolerable.
*/ staticvoid
PLy_global_args_pop(PLyProcedure *proc)
{
Assert(proc->calldepth > 0); /* We only need to pop if we were already inside some active call */ if (proc->calldepth > 1)
{
PLySavedArgs *ptr = proc->argstack;
/* Pop the callstack */
Assert(ptr != NULL);
proc->argstack = ptr->next;
proc->calldepth--;
/* Release refcount on the iter, if we still have one */
Py_XDECREF(srfstate->iter);
srfstate->iter = NULL; /* And drop any saved args; we won't need them */ if (srfstate->savedargs)
PLy_function_drop_args(srfstate->savedargs);
srfstate->savedargs = NULL;
}
if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("TD[\"new\"] deleted, cannot modify row")));
Py_INCREF(plntup); if (!PyDict_Check(plntup))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("TD[\"new\"] is not a dictionary")));
for (i = 0; i < nkeys; i++)
{
PyObject *platt; char *plattstr; int attn;
PLyObToDatum *att;
platt = PyList_GetItem(plkeys, i); if (PyUnicode_Check(platt))
plattstr = PLyUnicode_AsString(platt); else
{
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("TD[\"new\"] dictionary key at ordinal position %d is not a string", i)));
plattstr = NULL; /* keep compiler quiet */
}
attn = SPI_fnumber(tupdesc, plattstr); if (attn == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row",
plattstr))); if (attn <= 0)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot set system attribute \"%s\"",
plattstr))); if (TupleDescAttr(tupdesc, attn - 1)->attgenerated)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_TRIGGER_PROTOCOL_VIOLATED),
errmsg("cannot set generated column \"%s\"",
plattstr)));
plval = PyDict_GetItem(plntup, platt); if (plval == NULL)
elog(FATAL, "Python interpreter is probably corrupted");
Py_INCREF(plval);
/* We assume proc->result is set up to convert tuples properly */
att = &proc->result.u.tuple.atts[attn - 1];
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.