#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;
}
1.7.1