* pl_handler.c - Handler for the PL/pgSQL
* procedural language
*
- * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
#include "plpgsql.h"
+#include "access/htup_details.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "funcapi.h"
int plpgsql_variable_conflict = PLPGSQL_RESOLVE_ERROR;
+bool plpgsql_print_strict_params = false;
+
/* Hook for plugins */
PLpgSQL_plugin **plugin_ptr = NULL;
PGC_SUSET, 0,
NULL, NULL, NULL);
+ DefineCustomBoolVariable("plpgsql.print_strict_params",
+ gettext_noop("Print information about parameters in the DETAIL part of the error messages generated on INTO .. STRICT failures."),
+ NULL,
+ &plpgsql_print_strict_params,
+ false,
+ PGC_USERSET, 0,
+ NULL, NULL, NULL);
+
EmitWarningsOnPlaceholders("plpgsql");
plpgsql_HashTableInit();
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);
+ retval = plpgsql_exec_function(func, fcinfo, NULL);
}
PG_CATCH();
{
PLpgSQL_function *func;
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
+ EState *simple_eval_estate;
Datum retval;
int rc;
flinfo.fn_oid = InvalidOid;
flinfo.fn_mcxt = CurrentMemoryContext;
- retval = plpgsql_exec_function(func, &fake_fcinfo);
+ /* Create a private EState for simple-expression execution */
+ simple_eval_estate = CreateExecutorState();
+
+ /* And run the function */
+ PG_TRY();
+ {
+ retval = plpgsql_exec_function(func, &fake_fcinfo, simple_eval_estate);
+ }
+ PG_CATCH();
+ {
+ /*
+ * We need to clean up what would otherwise be long-lived resources
+ * accumulated by the failed DO block, principally cached plans for
+ * statements (which can be flushed with plpgsql_free_function_memory)
+ * and execution trees for simple expressions, which are in the
+ * private EState.
+ *
+ * Before releasing the private EState, we must clean up any
+ * simple_econtext_stack entries pointing into it, which we can do by
+ * invoking the subxact callback. (It will be called again later if
+ * some outer control level does a subtransaction abort, but no harm
+ * is done.) We cheat a bit knowing that plpgsql_subxact_cb does not
+ * pay attention to its parentSubid argument.
+ */
+ plpgsql_subxact_cb(SUBXACT_EVENT_ABORT_SUB,
+ GetCurrentSubTransactionId(),
+ 0, NULL);
+
+ /* Clean up the private EState */
+ FreeExecutorState(simple_eval_estate);
+
+ /* Function should now have no remaining use-counts ... */
+ func->use_count--;
+ Assert(func->use_count == 0);
+
+ /* ... so we can free subsidiary storage */
+ plpgsql_free_function_memory(func);
+
+ /* And propagate the error */
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ /* Clean up the private EState */
+ FreeExecutorState(simple_eval_estate);
/* Function should now have no remaining use-counts ... */
func->use_count--;
Oid *argtypes;
char **argnames;
char *argmodes;
- bool istrigger = false;
+ bool is_dml_trigger = false;
+ bool is_event_trigger = false;
int i;
/* Get the new function's pg_proc entry */
/* we assume OPAQUE with no arguments means a trigger */
if (proc->prorettype == TRIGGEROID ||
(proc->prorettype == OPAQUEOID && proc->pronargs == 0))
- istrigger = true;
+ is_dml_trigger = true;
+ else if (proc->prorettype == EVTTRIGGEROID)
+ is_event_trigger = true;
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
!IsPolymorphicType(proc->prorettype))
{
FunctionCallInfoData fake_fcinfo;
FmgrInfo flinfo;
- TriggerData trigdata;
int rc;
+ TriggerData trigdata;
+ EventTriggerData etrigdata;
/*
* Connect to SPI manager (is this needed for compilation?)
fake_fcinfo.flinfo = &flinfo;
flinfo.fn_oid = funcoid;
flinfo.fn_mcxt = CurrentMemoryContext;
- if (istrigger)
+ if (is_dml_trigger)
{
MemSet(&trigdata, 0, sizeof(trigdata));
trigdata.type = T_TriggerData;
fake_fcinfo.context = (Node *) &trigdata;
}
+ else if (is_event_trigger)
+ {
+ MemSet(&etrigdata, 0, sizeof(etrigdata));
+ etrigdata.type = T_EventTriggerData;
+ fake_fcinfo.context = (Node *) &etrigdata;
+ }
/* Test-compile the function */
plpgsql_compile(&fake_fcinfo, true);