#include "postgres.h"
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "utils/builtins.h"
#include "utils/rel.h"
#include "utils/typcache.h"
#include "plpython.h"
#include "plpy_exec.h"
#include "plpy_elog.h"
#include "plpy_main.h"
#include "plpy_procedure.h"
#include "plpy_subxactobject.h"
Go to the source code of this file.
Functions | |
static PyObject * | PLy_function_build_args (FunctionCallInfo fcinfo, PLyProcedure *proc) |
static void | PLy_function_delete_args (PLyProcedure *proc) |
static void | plpython_return_error_callback (void *arg) |
static PyObject * | PLy_trigger_build_args (FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *rv) |
static HeapTuple | PLy_modify_tuple (PLyProcedure *proc, PyObject *pltd, TriggerData *tdata, HeapTuple otup) |
static void | plpython_trigger_error_callback (void *arg) |
static PyObject * | PLy_procedure_call (PLyProcedure *proc, char *kargs, PyObject *vargs) |
static void | PLy_abort_open_subtransactions (int save_subxact_level) |
Datum | PLy_exec_function (FunctionCallInfo fcinfo, PLyProcedure *proc) |
HeapTuple | PLy_exec_trigger (FunctionCallInfo fcinfo, PLyProcedure *proc) |
static void plpython_return_error_callback | ( | void * | arg | ) | [static] |
Definition at line 446 of file plpy_exec.c.
References PLyExecutionContext::curr_proc, errcontext, and PLy_current_execution_context().
{ PLyExecutionContext *exec_ctx = PLy_current_execution_context(); if (exec_ctx->curr_proc) errcontext("while creating return value"); }
static void plpython_trigger_error_callback | ( | void * | arg | ) | [static] |
Definition at line 774 of file plpy_exec.c.
References PLyExecutionContext::curr_proc, errcontext, and PLy_current_execution_context().
{ PLyExecutionContext *exec_ctx = PLy_current_execution_context(); if (exec_ctx->curr_proc) errcontext("while modifying trigger row"); }
static void PLy_abort_open_subtransactions | ( | int | save_subxact_level | ) | [static] |
Definition at line 829 of file plpy_exec.c.
References Assert, CurrentResourceOwner, ereport, errmsg(), explicit_subtransactions, linitial, list_delete_first(), list_length(), MemoryContextSwitchTo(), NIL, PLySubtransactionData::oldcontext, PLySubtransactionData::oldowner, PLy_free(), RollbackAndReleaseCurrentSubTransaction(), SPI_restore_connection(), and WARNING.
Referenced by PLy_procedure_call().
{ Assert(save_subxact_level >= 0); while (list_length(explicit_subtransactions) > save_subxact_level) { PLySubtransactionData *subtransactiondata; Assert(explicit_subtransactions != NIL); ereport(WARNING, (errmsg("forcibly aborting a subtransaction that has not been exited"))); RollbackAndReleaseCurrentSubTransaction(); SPI_restore_connection(); subtransactiondata = (PLySubtransactionData *) linitial(explicit_subtransactions); explicit_subtransactions = list_delete_first(explicit_subtransactions); MemoryContextSwitchTo(subtransactiondata->oldcontext); CurrentResourceOwner = subtransactiondata->oldowner; PLy_free(subtransactiondata); } }
Datum PLy_exec_function | ( | FunctionCallInfo | fcinfo, | |
PLyProcedure * | proc | |||
) |
Definition at line 45 of file plpy_exec.c.
References ReturnSetInfo::allowedModes, Assert, ErrorContextCallback::callback, PLyTypeOutput::d, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, error_context_stack, ExprEndResult, PLyObToDatum::func, InputFunctionCall(), PLyTypeInfo::is_rowtype, PLyProcedure::is_setof, IsA, ReturnSetInfo::isDone, FunctionCallInfoData::isnull, lookup_rowtype_tupdesc(), NULL, PLyTypeInfo::out, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLy_function_build_args(), PLy_function_delete_args(), PLy_procedure_call(), PLyObject_ToCompositeDatum(), ErrorContextCallback::previous, RECORDOID, PLyProcedure::result, FunctionCallInfoData::resultinfo, ReturnSetInfo::returnMode, PLyProcedure::setof, SPI_finish(), SPI_OK_FINISH, PLyObToDatum::typfunc, PLyObToDatum::typioparam, PLyObToDatum::typmod, PLyObToDatum::typoid, and VOIDOID.
Referenced by plpython_call_handler(), and plpython_inline_handler().
{ Datum rv; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; ErrorContextCallback plerrcontext; PG_TRY(); { if (!proc->is_setof || proc->setof == NULL) { /* * Simple type returning function or first time for SETOF * function: actually execute the function. */ plargs = PLy_function_build_args(fcinfo, proc); plrv = PLy_procedure_call(proc, "args", plargs); if (!proc->is_setof) { /* * SETOF function parameters will be deleted when last row is * returned */ PLy_function_delete_args(proc); } Assert(plrv != NULL); } /* * If it returns a set, call the iterator to get the next return item. * We stay in the SPI context while doing this, because PyIter_Next() * calls back into Python code which might contain SPI calls. */ if (proc->is_setof) { bool has_error = false; ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (proc->setof == NULL) { /* first time -- do checks and setup */ 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 only value per call."))); } rsi->returnMode = SFRM_ValuePerCall; /* Make iterator out of returned object */ proc->setof = PyObject_GetIter(plrv); Py_DECREF(plrv); plrv = NULL; if (proc->setof == 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(proc->setof); if (plrv) rsi->isDone = ExprMultipleResult; else { rsi->isDone = ExprEndResult; has_error = PyErr_Occurred() != NULL; } if (rsi->isDone == ExprEndResult) { /* Iterator is exhausted or error happened */ Py_DECREF(proc->setof); proc->setof = NULL; Py_XDECREF(plargs); Py_XDECREF(plrv); PLy_function_delete_args(proc); if (has_error) PLy_elog(ERROR, "error fetching next item from iterator"); /* Disconnect from the SPI manager before returning */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); fcinfo->isnull = true; return (Datum) NULL; } } /* * 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"); plerrcontext.callback = plpython_return_error_callback; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* * If the function is declared to return void, the Python return value * must be None. For void-returning functions, we also treat a None * return value as a special "void datum" rather than NULL (as is the * case for non-void-returning functions). */ if (proc->result.out.d.typoid == VOIDOID) { if (plrv != Py_None) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("PL/Python function with return type \"void\" did not return None"))); fcinfo->isnull = false; rv = (Datum) 0; } else if (plrv == Py_None) { fcinfo->isnull = true; if (proc->result.is_rowtype < 1) rv = InputFunctionCall(&proc->result.out.d.typfunc, NULL, proc->result.out.d.typioparam, -1); else /* Tuple as None */ rv = (Datum) NULL; } else if (proc->result.is_rowtype >= 1) { TupleDesc desc; /* make sure it's not an unnamed record */ Assert((proc->result.out.d.typoid == RECORDOID && proc->result.out.d.typmod != -1) || (proc->result.out.d.typoid != RECORDOID && proc->result.out.d.typmod == -1)); desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, proc->result.out.d.typmod); rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv); fcinfo->isnull = (rv == (Datum) NULL); } else { fcinfo->isnull = false; rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv); } } PG_CATCH(); { Py_XDECREF(plargs); Py_XDECREF(plrv); /* * If there was an error the iterator might have not been exhausted * yet. Set it to NULL so the next invocation of the function will * start the iteration again. */ Py_XDECREF(proc->setof); proc->setof = NULL; PG_RE_THROW(); } PG_END_TRY(); error_context_stack = plerrcontext.previous; Py_XDECREF(plargs); Py_DECREF(plrv); return rv; }
HeapTuple PLy_exec_trigger | ( | FunctionCallInfo | fcinfo, | |
PLyProcedure * | proc | |||
) |
Definition at line 240 of file plpy_exec.c.
References Assert, CALLED_AS_TRIGGER, FunctionCallInfoData::context, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, pg_strcasecmp(), PG_TRY, PLy_input_tuple_funcs(), PLy_modify_tuple(), PLy_output_tuple_funcs(), PLy_procedure_call(), PLy_trigger_build_args(), PLyUnicode_AsString(), RelationData::rd_att, PLyProcedure::result, SPI_finish(), SPI_OK_FINISH, TriggerData::tg_event, TriggerData::tg_relation, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_UPDATE, and WARNING.
Referenced by plpython_call_handler().
{ HeapTuple rv = NULL; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; TriggerData *tdata; Assert(CALLED_AS_TRIGGER(fcinfo)); /* * Input/output conversion for trigger tuples. Use the result TypeInfo * variable to store the tuple conversion info. We do this over again on * each call to cover the possibility that the relation's tupdesc changed * since the trigger was last called. PLy_input_tuple_funcs and * PLy_output_tuple_funcs are responsible for not doing repetitive work. */ 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); PG_TRY(); { plargs = PLy_trigger_build_args(fcinfo, proc, &rv); plrv = PLy_procedure_call(proc, "TD", plargs); Assert(plrv != NULL); /* * Disconnect from SPI manager */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); /* * return of None means we're happy with the tuple */ if (plrv != Py_None) { char *srv; if (PyString_Check(plrv)) srv = PyString_AsString(plrv); else if (PyUnicode_Check(plrv)) srv = PLyUnicode_AsString(plrv); else { ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("unexpected return value from trigger procedure"), errdetail("Expected None or a string."))); srv = NULL; /* keep compiler quiet */ } if (pg_strcasecmp(srv, "SKIP") == 0) rv = NULL; else if (pg_strcasecmp(srv, "MODIFY") == 0) { TriggerData *tdata = (TriggerData *) fcinfo->context; if (TRIGGER_FIRED_BY_INSERT(tdata->tg_event) || TRIGGER_FIRED_BY_UPDATE(tdata->tg_event)) rv = PLy_modify_tuple(proc, plargs, tdata, rv); else ereport(WARNING, (errmsg("PL/Python trigger function returned \"MODIFY\" in a DELETE trigger -- ignored"))); } else if (pg_strcasecmp(srv, "OK") != 0) { /* * accept "OK" as an alternative to None; otherwise, raise an * error */ ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("unexpected return value from trigger procedure"), errdetail("Expected None, \"OK\", \"SKIP\", or \"MODIFY\"."))); } } } PG_CATCH(); { Py_XDECREF(plargs); Py_XDECREF(plrv); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(plargs); Py_DECREF(plrv); return rv; }
static PyObject * PLy_function_build_args | ( | FunctionCallInfo | fcinfo, | |
PLyProcedure * | proc | |||
) | [static] |
Definition at line 338 of file plpy_exec.c.
References FunctionCallInfoData::arg, arg, PLyProcedure::argnames, FunctionCallInfoData::argnull, PLyProcedure::args, PLyTypeOutput::d, PLyTypeInput::d, DatumGetHeapTupleHeader, ereport, errcode(), errmsg(), ERROR, PLyDatumToOb::func, get_call_result_type(), PLyProcedure::globals, HeapTupleHeaderGetDatumLength, HeapTupleHeaderGetTypeId, HeapTupleHeaderGetTypMod, i, PLyTypeInfo::in, PLyTypeInfo::is_rowtype, lookup_rowtype_tupdesc(), PLyProcedure::nargs, NULL, PLyTypeInfo::out, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLy_input_tuple_funcs(), PLy_output_record_funcs(), PLyDict_FromTuple(), RECORDOID, ReleaseTupleDesc, PLyProcedure::result, HeapTupleData::t_data, HeapTupleData::t_len, TYPEFUNC_COMPOSITE, and PLyObToDatum::typoid.
Referenced by PLy_exec_function().
{ PyObject *volatile arg = NULL; PyObject *volatile args = NULL; int i; PG_TRY(); { args = PyList_New(proc->nargs); for (i = 0; i < proc->nargs; i++) { if (proc->args[i].is_rowtype > 0) { if (fcinfo->argnull[i]) arg = NULL; else { HeapTupleHeader td; Oid tupType; int32 tupTypmod; TupleDesc tupdesc; HeapTupleData tmptup; td = DatumGetHeapTupleHeader(fcinfo->arg[i]); /* Extract rowtype info and find a tupdesc */ tupType = HeapTupleHeaderGetTypeId(td); tupTypmod = HeapTupleHeaderGetTypMod(td); tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Set up I/O funcs if not done yet */ if (proc->args[i].is_rowtype != 1) PLy_input_tuple_funcs(&(proc->args[i]), tupdesc); /* Build a temporary HeapTuple control structure */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; arg = PLyDict_FromTuple(&(proc->args[i]), &tmptup, tupdesc); ReleaseTupleDesc(tupdesc); } } else { if (fcinfo->argnull[i]) arg = NULL; else { arg = (proc->args[i].in.d.func) (&(proc->args[i].in.d), fcinfo->arg[i]); } } if (arg == NULL) { Py_INCREF(Py_None); arg = Py_None; } if (PyList_SetItem(args, i, arg) == -1) PLy_elog(ERROR, "PyList_SetItem() failed, while setting up arguments"); if (proc->argnames && proc->argnames[i] && PyDict_SetItemString(proc->globals, proc->argnames[i], arg) == -1) PLy_elog(ERROR, "PyDict_SetItemString() failed, while setting up arguments"); arg = NULL; } /* Set up output conversion for functions returning RECORD */ if (proc->result.out.d.typoid == RECORDOID) { TupleDesc desc; 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"))); /* cache the output conversion functions */ PLy_output_record_funcs(&(proc->result), desc); } } PG_CATCH(); { Py_XDECREF(arg); Py_XDECREF(args); PG_RE_THROW(); } PG_END_TRY(); return args; }
static void PLy_function_delete_args | ( | PLyProcedure * | proc | ) | [static] |
Definition at line 433 of file plpy_exec.c.
References PLyProcedure::argnames, PLyProcedure::globals, i, and PLyProcedure::nargs.
Referenced by PLy_exec_function().
static HeapTuple PLy_modify_tuple | ( | PLyProcedure * | proc, | |
PyObject * | pltd, | |||
TriggerData * | tdata, | |||
HeapTuple | otup | |||
) | [static] |
Definition at line 633 of file plpy_exec.c.
References tupleDesc::attrs, PLyObToTuple::atts, ErrorContextCallback::callback, elog, ereport, errmsg(), ERROR, error_context_stack, FATAL, PLyObToDatum::func, i, InputFunctionCall(), NULL, PLyTypeInfo::out, palloc(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLyUnicode_AsString(), ErrorContextCallback::previous, PLyTypeOutput::r, RelationData::rd_att, PLyProcedure::result, SPI_ERROR_NOATTRIBUTE, SPI_fnumber(), SPI_modifytuple(), SPI_result, TriggerData::tg_relation, PLyObToDatum::typfunc, and PLyObToDatum::typioparam.
Referenced by PLy_exec_trigger().
{ PyObject *volatile plntup; PyObject *volatile plkeys; PyObject *volatile platt; PyObject *volatile plval; PyObject *volatile plstr; HeapTuple rtup; int natts, i, attn, atti; int *volatile modattrs; Datum *volatile modvalues; char *volatile modnulls; TupleDesc tupdesc; ErrorContextCallback plerrcontext; plerrcontext.callback = plpython_trigger_error_callback; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; plntup = plkeys = platt = plval = plstr = NULL; modattrs = NULL; modvalues = NULL; modnulls = NULL; PG_TRY(); { if ((plntup = PyDict_GetItemString(pltd, "new")) == NULL) ereport(ERROR, (errmsg("TD[\"new\"] deleted, cannot modify row"))); if (!PyDict_Check(plntup)) ereport(ERROR, (errmsg("TD[\"new\"] is not a dictionary"))); Py_INCREF(plntup); plkeys = PyDict_Keys(plntup); natts = PyList_Size(plkeys); modattrs = (int *) palloc(natts * sizeof(int)); modvalues = (Datum *) palloc(natts * sizeof(Datum)); modnulls = (char *) palloc(natts * sizeof(char)); tupdesc = tdata->tg_relation->rd_att; for (i = 0; i < natts; i++) { char *plattstr; platt = PyList_GetItem(plkeys, i); if (PyString_Check(platt)) plattstr = PyString_AsString(platt); else if (PyUnicode_Check(platt)) plattstr = PLyUnicode_AsString(platt); else { ereport(ERROR, (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, (errmsg("key \"%s\" found in TD[\"new\"] does not exist as a column in the triggering row", plattstr))); atti = attn - 1; plval = PyDict_GetItem(plntup, platt); if (plval == NULL) elog(FATAL, "Python interpreter is probably corrupted"); Py_INCREF(plval); modattrs[i] = attn; if (tupdesc->attrs[atti]->attisdropped) { modvalues[i] = (Datum) 0; modnulls[i] = 'n'; } else if (plval != Py_None) { PLyObToDatum *att = &proc->result.out.r.atts[atti]; modvalues[i] = (att->func) (att, tupdesc->attrs[atti]->atttypmod, plval); modnulls[i] = ' '; } else { modvalues[i] = InputFunctionCall(&proc->result.out.r.atts[atti].typfunc, NULL, proc->result.out.r.atts[atti].typioparam, tupdesc->attrs[atti]->atttypmod); modnulls[i] = 'n'; } Py_DECREF(plval); plval = NULL; } rtup = SPI_modifytuple(tdata->tg_relation, otup, natts, modattrs, modvalues, modnulls); if (rtup == NULL) elog(ERROR, "SPI_modifytuple failed: error %d", SPI_result); } PG_CATCH(); { Py_XDECREF(plntup); Py_XDECREF(plkeys); Py_XDECREF(plval); Py_XDECREF(plstr); if (modnulls) pfree(modnulls); if (modvalues) pfree(modvalues); if (modattrs) pfree(modattrs); PG_RE_THROW(); } PG_END_TRY(); Py_DECREF(plntup); Py_DECREF(plkeys); pfree(modattrs); pfree(modvalues); pfree(modnulls); error_context_stack = plerrcontext.previous; return rtup; }
static PyObject * PLy_procedure_call | ( | PLyProcedure * | proc, | |
char * | kargs, | |||
PyObject * | vargs | |||
) | [static] |
Definition at line 784 of file plpy_exec.c.
References Assert, PLyProcedure::code, ERROR, explicit_subtransactions, PLyProcedure::globals, list_length(), NULL, PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_abort_open_subtransactions(), and PLy_elog().
Referenced by PLy_exec_function(), and PLy_exec_trigger().
{ PyObject *rv; int volatile save_subxact_level = list_length(explicit_subtransactions); PyDict_SetItemString(proc->globals, kargs, vargs); PG_TRY(); { #if PY_VERSION_HEX >= 0x03020000 rv = PyEval_EvalCode(proc->code, proc->globals, proc->globals); #else rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals); #endif /* * Since plpy will only let you close subtransactions that you * started, you cannot *unnest* subtransactions, only *nest* them * without closing. */ Assert(list_length(explicit_subtransactions) >= save_subxact_level); } PG_CATCH(); { PLy_abort_open_subtransactions(save_subxact_level); PG_RE_THROW(); } PG_END_TRY(); PLy_abort_open_subtransactions(save_subxact_level); /* If the Python code returned an error, propagate it */ if (rv == NULL) PLy_elog(ERROR, NULL); return rv; }
static PyObject * PLy_trigger_build_args | ( | FunctionCallInfo | fcinfo, | |
PLyProcedure * | proc, | |||
HeapTuple * | rv | |||
) | [static] |
Definition at line 455 of file plpy_exec.c.
References FunctionCallInfoData::context, DatumGetCString, DirectFunctionCall1, elog, ERROR, i, NULL, ObjectIdGetDatum, oidout(), pfree(), PG_CATCH, PG_END_TRY, PG_RE_THROW, PG_TRY, PLy_elog(), PLyDict_FromTuple(), RelationData::rd_att, RelationData::rd_id, PLyProcedure::result, SPI_getnspname(), SPI_getrelname(), TriggerData::tg_event, TriggerData::tg_newtuple, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigtuple, Trigger::tgargs, Trigger::tgname, Trigger::tgnargs, TRIGGER_FIRED_AFTER, TRIGGER_FIRED_BEFORE, TRIGGER_FIRED_BY_DELETE, TRIGGER_FIRED_BY_INSERT, TRIGGER_FIRED_BY_TRUNCATE, TRIGGER_FIRED_BY_UPDATE, TRIGGER_FIRED_FOR_ROW, TRIGGER_FIRED_FOR_STATEMENT, and TRIGGER_FIRED_INSTEAD.
Referenced by PLy_exec_trigger().
{ TriggerData *tdata = (TriggerData *) fcinfo->context; PyObject *pltname, *pltevent, *pltwhen, *pltlevel, *pltrelid, *plttablename, *plttableschema; PyObject *pltargs, *pytnew, *pytold; PyObject *volatile pltdata = NULL; char *stroid; PG_TRY(); { pltdata = PyDict_New(); if (!pltdata) PLy_elog(ERROR, "could not create new dictionary while building trigger arguments"); 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); stroid = SPI_getrelname(tdata->tg_relation); plttablename = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "table_name", plttablename); Py_DECREF(plttablename); pfree(stroid); stroid = SPI_getnspname(tdata->tg_relation); plttableschema = PyString_FromString(stroid); PyDict_SetItemString(pltdata, "table_schema", plttableschema); Py_DECREF(plttableschema); 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 if (TRIGGER_FIRED_INSTEAD(tdata->tg_event)) pltwhen = PyString_FromString("INSTEAD OF"); 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 = 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 if (TRIGGER_FIRED_BY_TRUNCATE(tdata->tg_event)) pltevent = PyString_FromString("TRUNCATE"); 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); } PG_CATCH(); { Py_XDECREF(pltdata); PG_RE_THROW(); } PG_END_TRY(); return pltdata; }