/* * Set environment variables corresponding to trigger data */ static void set_trigger_data_envvars(TriggerData *trigdata) { const char *tg_when_str = NULL; const char *tg_level_str = NULL; const char *tg_op_str = NULL; setenv("PLSH_TG_NAME", trigdata->tg_trigger->tgname, 1); if (TRIGGER_FIRED_BEFORE(trigdata->tg_event)) tg_when_str = "BEFORE"; #ifdef TRIGGER_FIRED_INSTEAD else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event)) tg_when_str = "INSTEAD OF"; #endif else if (TRIGGER_FIRED_AFTER(trigdata->tg_event)) tg_when_str = "AFTER"; if (tg_when_str) setenv("PLSH_TG_WHEN", tg_when_str, 1); if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) tg_level_str = "ROW"; else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event)) tg_level_str = "STATEMENT"; if (tg_level_str) setenv("PLSH_TG_LEVEL", tg_level_str, 1); if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) tg_op_str = "DELETE"; else if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) tg_op_str = "INSERT"; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) tg_op_str = "UPDATE"; #ifdef TRIGGER_FIRED_BY_TRUNCATE else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event)) tg_op_str = "TRUNCATE"; #endif if (tg_op_str) setenv("PLSH_TG_OP", tg_op_str, 1); setenv("PLSH_TG_TABLE_NAME", NameStr(trigdata->tg_relation->rd_rel->relname), 1); setenv("PLSH_TG_TABLE_SCHEMA", get_namespace_name(trigdata->tg_relation->rd_rel->relnamespace), 1); }
/* * parse trigger arguments. */ void pgq_prepare_event(struct PgqTriggerEvent *ev, TriggerData *tg, bool newstyle) { memset(ev, 0, sizeof(*ev)); /* * Check trigger calling conventions */ if (TRIGGER_FIRED_BY_TRUNCATE(tg->tg_event)) { if (!TRIGGER_FIRED_FOR_STATEMENT(tg->tg_event)) elog(ERROR, "pgq tRuncate trigger must be fired FOR EACH STATEMENT"); } else if (!TRIGGER_FIRED_FOR_ROW(tg->tg_event)) { elog(ERROR, "pgq Ins/Upd/Del trigger must be fired FOR EACH ROW"); } if (tg->tg_trigger->tgnargs < 1) elog(ERROR, "pgq trigger must have destination queue as argument"); /* * check operation type */ if (TRIGGER_FIRED_BY_INSERT(tg->tg_event)) ev->op_type = 'I'; else if (TRIGGER_FIRED_BY_UPDATE(tg->tg_event)) ev->op_type = 'U'; else if (TRIGGER_FIRED_BY_DELETE(tg->tg_event)) ev->op_type = 'D'; else if (TRIGGER_FIRED_BY_TRUNCATE(tg->tg_event)) ev->op_type = 'R'; else elog(ERROR, "unknown event for pgq trigger"); /* * load table info */ ev->tgdata = tg; ev->info = find_table_info(tg->tg_relation); ev->table_name = ev->info->table_name; ev->pkey_list = ev->info->pkey_list; ev->queue_name = tg->tg_trigger->tgargs[0]; /* * parse args, newstyle args are cached */ ev->tgargs = find_trigger_info(ev->info, tg->tg_trigger->tgoid, true); if (newstyle) { if (!ev->tgargs->finalized) parse_newstyle_args(ev, tg); if (ev->tgargs->pkey_list) ev->pkey_list = ev->tgargs->pkey_list; /* Check if we have pkey */ if (ev->op_type == 'U' || ev->op_type == 'D') { if (ev->pkey_list[0] == 0) elog(ERROR, "Update/Delete on table without pkey"); } } else { parse_oldstyle_args(ev, tg); } ev->tgargs->finalized = true; /* * Check if BEFORE/AFTER makes sense. */ if (ev->tgargs->skip) { if (TRIGGER_FIRED_AFTER(tg->tg_event)) elog(ERROR, "SKIP does not work in AFTER trigger."); } else { if (!TRIGGER_FIRED_AFTER(tg->tg_event)) /* dont care ??? */ ; } if (ev->tgargs->deny) { elog(ERROR, "Table '%s' to queue '%s': change not allowed (%c)", ev->table_name, ev->queue_name, ev->op_type); } /* * init data */ ev->field[EV_TYPE] = pgq_init_varbuf(); ev->field[EV_DATA] = pgq_init_varbuf(); ev->field[EV_EXTRA1] = pgq_init_varbuf(); /* * Do the backup, if requested. */ if (ev->tgargs->backup) { ev->field[EV_EXTRA2] = pgq_init_varbuf(); pgq_urlenc_row(ev, tg->tg_trigtuple, ev->field[EV_EXTRA2]); } }
/* * Internal handler function */ Datum handler_internal(Oid function_oid, FunctionCallInfo fcinfo, bool execute) { HeapTuple proctuple; Form_pg_proc pg_proc_entry; const char * sourcecode; const char * rest; char *tempfile; int i; int argc; char * arguments[FUNC_MAX_ARGS + 2]; char * ret; HeapTuple returntuple = NULL; Datum prosrcdatum; bool isnull; proctuple = SearchSysCache(PROCOID, ObjectIdGetDatum(function_oid), 0, 0, 0); if (!HeapTupleIsValid(proctuple)) elog(ERROR, "cache lookup failed for function %u", function_oid); prosrcdatum = SysCacheGetAttr(PROCOID, proctuple, Anum_pg_proc_prosrc, &isnull); if (isnull) elog(ERROR, "null prosrc"); sourcecode = DatumGetCString(DirectFunctionCall1(textout, prosrcdatum)); parse_shell_and_arguments(sourcecode, &argc, arguments, &rest); /* validation stops here */ if (!execute) { ReleaseSysCache(proctuple); PG_RETURN_VOID(); } tempfile = write_to_tempfile(rest); arguments[argc++] = tempfile; /* evaluate arguments */ pg_proc_entry = (Form_pg_proc) GETSTRUCT(proctuple); if (CALLED_AS_TRIGGER(fcinfo)) { TriggerData *trigdata = (TriggerData *) fcinfo->context; Trigger *trigger = trigdata->tg_trigger; TupleDesc tupdesc = trigdata->tg_relation->rd_att; HeapTuple oldtuple = trigdata->tg_trigtuple; /* first the CREATE TRIGGER fixed arguments */ for (i = 0; i < trigger->tgnargs; i++) { arguments[argc++] = trigger->tgargs[i]; } if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event)) for (i = 0; i < tupdesc->natts; i++) { char * s; bool isnull; Datum attr; attr = heap_getattr(oldtuple, i + 1, tupdesc, &isnull); if (isnull) s = ""; else s = type_to_cstring(attr, tupdesc->attrs[i]->atttypid); elog(DEBUG2, "arg %d is \"%s\" (type %u)", i, s, tupdesc->attrs[i]->atttypid); arguments[argc++] = s; } /* since we can't alter the tuple anyway, set up a return tuple right now */ if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event)) returntuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event)) returntuple = trigdata->tg_trigtuple; else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event)) returntuple = trigdata->tg_newtuple; #ifdef TRIGGER_FIRED_BY_TRUNCATE else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event)) returntuple = trigdata->tg_trigtuple; #endif else elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, UPDATE, or TRUNCATE"); } else if (CALLED_AS_EVENT_TRIGGER(fcinfo)) { /* nothing */ } else /* not trigger */ { for (i = 0; i < pg_proc_entry->pronargs; i++) { char * s; if (PG_ARGISNULL(i)) s = ""; else s = type_to_cstring(PG_GETARG_DATUM(i), pg_proc_entry->proargtypes.values[i]); elog(DEBUG2, "arg %d is \"%s\"", i, s); arguments[argc++] = s; } } /* terminate list */ arguments[argc] = NULL; ret = handler_internal2(tempfile, arguments, NameStr(pg_proc_entry->proname), CALLED_AS_TRIGGER(fcinfo) ? (TriggerData *) fcinfo->context : NULL, CALLED_AS_EVENT_TRIGGER(fcinfo) ? (EventTriggerData *) fcinfo->context : NULL); ReleaseSysCache(proctuple); if (CALLED_AS_TRIGGER(fcinfo)) { PG_RETURN_DATUM(PointerGetDatum(returntuple)); } else if (CALLED_AS_EVENT_TRIGGER(fcinfo)) { PG_RETURN_NULL(); } else { if (ret) PG_RETURN_DATUM(cstring_to_type(ret, pg_proc_entry->prorettype)); else PG_RETURN_NULL(); } }
static PyObject * PLy_trigger_build_args(FunctionCallInfo fcinfo, PLyProcedure *proc, HeapTuple *rv) { 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; }