📄 plpython.c
字号:
if (plval != Py_None && !tupdesc->attrs[atti]->attisdropped) { plstr = PyObject_Str(plval); src = PyString_AsString(plstr); modvalues[i] = FunctionCall3(&proc->result.out.r.atts[atti].typfunc, CStringGetDatum(src), ObjectIdGetDatum(proc->result.out.r.atts[atti].typelem), Int32GetDatum(tupdesc->attrs[atti]->atttypmod)); modnulls[i] = ' '; Py_DECREF(plstr); plstr = NULL; } else { modvalues[i] = (Datum) 0; modnulls[i] = 'n'; } Py_DECREF(plval); plval = NULL; } rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, modattrs, modvalues, modnulls); /* * FIXME -- these leak if not explicitly pfree'd by other elog calls, * no? (No, I think, but might as well leave the pfrees here...) */ pfree(modattrs); pfree(modvalues); pfree(modnulls); if (rtup == NULL) elog(ERROR, "SPI_modifytuple failed -- error %d", SPI_result); Py_DECREF(plntup); Py_DECREF(plkeys); RESTORE_EXC(); return rtup;}PyObject *PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc, HeapTuple *rv){ DECLARE_EXC(); TriggerData *tdata; PyObject *pltname, *pltevent, *pltwhen, *pltlevel, *pltrelid; PyObject *pltargs, *pytnew, *pytold; PyObject *volatile pltdata = NULL; char *stroid; enter(); SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); Py_XDECREF(pltdata); RERAISE_EXC(); } tdata = (TriggerData *) fcinfo->context; pltdata = PyDict_New(); if (!pltdata) PLy_elog(ERROR, "could not build arguments for trigger procedure"); pltname = PyString_FromString(tdata->tg_trigger->tgname); PyDict_SetItemString(pltdata, "name", pltname); Py_DECREF(pltname); stroid = DatumGetCString(DirectFunctionCall1(oidout, ObjectIdGetDatum(tdata->tg_relation->rd_id))); pltrelid = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "relid", pltrelid); Py_DECREF(pltrelid); pfree(stroid); if (TRIGGER_FIRED_BEFORE(tdata->tg_event)) pltwhen = PyString_FromString("BEFORE"); else if (TRIGGER_FIRED_AFTER(tdata->tg_event)) pltwhen = PyString_FromString("AFTER"); else { elog(ERROR, "unrecognized WHEN tg_event: %u", tdata->tg_event); pltwhen = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "when", pltwhen); Py_DECREF(pltwhen); if (TRIGGER_FIRED_FOR_ROW(tdata->tg_event)) { pltlevel = PyString_FromString("ROW"); PyDict_SetItemString(pltdata, "level", pltlevel); Py_DECREF(pltlevel); if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) { pltevent = PyString_FromString("INSERT"); PyDict_SetItemString(pltdata, "old", Py_None); pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "new", pytnew); Py_DECREF(pytnew); *rv = tdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) { pltevent = PyString_FromString("DELETE"); PyDict_SetItemString(pltdata, "new", Py_None); pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "old", pytold); Py_DECREF(pytold); *rv = tdata->tg_trigtuple; } else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) { pltevent = PyString_FromString("UPDATE"); pytnew = PLyDict_FromTuple(&(proc->result), tdata->tg_newtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "new", pytnew); Py_DECREF(pytnew); pytold = PLyDict_FromTuple(&(proc->result), tdata->tg_trigtuple, tdata->tg_relation->rd_att); PyDict_SetItemString(pltdata, "old", pytold); Py_DECREF(pytold); *rv = tdata->tg_newtuple; } else { elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event); pltevent = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "event", pltevent); Py_DECREF(pltevent); } else if (TRIGGER_FIRED_FOR_STATEMENT(tdata->tg_event)) { pltlevel = PyString_FromString("STATEMENT"); PyDict_SetItemString(pltdata, "level", pltlevel); Py_DECREF(pltlevel); PyDict_SetItemString(pltdata, "old", Py_None); PyDict_SetItemString(pltdata, "new", Py_None); *rv = (HeapTuple) NULL; if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event)) pltevent = PyString_FromString("INSERT"); else if (TRIGGER_FIRED_BY_DELETE(tdata->tg_event)) pltevent = PyString_FromString("DELETE"); else if (TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) pltevent = PyString_FromString("UPDATE"); else { elog(ERROR, "unrecognized OP tg_event: %u", tdata->tg_event); pltevent = NULL; /* keep compiler quiet */ } PyDict_SetItemString(pltdata, "event", pltevent); Py_DECREF(pltevent); } else elog(ERROR, "unrecognized LEVEL tg_event: %u", tdata->tg_event); if (tdata->tg_trigger->tgnargs) { /* * all strings... */ int i; PyObject *pltarg; pltargs = PyList_New(tdata->tg_trigger->tgnargs); for (i = 0; i < tdata->tg_trigger->tgnargs; i++) { pltarg = PyString_FromString(tdata->tg_trigger->tgargs[i]); /* * stolen, don't Py_DECREF */ PyList_SetItem(pltargs, i, pltarg); } } else { Py_INCREF(Py_None); pltargs = Py_None; } PyDict_SetItemString(pltdata, "args", pltargs); Py_DECREF(pltargs); RESTORE_EXC(); return pltdata;}/* function handler and friends */DatumPLy_function_handler(FunctionCallInfo fcinfo, PLyProcedure * proc){ DECLARE_EXC(); Datum rv; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; PyObject *volatile plrv_so = NULL; char *plrv_sc; enter(); /* * setup to catch elog in while building function arguments, and * DECREF the plargs if the function call fails */ SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); Py_XDECREF(plargs); Py_XDECREF(plrv); Py_XDECREF(plrv_so); RERAISE_EXC(); } plargs = PLy_function_build_args(fcinfo, proc); plrv = PLy_procedure_call(proc, "args", plargs); /* * Disconnect from SPI manager and then create the return values datum * (if the input function does a palloc for it this must not be * allocated in the SPI memory context because SPI_finish would free * it). */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); if (plrv == NULL) { elog(FATAL, "PLy_procedure_call returned NULL");#ifdef NOT_USED if (!PLy_restart_in_progress) PLy_elog(ERROR, "function \"%s\" failed", proc->proname); /* * FIXME is this dead code? i'm pretty sure it is for unnested * calls, but not for nested calls */ RAISE_EXC(1);#endif } /* * convert the python PyObject to a postgresql Datum FIXME returning a * NULL, ie PG_RETURN_NULL() blows the backend to small messy bits... * it this a bug or expected? so just call with the string value of * None for now */ if (plrv == Py_None) { fcinfo->isnull = true; rv = (Datum) NULL; } else { fcinfo->isnull = false; plrv_so = PyObject_Str(plrv); plrv_sc = PyString_AsString(plrv_so); rv = FunctionCall3(&proc->result.out.d.typfunc, PointerGetDatum(plrv_sc), ObjectIdGetDatum(proc->result.out.d.typelem), Int32GetDatum(-1)); } RESTORE_EXC(); Py_XDECREF(plargs); Py_DECREF(plrv); Py_XDECREF(plrv_so); return rv;}PyObject *PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs){ PyObject *rv; PLyProcedure *current; enter(); current = PLy_last_procedure; PLy_last_procedure = proc; PyDict_SetItemString(proc->globals, kargs, vargs); rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals); PLy_last_procedure = current; if ((rv == NULL) || (PyErr_Occurred())) { Py_XDECREF(rv); if (!PLy_restart_in_progress) PLy_elog(ERROR, "function \"%s\" failed", proc->proname); RAISE_EXC(1); } return rv;}PyObject *PLy_function_build_args(FunctionCallInfo fcinfo, PLyProcedure * proc){ DECLARE_EXC(); PyObject *volatile arg = NULL; PyObject *volatile args = NULL; int i; enter(); /* * FIXME -- if the setjmp setup is expensive, add the arg and args * field to the procedure struct and cleanup at the start of the next * call */ SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); Py_XDECREF(arg); Py_XDECREF(args); RERAISE_EXC(); } args = PyList_New(proc->nargs); for (i = 0; i < proc->nargs; i++) { if (proc->args[i].is_rel == 1) { TupleTableSlot *slot = (TupleTableSlot *) fcinfo->arg[i]; arg = PLyDict_FromTuple(&(proc->args[i]), slot->val, slot->ttc_tupleDescriptor); } else { if (!fcinfo->argnull[i]) { char *ct; Datum dt; dt = FunctionCall3(&(proc->args[i].in.d.typfunc), fcinfo->arg[i], ObjectIdGetDatum(proc->args[i].in.d.typelem), Int32GetDatum(-1)); ct = DatumGetCString(dt); arg = (proc->args[i].in.d.func) (ct); pfree(ct); } else arg = NULL; } if (arg == NULL) { Py_INCREF(Py_None); arg = Py_None; } /* * FIXME -- error check this */ PyList_SetItem(args, i, arg); } RESTORE_EXC(); return args;}/* * PLyProcedure functions *//* PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and * returns a new PLyProcedure. fcinfo is the call info, tgreloid is the * relation OID when calling a trigger, or InvalidOid (zero) for ordinary * function calls. */static PLyProcedure *PLy_procedure_get(FunctionCallInfo fcinfo, Oid tgreloid){ Oid fn_oid; HeapTuple procTup; char key[128]; PyObject *plproc; PLyProcedure *proc = NULL; int rv; enter(); fn_oid = fcinfo->flinfo->fn_oid; procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) elog(ERROR, "cache lookup failed for function %u", fn_oid); rv = snprintf(key, sizeof(key), "%u_%u", fn_oid, tgreloid); if ((rv >= sizeof(key)) || (rv < 0)) elog(ERROR, "key too long"); plproc = PyDict_GetItemString(PLy_procedure_cache, key); if (plproc != NULL) { Py_INCREF(plproc); if (!PyCObject_Check(plproc)) elog(FATAL, "expected a PyCObject, didn't get one"); mark(); proc = PyCObject_AsVoidPtr(plproc); if (proc->me != plproc) elog(FATAL, "proc->me != plproc"); /* did we find an up-to-date cache entry? */ if (proc->fn_xmin != HeapTupleHeaderGetXmin(procTup->t_data) || proc->fn_cmin != HeapTupleHeaderGetCmin(procTup->t_data)) { Py_DECREF(plproc); proc = NULL; } } if (proc == NULL) proc = PLy_procedure_create(fcinfo, tgreloid, procTup, key); ReleaseSysCache(procTup); return proc;}static PLyProcedure *PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, HeapTuple procTup, char *key){ char procName[NAMEDATALEN + 256]; DECLARE_EXC(); Form_pg_proc procStruct; PLyProcedure *volatile proc; char *volatile procSource = NULL; Datum procDatum; int i, rv; enter(); procStruct = (Form_pg_proc) GETSTRUCT(procTup); if (OidIsValid(tgreloid)) rv = snprintf(procName, sizeof(procName), "__plpython_procedure_%s_%u_trigger_%u", NameStr(procStruct->proname), fcinfo->flinfo->fn_oid, tgreloid); else rv = snprintf(procName, sizeof(procName), "__plpython_procedure_%s_%u", NameStr(procStruct->proname), fcinfo->flinfo->fn_oid); if ((rv >= sizeof(procName)) || (rv < 0)) elog(ERROR, "procedure name would overrun buffer"); proc = PLy_malloc(sizeof(PLyProcedure)); proc->proname = PLy_malloc(strlen(NameStr(procStruct->proname)) + 1); strcpy(proc->proname, NameStr(procStruct->proname)); proc->pyname = PLy_malloc(strlen(procName) + 1); strcpy(proc->pyname, procName); proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); proc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); PLy_typeinfo_init(&proc->result); for (i = 0; i < FUNC_MAX_ARGS; i++) PLy_typeinfo_init(&proc->args[i]); proc->nargs = 0; proc->code = proc->statics = NULL; proc->globals = proc->me = NULL; SAVE_EXC(); if (TRAP_EXC()) { RESTORE_EXC(); PLy_procedure_delete(proc); if (procSource) pfree(procSource); RERAISE_EXC(); } /* * get information required for output conversion of the return value, * but only if this isn't a trigger. */ if (!CALLED_AS_TRIGGER(fcinfo)) { HeapTuple rvTypeTup; Form_pg_type rvTypeStruct; rvTypeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(rvTypeTup)) elog(ERROR, "cache lookup failed for type %u", procStruct->prorettype); rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); if (rvTypeStruct->typrelid == InvalidOid) PLy_output_datum_func(&proc->result, rvTypeStruct); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("tuple return types are not supported yet"))); ReleaseSysCache(rvTypeTup); } else { /* * input/output conversion for trigger tuples. use the result * TypeInfo variable to store the tuple conversion info. */ TriggerData *tdata = (TriggerData *) fcinfo->context; PLy_input_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); PLy_output_tuple_funcs(&(proc->result), tdata->tg_relation->rd_att); } /* * now get information required for input conversion of the procedures
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -