diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 97cb763..526aa8f 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** do_compile(FunctionCallInfo fcinfo,
*** 607,613 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_name_varno = var->dno;
  
  			/* Add the variable tg_when */
  			var = plpgsql_build_variable("tg_when", 0,
--- 607,615 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_NAME;
  
  			/* Add the variable tg_when */
  			var = plpgsql_build_variable("tg_when", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 615,621 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_when_varno = var->dno;
  
  			/* Add the variable tg_level */
  			var = plpgsql_build_variable("tg_level", 0,
--- 617,625 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_WHEN;
  
  			/* Add the variable tg_level */
  			var = plpgsql_build_variable("tg_level", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 623,629 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_level_varno = var->dno;
  
  			/* Add the variable tg_op */
  			var = plpgsql_build_variable("tg_op", 0,
--- 627,635 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_LEVEL;
  
  			/* Add the variable tg_op */
  			var = plpgsql_build_variable("tg_op", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 631,637 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_op_varno = var->dno;
  
  			/* Add the variable tg_relid */
  			var = plpgsql_build_variable("tg_relid", 0,
--- 637,645 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_OP;
  
  			/* Add the variable tg_relid */
  			var = plpgsql_build_variable("tg_relid", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 639,645 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_relid_varno = var->dno;
  
  			/* Add the variable tg_relname */
  			var = plpgsql_build_variable("tg_relname", 0,
--- 647,655 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_RELID;
  
  			/* Add the variable tg_relname */
  			var = plpgsql_build_variable("tg_relname", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 647,653 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_relname_varno = var->dno;
  
  			/* tg_table_name is now preferred to tg_relname */
  			var = plpgsql_build_variable("tg_table_name", 0,
--- 657,665 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_TABLE_NAME;
  
  			/* tg_table_name is now preferred to tg_relname */
  			var = plpgsql_build_variable("tg_table_name", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 655,661 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_table_name_varno = var->dno;
  
  			/* add the variable tg_table_schema */
  			var = plpgsql_build_variable("tg_table_schema", 0,
--- 667,675 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_TABLE_NAME;
  
  			/* add the variable tg_table_schema */
  			var = plpgsql_build_variable("tg_table_schema", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 663,669 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_table_schema_varno = var->dno;
  
  			/* Add the variable tg_nargs */
  			var = plpgsql_build_variable("tg_nargs", 0,
--- 677,685 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_TABLE_SCHEMA;
  
  			/* Add the variable tg_nargs */
  			var = plpgsql_build_variable("tg_nargs", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 671,677 ****
  																-1,
  																InvalidOid),
  										 true);
! 			function->tg_nargs_varno = var->dno;
  
  			/* Add the variable tg_argv */
  			var = plpgsql_build_variable("tg_argv", 0,
--- 687,695 ----
  																-1,
  																InvalidOid),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_NARGS;
  
  			/* Add the variable tg_argv */
  			var = plpgsql_build_variable("tg_argv", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 679,685 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_argv_varno = var->dno;
  
  			break;
  
--- 697,705 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_ARGV;
  
  			break;
  
*************** do_compile(FunctionCallInfo fcinfo,
*** 701,707 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_event_varno = var->dno;
  
  			/* Add the variable tg_tag */
  			var = plpgsql_build_variable("tg_tag", 0,
--- 721,729 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_EVENT;
  
  			/* Add the variable tg_tag */
  			var = plpgsql_build_variable("tg_tag", 0,
*************** do_compile(FunctionCallInfo fcinfo,
*** 709,715 ****
  																-1,
  																function->fn_input_collation),
  										 true);
! 			function->tg_tag_varno = var->dno;
  
  			break;
  
--- 731,739 ----
  																-1,
  																function->fn_input_collation),
  										 true);
! 			Assert(var->dtype == PLPGSQL_DTYPE_VAR);
! 			var->dtype = PLPGSQL_DTYPE_PROMISE;
! 			((PLpgSQL_var *) var)->promise = PLPGSQL_PROMISE_TG_TAG;
  
  			break;
  
*************** build_row_from_vars(PLpgSQL_variable **v
*** 1878,1883 ****
--- 1902,1908 ----
  		switch (var->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				typoid = ((PLpgSQL_var *) var)->datatype->typoid;
  				typmod = ((PLpgSQL_var *) var)->datatype->atttypmod;
  				typcoll = ((PLpgSQL_var *) var)->datatype->collation;
*************** plpgsql_finish_datums(PLpgSQL_function *
*** 2196,2201 ****
--- 2221,2227 ----
  		switch (function->datums[i]->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				copiable_size += MAXALIGN(sizeof(PLpgSQL_var));
  				break;
  			case PLPGSQL_DTYPE_REC:
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index e2d315c..d578c56 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** static void coerce_function_result_tuple
*** 237,242 ****
--- 237,244 ----
  static void plpgsql_exec_error_callback(void *arg);
  static void copy_plpgsql_datums(PLpgSQL_execstate *estate,
  					PLpgSQL_function *func);
+ static void plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
+ 						PLpgSQL_var *var);
  static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
  static void push_stmt_mcontext(PLpgSQL_execstate *estate);
  static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
*************** plpgsql_exec_function(PLpgSQL_function *
*** 547,552 ****
--- 549,555 ----
  				break;
  
  			default:
+ 				/* Anything else should not be an argument variable */
  				elog(ERROR, "unrecognized dtype: %d", func->datums[i]->dtype);
  		}
  	}
*************** plpgsql_exec_trigger(PLpgSQL_function *f
*** 834,843 ****
  {
  	PLpgSQL_execstate estate;
  	ErrorContextCallback plerrcontext;
- 	int			i;
  	int			rc;
  	TupleDesc	tupdesc;
- 	PLpgSQL_var *var;
  	PLpgSQL_rec *rec_new,
  			   *rec_old;
  	HeapTuple	rettup;
--- 837,844 ----
*************** plpgsql_exec_trigger(PLpgSQL_function *f
*** 846,851 ****
--- 847,853 ----
  	 * Setup the execution state
  	 */
  	plpgsql_estate_setup(&estate, func, NULL, NULL);
+ 	estate.trigdata = trigdata;
  
  	/*
  	 * Setup error traceback support for ereport()
*************** plpgsql_exec_trigger(PLpgSQL_function *f
*** 906,1011 ****
  	rc = SPI_register_trigger_data(trigdata);
  	Assert(rc >= 0);
  
- 	/*
- 	 * Assign the special tg_ variables
- 	 */
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_op_varno]);
- 	if (TRIGGER_FIRED_BY_INSERT(trigdata->tg_event))
- 		assign_text_var(&estate, var, "INSERT");
- 	else if (TRIGGER_FIRED_BY_UPDATE(trigdata->tg_event))
- 		assign_text_var(&estate, var, "UPDATE");
- 	else if (TRIGGER_FIRED_BY_DELETE(trigdata->tg_event))
- 		assign_text_var(&estate, var, "DELETE");
- 	else if (TRIGGER_FIRED_BY_TRUNCATE(trigdata->tg_event))
- 		assign_text_var(&estate, var, "TRUNCATE");
- 	else
- 		elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, UPDATE, or TRUNCATE");
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_name_varno]);
- 	assign_simple_var(&estate, var,
- 					  DirectFunctionCall1(namein,
- 										  CStringGetDatum(trigdata->tg_trigger->tgname)),
- 					  false, true);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_when_varno]);
- 	if (TRIGGER_FIRED_BEFORE(trigdata->tg_event))
- 		assign_text_var(&estate, var, "BEFORE");
- 	else if (TRIGGER_FIRED_AFTER(trigdata->tg_event))
- 		assign_text_var(&estate, var, "AFTER");
- 	else if (TRIGGER_FIRED_INSTEAD(trigdata->tg_event))
- 		assign_text_var(&estate, var, "INSTEAD OF");
- 	else
- 		elog(ERROR, "unrecognized trigger execution time: not BEFORE, AFTER, or INSTEAD OF");
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_level_varno]);
- 	if (TRIGGER_FIRED_FOR_ROW(trigdata->tg_event))
- 		assign_text_var(&estate, var, "ROW");
- 	else if (TRIGGER_FIRED_FOR_STATEMENT(trigdata->tg_event))
- 		assign_text_var(&estate, var, "STATEMENT");
- 	else
- 		elog(ERROR, "unrecognized trigger event type: not ROW or STATEMENT");
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_relid_varno]);
- 	assign_simple_var(&estate, var,
- 					  ObjectIdGetDatum(trigdata->tg_relation->rd_id),
- 					  false, false);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_relname_varno]);
- 	assign_simple_var(&estate, var,
- 					  DirectFunctionCall1(namein,
- 										  CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))),
- 					  false, true);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_table_name_varno]);
- 	assign_simple_var(&estate, var,
- 					  DirectFunctionCall1(namein,
- 										  CStringGetDatum(RelationGetRelationName(trigdata->tg_relation))),
- 					  false, true);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_table_schema_varno]);
- 	assign_simple_var(&estate, var,
- 					  DirectFunctionCall1(namein,
- 										  CStringGetDatum(get_namespace_name(
- 																			 RelationGetNamespace(
- 																								  trigdata->tg_relation)))),
- 					  false, true);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_nargs_varno]);
- 	assign_simple_var(&estate, var,
- 					  Int16GetDatum(trigdata->tg_trigger->tgnargs),
- 					  false, false);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_argv_varno]);
- 	if (trigdata->tg_trigger->tgnargs > 0)
- 	{
- 		/*
- 		 * For historical reasons, tg_argv[] subscripts start at zero not one.
- 		 * So we can't use construct_array().
- 		 */
- 		int			nelems = trigdata->tg_trigger->tgnargs;
- 		Datum	   *elems;
- 		int			dims[1];
- 		int			lbs[1];
- 
- 		elems = palloc(sizeof(Datum) * nelems);
- 		for (i = 0; i < nelems; i++)
- 			elems[i] = CStringGetTextDatum(trigdata->tg_trigger->tgargs[i]);
- 		dims[0] = nelems;
- 		lbs[0] = 0;
- 
- 		assign_simple_var(&estate, var,
- 						  PointerGetDatum(construct_md_array(elems, NULL,
- 															 1, dims, lbs,
- 															 TEXTOID,
- 															 -1, false, 'i')),
- 						  false, true);
- 	}
- 	else
- 	{
- 		assign_simple_var(&estate, var, (Datum) 0, true, false);
- 	}
- 
  	estate.err_text = gettext_noop("during function entry");
  
  	/*
--- 908,913 ----
*************** plpgsql_exec_event_trigger(PLpgSQL_funct
*** 1153,1164 ****
  	PLpgSQL_execstate estate;
  	ErrorContextCallback plerrcontext;
  	int			rc;
- 	PLpgSQL_var *var;
  
  	/*
  	 * Setup the execution state
  	 */
  	plpgsql_estate_setup(&estate, func, NULL, NULL);
  
  	/*
  	 * Setup error traceback support for ereport()
--- 1055,1066 ----
  	PLpgSQL_execstate estate;
  	ErrorContextCallback plerrcontext;
  	int			rc;
  
  	/*
  	 * Setup the execution state
  	 */
  	plpgsql_estate_setup(&estate, func, NULL, NULL);
+ 	estate.evtrigdata = trigdata;
  
  	/*
  	 * Setup error traceback support for ereport()
*************** plpgsql_exec_event_trigger(PLpgSQL_funct
*** 1175,1189 ****
  	copy_plpgsql_datums(&estate, func);
  
  	/*
- 	 * Assign the special tg_ variables
- 	 */
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_event_varno]);
- 	assign_text_var(&estate, var, trigdata->event);
- 
- 	var = (PLpgSQL_var *) (estate.datums[func->tg_tag_varno]);
- 	assign_text_var(&estate, var, trigdata->tag);
- 
- 	/*
  	 * Let the instrumentation plugin peek at this function
  	 */
  	if (*plpgsql_plugin_ptr && (*plpgsql_plugin_ptr)->func_beg)
--- 1077,1082 ----
*************** copy_plpgsql_datums(PLpgSQL_execstate *e
*** 1321,1326 ****
--- 1214,1220 ----
  		switch (indatum->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				outdatum = (PLpgSQL_datum *) ws_next;
  				memcpy(outdatum, indatum, sizeof(PLpgSQL_var));
  				ws_next += MAXALIGN(sizeof(PLpgSQL_var));
*************** copy_plpgsql_datums(PLpgSQL_execstate *e
*** 1357,1362 ****
--- 1251,1416 ----
  }
  
  /*
+  * If the variable has an armed "promise", compute the promised value
+  * and assign it to the variable.
+  * The assignment automatically disarms the promise.
+  */
+ static void
+ plpgsql_fulfill_promise(PLpgSQL_execstate *estate,
+ 						PLpgSQL_var *var)
+ {
+ 	MemoryContext oldcontext;
+ 
+ 	if (var->promise == PLPGSQL_PROMISE_NONE)
+ 		return;					/* nothing to do */
+ 
+ 	/*
+ 	 * This will typically be invoked in a short-lived context such as the
+ 	 * mcontext.  We must create variable values in the estate's datum
+ 	 * context.  This quick-and-dirty solution risks leaking some additional
+ 	 * cruft there, but since any one promise is honored at most once per
+ 	 * function call, it's probably not worth being more careful.
+ 	 */
+ 	oldcontext = MemoryContextSwitchTo(estate->datum_context);
+ 
+ 	switch (var->promise)
+ 	{
+ 		case PLPGSQL_PROMISE_TG_NAME:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			assign_simple_var(estate, var,
+ 							  DirectFunctionCall1(namein,
+ 												  CStringGetDatum(estate->trigdata->tg_trigger->tgname)),
+ 							  false, true);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_WHEN:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			if (TRIGGER_FIRED_BEFORE(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "BEFORE");
+ 			else if (TRIGGER_FIRED_AFTER(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "AFTER");
+ 			else if (TRIGGER_FIRED_INSTEAD(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "INSTEAD OF");
+ 			else
+ 				elog(ERROR, "unrecognized trigger execution time: not BEFORE, AFTER, or INSTEAD OF");
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_LEVEL:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			if (TRIGGER_FIRED_FOR_ROW(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "ROW");
+ 			else if (TRIGGER_FIRED_FOR_STATEMENT(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "STATEMENT");
+ 			else
+ 				elog(ERROR, "unrecognized trigger event type: not ROW or STATEMENT");
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_OP:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			if (TRIGGER_FIRED_BY_INSERT(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "INSERT");
+ 			else if (TRIGGER_FIRED_BY_UPDATE(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "UPDATE");
+ 			else if (TRIGGER_FIRED_BY_DELETE(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "DELETE");
+ 			else if (TRIGGER_FIRED_BY_TRUNCATE(estate->trigdata->tg_event))
+ 				assign_text_var(estate, var, "TRUNCATE");
+ 			else
+ 				elog(ERROR, "unrecognized trigger action: not INSERT, DELETE, UPDATE, or TRUNCATE");
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_RELID:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			assign_simple_var(estate, var,
+ 							  ObjectIdGetDatum(estate->trigdata->tg_relation->rd_id),
+ 							  false, false);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_TABLE_NAME:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			assign_simple_var(estate, var,
+ 							  DirectFunctionCall1(namein,
+ 												  CStringGetDatum(RelationGetRelationName(estate->trigdata->tg_relation))),
+ 							  false, true);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_TABLE_SCHEMA:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			assign_simple_var(estate, var,
+ 							  DirectFunctionCall1(namein,
+ 												  CStringGetDatum(get_namespace_name(RelationGetNamespace(estate->trigdata->tg_relation)))),
+ 							  false, true);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_NARGS:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			assign_simple_var(estate, var,
+ 							  Int16GetDatum(estate->trigdata->tg_trigger->tgnargs),
+ 							  false, false);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_ARGV:
+ 			if (estate->trigdata == NULL)
+ 				elog(ERROR, "trigger promise is not in a trigger function");
+ 			if (estate->trigdata->tg_trigger->tgnargs > 0)
+ 			{
+ 				/*
+ 				 * For historical reasons, tg_argv[] subscripts start at zero
+ 				 * not one.  So we can't use construct_array().
+ 				 */
+ 				int			nelems = estate->trigdata->tg_trigger->tgnargs;
+ 				Datum	   *elems;
+ 				int			dims[1];
+ 				int			lbs[1];
+ 				int			i;
+ 
+ 				elems = palloc(sizeof(Datum) * nelems);
+ 				for (i = 0; i < nelems; i++)
+ 					elems[i] = CStringGetTextDatum(estate->trigdata->tg_trigger->tgargs[i]);
+ 				dims[0] = nelems;
+ 				lbs[0] = 0;
+ 
+ 				assign_simple_var(estate, var,
+ 								  PointerGetDatum(construct_md_array(elems, NULL,
+ 																	 1, dims, lbs,
+ 																	 TEXTOID,
+ 																	 -1, false, 'i')),
+ 								  false, true);
+ 			}
+ 			else
+ 			{
+ 				assign_simple_var(estate, var, (Datum) 0, true, false);
+ 			}
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_EVENT:
+ 			if (estate->evtrigdata == NULL)
+ 				elog(ERROR, "event trigger promise is not in an event trigger function");
+ 			assign_text_var(estate, var, estate->evtrigdata->event);
+ 			break;
+ 
+ 		case PLPGSQL_PROMISE_TG_TAG:
+ 			if (estate->evtrigdata == NULL)
+ 				elog(ERROR, "event trigger promise is not in an event trigger function");
+ 			assign_text_var(estate, var, estate->evtrigdata->tag);
+ 			break;
+ 
+ 		default:
+ 			elog(ERROR, "unrecognized promise type: %d", var->promise);
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ }
+ 
+ /*
   * Create a memory context for statement-lifespan variables, if we don't
   * have one already.  It will be a child of stmt_mcontext_parent, which is
   * either the function's main context or a pushed-down outer stmt_mcontext.
*************** exec_stmt_block(PLpgSQL_execstate *estat
*** 1464,1469 ****
--- 1518,1527 ----
  
  		/*
  		 * The set of dtypes handled here must match plpgsql_add_initdatums().
+ 		 *
+ 		 * Note that we currently don't support promise datums within blocks,
+ 		 * only at a function's outermost scope, so we needn't handle those
+ 		 * here.
  		 */
  		switch (datum->dtype)
  		{
*************** exec_stmt_return(PLpgSQL_execstate *esta
*** 2778,2783 ****
--- 2836,2847 ----
  
  		switch (retvar->dtype)
  		{
+ 			case PLPGSQL_DTYPE_PROMISE:
+ 				/* fulfill promise if needed, then handle like regular var */
+ 				plpgsql_fulfill_promise(estate, (PLpgSQL_var *) retvar);
+ 
+ 				/* FALL THRU */
+ 
  			case PLPGSQL_DTYPE_VAR:
  				{
  					PLpgSQL_var *var = (PLpgSQL_var *) retvar;
*************** exec_stmt_return_next(PLpgSQL_execstate 
*** 2917,2922 ****
--- 2981,2992 ----
  
  		switch (retvar->dtype)
  		{
+ 			case PLPGSQL_DTYPE_PROMISE:
+ 				/* fulfill promise if needed, then handle like regular var */
+ 				plpgsql_fulfill_promise(estate, (PLpgSQL_var *) retvar);
+ 
+ 				/* FALL THRU */
+ 
  			case PLPGSQL_DTYPE_VAR:
  				{
  					PLpgSQL_var *var = (PLpgSQL_var *) retvar;
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3487,3492 ****
--- 3557,3564 ----
  	func->cur_estate = estate;
  
  	estate->func = func;
+ 	estate->trigdata = NULL;
+ 	estate->evtrigdata = NULL;
  
  	estate->retval = (Datum) 0;
  	estate->retisnull = true;
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4542,4547 ****
--- 4614,4620 ----
  	switch (target->dtype)
  	{
  		case PLPGSQL_DTYPE_VAR:
+ 		case PLPGSQL_DTYPE_PROMISE:
  			{
  				/*
  				 * Target is a variable
*************** exec_assign_value(PLpgSQL_execstate *est
*** 4604,4613 ****
--- 4677,4692 ----
  				 * cannot reliably be made any earlier; we have to be looking
  				 * at the object's standard R/W pointer to be sure pointer
  				 * equality is meaningful.
+ 				 *
+ 				 * Also, if it's a promise variable, we should disarm the
+ 				 * promise in any case --- otherwise, assigning null to an
+ 				 * armed promise variable would fail to disarm the promise.
  				 */
  				if (var->value != newvalue || var->isnull || isNull)
  					assign_simple_var(estate, var, newvalue, isNull,
  									  (!var->datatype->typbyval && !isNull));
+ 				else
+ 					var->promise = PLPGSQL_PROMISE_NONE;
  				break;
  			}
  
*************** exec_eval_datum(PLpgSQL_execstate *estat
*** 4951,4956 ****
--- 5030,5041 ----
  
  	switch (datum->dtype)
  	{
+ 		case PLPGSQL_DTYPE_PROMISE:
+ 			/* fulfill promise if needed, then handle like regular var */
+ 			plpgsql_fulfill_promise(estate, (PLpgSQL_var *) datum);
+ 
+ 			/* FALL THRU */
+ 
  		case PLPGSQL_DTYPE_VAR:
  			{
  				PLpgSQL_var *var = (PLpgSQL_var *) datum;
*************** plpgsql_exec_get_datum_type(PLpgSQL_exec
*** 5093,5098 ****
--- 5178,5184 ----
  	switch (datum->dtype)
  	{
  		case PLPGSQL_DTYPE_VAR:
+ 		case PLPGSQL_DTYPE_PROMISE:
  			{
  				PLpgSQL_var *var = (PLpgSQL_var *) datum;
  
*************** plpgsql_exec_get_datum_type_info(PLpgSQL
*** 5176,5181 ****
--- 5262,5268 ----
  	switch (datum->dtype)
  	{
  		case PLPGSQL_DTYPE_VAR:
+ 		case PLPGSQL_DTYPE_PROMISE:
  			{
  				PLpgSQL_var *var = (PLpgSQL_var *) datum;
  
*************** plpgsql_param_fetch(ParamListInfo params
*** 5874,5879 ****
--- 5961,5967 ----
  		switch (datum->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				/* always safe */
  				break;
  
*************** plpgsql_param_compile(ParamListInfo para
*** 5989,5996 ****
  	 * Select appropriate eval function.  It seems worth special-casing
  	 * DTYPE_VAR and DTYPE_RECFIELD for performance.  Also, we can determine
  	 * in advance whether MakeExpandedObjectReadOnly() will be required.
! 	 * Currently, only VAR and REC datums could contain read/write expanded
! 	 * objects.
  	 */
  	if (datum->dtype == PLPGSQL_DTYPE_VAR)
  	{
--- 6077,6084 ----
  	 * Select appropriate eval function.  It seems worth special-casing
  	 * DTYPE_VAR and DTYPE_RECFIELD for performance.  Also, we can determine
  	 * in advance whether MakeExpandedObjectReadOnly() will be required.
! 	 * Currently, only VAR/PROMISE and REC datums could contain read/write
! 	 * expanded objects.
  	 */
  	if (datum->dtype == PLPGSQL_DTYPE_VAR)
  	{
*************** plpgsql_param_compile(ParamListInfo para
*** 6002,6007 ****
--- 6090,6103 ----
  	}
  	else if (datum->dtype == PLPGSQL_DTYPE_RECFIELD)
  		scratch.d.cparam.paramfunc = plpgsql_param_eval_recfield;
+ 	else if (datum->dtype == PLPGSQL_DTYPE_PROMISE)
+ 	{
+ 		if (dno != expr->rwparam &&
+ 			((PLpgSQL_var *) datum)->datatype->typlen == -1)
+ 			scratch.d.cparam.paramfunc = plpgsql_param_eval_generic_ro;
+ 		else
+ 			scratch.d.cparam.paramfunc = plpgsql_param_eval_generic;
+ 	}
  	else if (datum->dtype == PLPGSQL_DTYPE_REC &&
  			 dno != expr->rwparam)
  		scratch.d.cparam.paramfunc = plpgsql_param_eval_generic_ro;
*************** static void
*** 7680,7686 ****
  assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
  				  Datum newvalue, bool isnull, bool freeable)
  {
! 	Assert(var->dtype == PLPGSQL_DTYPE_VAR);
  	/* Free the old value if needed */
  	if (var->freeval)
  	{
--- 7776,7783 ----
  assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
  				  Datum newvalue, bool isnull, bool freeable)
  {
! 	Assert(var->dtype == PLPGSQL_DTYPE_VAR ||
! 		   var->dtype == PLPGSQL_DTYPE_PROMISE);
  	/* Free the old value if needed */
  	if (var->freeval)
  	{
*************** assign_simple_var(PLpgSQL_execstate *est
*** 7695,7700 ****
--- 7792,7804 ----
  	var->value = newvalue;
  	var->isnull = isnull;
  	var->freeval = freeable;
+ 
+ 	/*
+ 	 * If it's a promise variable, then either we just assigned the promised
+ 	 * value, or the user explicitly assigned an overriding value.  Either
+ 	 * way, cancel the promise.
+ 	 */
+ 	var->promise = PLPGSQL_PROMISE_NONE;
  }
  
  /*
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index b36fab6..379fd69 100644
*** a/src/pl/plpgsql/src/pl_funcs.c
--- b/src/pl/plpgsql/src/pl_funcs.c
*************** plpgsql_free_function_memory(PLpgSQL_fun
*** 729,734 ****
--- 729,735 ----
  		switch (d->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				{
  					PLpgSQL_var *var = (PLpgSQL_var *) d;
  
*************** plpgsql_dumptree(PLpgSQL_function *func)
*** 1582,1587 ****
--- 1583,1589 ----
  		switch (d->dtype)
  		{
  			case PLPGSQL_DTYPE_VAR:
+ 			case PLPGSQL_DTYPE_PROMISE:
  				{
  					PLpgSQL_var *var = (PLpgSQL_var *) d;
  
*************** plpgsql_dumptree(PLpgSQL_function *func)
*** 1608,1613 ****
--- 1610,1618 ----
  						dump_expr(var->cursor_explicit_expr);
  						printf("\n");
  					}
+ 					if (var->promise != PLPGSQL_PROMISE_NONE)
+ 						printf("                                  PROMISE %d\n",
+ 							   (int) var->promise);
  				}
  				break;
  			case PLPGSQL_DTYPE_ROW:
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index ee943ee..5bf4594 100644
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
*************** make_return_stmt(int location)
*** 3170,3175 ****
--- 3170,3176 ----
  
  		if (tok == T_DATUM && plpgsql_peek() == ';' &&
  			(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+ 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
  			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
  			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
  		{
*************** make_return_next_stmt(int location)
*** 3231,3236 ****
--- 3232,3238 ----
  
  		if (tok == T_DATUM && plpgsql_peek() == ';' &&
  			(yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_VAR ||
+ 			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_PROMISE ||
  			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_ROW ||
  			 yylval.wdatum.datum->dtype == PLPGSQL_DTYPE_REC))
  		{
*************** check_assignable(PLpgSQL_datum *datum, i
*** 3318,3323 ****
--- 3320,3326 ----
  	switch (datum->dtype)
  	{
  		case PLPGSQL_DTYPE_VAR:
+ 		case PLPGSQL_DTYPE_PROMISE:
  			if (((PLpgSQL_var *) datum)->isconst)
  				ereport(ERROR,
  						(errcode(ERRCODE_ERROR_IN_ASSIGNMENT),
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index dadbfb5..01b89a5 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef enum PLpgSQL_datum_type
*** 63,72 ****
  	PLPGSQL_DTYPE_ROW,
  	PLPGSQL_DTYPE_REC,
  	PLPGSQL_DTYPE_RECFIELD,
! 	PLPGSQL_DTYPE_ARRAYELEM
  } PLpgSQL_datum_type;
  
  /*
   * Variants distinguished in PLpgSQL_type structs
   */
  typedef enum PLpgSQL_type_type
--- 63,92 ----
  	PLPGSQL_DTYPE_ROW,
  	PLPGSQL_DTYPE_REC,
  	PLPGSQL_DTYPE_RECFIELD,
! 	PLPGSQL_DTYPE_ARRAYELEM,
! 	PLPGSQL_DTYPE_PROMISE
  } PLpgSQL_datum_type;
  
  /*
+  * DTYPE_PROMISE datums have these possible ways of computing the promise
+  */
+ typedef enum PLpgSQL_promise_type
+ {
+ 	PLPGSQL_PROMISE_NONE = 0,	/* not a promise, or promise satisfied */
+ 	PLPGSQL_PROMISE_TG_NAME,
+ 	PLPGSQL_PROMISE_TG_WHEN,
+ 	PLPGSQL_PROMISE_TG_LEVEL,
+ 	PLPGSQL_PROMISE_TG_OP,
+ 	PLPGSQL_PROMISE_TG_RELID,
+ 	PLPGSQL_PROMISE_TG_TABLE_NAME,
+ 	PLPGSQL_PROMISE_TG_TABLE_SCHEMA,
+ 	PLPGSQL_PROMISE_TG_NARGS,
+ 	PLPGSQL_PROMISE_TG_ARGV,
+ 	PLPGSQL_PROMISE_TG_EVENT,
+ 	PLPGSQL_PROMISE_TG_TAG
+ } PLpgSQL_promise_type;
+ 
+ /*
   * Variants distinguished in PLpgSQL_type structs
   */
  typedef enum PLpgSQL_type_type
*************** typedef struct PLpgSQL_variable
*** 248,253 ****
--- 268,281 ----
  
  /*
   * Scalar variable
+  *
+  * DTYPE_VAR and DTYPE_PROMISE datums both use this struct type.
+  * A PROMISE datum works exactly like a VAR datum for most purposes,
+  * but if it is read without having previously been assigned to, then
+  * a special "promised" value is computed and assigned to the datum
+  * before the read is performed.  This technique avoids the overhead of
+  * computing the variable's value in cases where we expect that many
+  * functions will never read it.
   */
  typedef struct PLpgSQL_var
  {
*************** typedef struct PLpgSQL_var
*** 271,279 ****
--- 299,316 ----
  	int			cursor_explicit_argrow;
  	int			cursor_options;
  
+ 	/* Fields below here can change at runtime */
+ 
  	Datum		value;
  	bool		isnull;
  	bool		freeval;
+ 
+ 	/*
+ 	 * The promise field records which "promised" value to assign if the
+ 	 * promise must be honored.  If it's a normal variable, or the promise has
+ 	 * been fulfilled, this is PLPGSQL_PROMISE_NONE.
+ 	 */
+ 	PLpgSQL_promise_type promise;
  } PLpgSQL_var;
  
  /*
*************** typedef struct PLpgSQL_function
*** 869,888 ****
  	int			found_varno;
  	int			new_varno;
  	int			old_varno;
- 	int			tg_name_varno;
- 	int			tg_when_varno;
- 	int			tg_level_varno;
- 	int			tg_op_varno;
- 	int			tg_relid_varno;
- 	int			tg_relname_varno;
- 	int			tg_table_name_varno;
- 	int			tg_table_schema_varno;
- 	int			tg_nargs_varno;
- 	int			tg_argv_varno;
- 
- 	/* for event triggers */
- 	int			tg_event_varno;
- 	int			tg_tag_varno;
  
  	PLpgSQL_resolve_option resolve_option;
  
--- 906,911 ----
*************** typedef struct PLpgSQL_execstate
*** 912,917 ****
--- 935,943 ----
  {
  	PLpgSQL_function *func;		/* function being executed */
  
+ 	TriggerData *trigdata;		/* if regular trigger, data about firing */
+ 	EventTriggerData *evtrigdata;	/* if event trigger, data about firing */
+ 
  	Datum		retval;
  	bool		retisnull;
  	Oid			rettype;		/* type of current retval */
