/* * 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(); } }
Datum plpgsql_call_handler(PG_FUNCTION_ARGS) { PLpgSQL_function *func; PLpgSQL_execstate *save_cur_estate; Datum retval; int rc; /* * Connect to SPI manager */ if ((rc = SPI_connect()) != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(rc)); /* Find or compile the function */ func = plpgsql_compile(fcinfo, false); /* Must save and restore prior value of cur_estate */ save_cur_estate = func->cur_estate; /* Mark the function as busy, so it can't be deleted from under us */ func->use_count++; PG_TRY(); { /* * Determine if called as function or trigger and call appropriate * subhandler */ if (CALLED_AS_TRIGGER(fcinfo)) retval = PointerGetDatum(plpgsql_exec_trigger(func, (TriggerData *) fcinfo->context)); else if (CALLED_AS_EVENT_TRIGGER(fcinfo)) { plpgsql_exec_event_trigger(func, (EventTriggerData *) fcinfo->context); retval = (Datum) 0; } else retval = plpgsql_exec_function(func, fcinfo); } PG_CATCH(); { /* Decrement use-count, restore cur_estate, and propagate error */ func->use_count--; func->cur_estate = save_cur_estate; PG_RE_THROW(); } PG_END_TRY(); func->use_count--; func->cur_estate = save_cur_estate; /* * Disconnect from SPI manager */ if ((rc = SPI_finish()) != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(rc)); return retval; }