Pavel Stehule <[email protected]> writes:
> please, can you rebase all three patches necessary for patching?
Done. These now need to be applied over
https://www.postgresql.org/message-id/[email protected]
regards, tom lane
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 09ecaec..97cb763 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** plpgsql_adddatum(PLpgSQL_datum *new)
*** 2183,2194 ****
--- 2183,2211 ----
static void
plpgsql_finish_datums(PLpgSQL_function *function)
{
+ Size copiable_size = 0;
int i;
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
+ {
function->datums[i] = plpgsql_Datums[i];
+
+ /* This must agree with copy_plpgsql_datums on what is copiable */
+ switch (function->datums[i]->dtype)
+ {
+ case PLPGSQL_DTYPE_VAR:
+ copiable_size += MAXALIGN(sizeof(PLpgSQL_var));
+ break;
+ case PLPGSQL_DTYPE_REC:
+ copiable_size += MAXALIGN(sizeof(PLpgSQL_rec));
+ break;
+ default:
+ break;
+ }
+ }
+ function->copiable_size = copiable_size;
}
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index e119744..e2d315c 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** static HTAB *shared_cast_hash = NULL;
*** 235,241 ****
static void coerce_function_result_tuple(PLpgSQL_execstate *estate,
TupleDesc tupdesc);
static void plpgsql_exec_error_callback(void *arg);
! static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
static void push_stmt_mcontext(PLpgSQL_execstate *estate);
static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
--- 235,242 ----
static void coerce_function_result_tuple(PLpgSQL_execstate *estate,
TupleDesc tupdesc);
static void plpgsql_exec_error_callback(void *arg);
! static void copy_plpgsql_datums(PLpgSQL_execstate *estate,
! PLpgSQL_function *func);
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 *
*** 458,465 ****
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! for (i = 0; i < estate.ndatums; i++)
! estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
* Store the actual call argument values into the appropriate variables
--- 459,465 ----
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! copy_plpgsql_datums(&estate, func);
/*
* Store the actual call argument values into the appropriate variables
*************** plpgsql_exec_trigger(PLpgSQL_function *f
*** 859,866 ****
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! for (i = 0; i < estate.ndatums; i++)
! estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
* Put the OLD and NEW tuples into record variables
--- 859,865 ----
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! copy_plpgsql_datums(&estate, func);
/*
* Put the OLD and NEW tuples into record variables
*************** plpgsql_exec_event_trigger(PLpgSQL_funct
*** 1153,1159 ****
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
- int i;
int rc;
PLpgSQL_var *var;
--- 1152,1157 ----
*************** plpgsql_exec_event_trigger(PLpgSQL_funct
*** 1174,1181 ****
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! for (i = 0; i < estate.ndatums; i++)
! estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
/*
* Assign the special tg_ variables
--- 1172,1178 ----
* Make local execution copies of all the datums
*/
estate.err_text = gettext_noop("during initialization of execution state");
! copy_plpgsql_datums(&estate, func);
/*
* Assign the special tg_ variables
*************** plpgsql_exec_error_callback(void *arg)
*** 1290,1346 ****
* Support function for initializing local execution variables
* ----------
*/
! static PLpgSQL_datum *
! copy_plpgsql_datum(PLpgSQL_datum *datum)
{
! PLpgSQL_datum *result;
! switch (datum->dtype)
! {
! case PLPGSQL_DTYPE_VAR:
! {
! PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
! memcpy(new, datum, sizeof(PLpgSQL_var));
! /* should be preset to null/non-freeable */
! Assert(new->isnull);
! Assert(!new->freeval);
! result = (PLpgSQL_datum *) new;
! }
! break;
! case PLPGSQL_DTYPE_REC:
! {
! PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
! memcpy(new, datum, sizeof(PLpgSQL_rec));
! /* should be preset to empty */
! Assert(new->erh == NULL);
! result = (PLpgSQL_datum *) new;
! }
! break;
! case PLPGSQL_DTYPE_ROW:
! case PLPGSQL_DTYPE_RECFIELD:
! case PLPGSQL_DTYPE_ARRAYELEM:
! /*
! * These datum records are read-only at runtime, so no need to
! * copy them (well, RECFIELD and ARRAYELEM contain cached data,
! * but we'd just as soon centralize the caching anyway)
! */
! result = datum;
! break;
! default:
! elog(ERROR, "unrecognized dtype: %d", datum->dtype);
! result = NULL; /* keep compiler quiet */
! break;
}
! return result;
}
/*
--- 1287,1359 ----
* Support function for initializing local execution variables
* ----------
*/
! static void
! copy_plpgsql_datums(PLpgSQL_execstate *estate,
! PLpgSQL_function *func)
{
! int ndatums = estate->ndatums;
! PLpgSQL_datum **indatums;
! PLpgSQL_datum **outdatums;
! char *workspace;
! char *ws_next;
! int i;
! /* Allocate local datum-pointer array */
! estate->datums = (PLpgSQL_datum **)
! palloc(sizeof(PLpgSQL_datum *) * ndatums);
! /*
! * To reduce palloc overhead, we make a single palloc request for all the
! * space needed for locally-instantiated datums.
! */
! workspace = palloc(func->copiable_size);
! ws_next = workspace;
! /* Fill datum-pointer array, copying datums into workspace as needed */
! indatums = func->datums;
! outdatums = estate->datums;
! for (i = 0; i < ndatums; i++)
! {
! PLpgSQL_datum *indatum = indatums[i];
! PLpgSQL_datum *outdatum;
! /* This must agree with plpgsql_finish_datums on what is copiable */
! switch (indatum->dtype)
! {
! case PLPGSQL_DTYPE_VAR:
! outdatum = (PLpgSQL_datum *) ws_next;
! memcpy(outdatum, indatum, sizeof(PLpgSQL_var));
! ws_next += MAXALIGN(sizeof(PLpgSQL_var));
! break;
! case PLPGSQL_DTYPE_REC:
! outdatum = (PLpgSQL_datum *) ws_next;
! memcpy(outdatum, indatum, sizeof(PLpgSQL_rec));
! ws_next += MAXALIGN(sizeof(PLpgSQL_rec));
! break;
! case PLPGSQL_DTYPE_ROW:
! case PLPGSQL_DTYPE_RECFIELD:
! case PLPGSQL_DTYPE_ARRAYELEM:
! /*
! * These datum records are read-only at runtime, so no need to
! * copy them (well, RECFIELD and ARRAYELEM contain cached
! * data, but we'd just as soon centralize the caching anyway).
! */
! outdatum = indatum;
! break;
! default:
! elog(ERROR, "unrecognized dtype: %d", indatum->dtype);
! outdatum = NULL; /* keep compiler quiet */
! break;
! }
! outdatums[i] = outdatum;
}
! Assert(ws_next == workspace + func->copiable_size);
}
/*
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3504,3511 ****
estate->found_varno = func->found_varno;
estate->ndatums = func->ndatums;
! estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
! /* caller is expected to fill the datums array */
estate->datum_context = CurrentMemoryContext;
/* initialize our ParamListInfo with appropriate hook functions */
--- 3517,3524 ----
estate->found_varno = func->found_varno;
estate->ndatums = func->ndatums;
! estate->datums = NULL;
! /* the datums array will be filled by copy_plpgsql_datums() */
estate->datum_context = CurrentMemoryContext;
/* initialize our ParamListInfo with appropriate hook functions */
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index 97c0d4f..ee943ee 100644
*** a/src/pl/plpgsql/src/pl_gram.y
--- b/src/pl/plpgsql/src/pl_gram.y
*************** decl_statement : decl_varname decl_const
*** 564,570 ****
curname_def = palloc0(sizeof(PLpgSQL_expr));
- curname_def->dtype = PLPGSQL_DTYPE_EXPR;
strcpy(buf, "SELECT ");
cp1 = new->refname;
cp2 = buf + strlen(buf);
--- 564,569 ----
*************** read_sql_construct(int until,
*** 2697,2703 ****
}
expr = palloc0(sizeof(PLpgSQL_expr));
- expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->paramnos = NULL;
--- 2696,2701 ----
*************** make_execsql_stmt(int firsttoken, int lo
*** 2944,2950 ****
ds.data[--ds.len] = '\0';
expr = palloc0(sizeof(PLpgSQL_expr));
- expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->paramnos = NULL;
--- 2942,2947 ----
*************** read_cursor_args(PLpgSQL_var *cursor, in
*** 3816,3822 ****
appendStringInfoChar(&ds, ';');
expr = palloc0(sizeof(PLpgSQL_expr));
- expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->paramnos = NULL;
--- 3813,3818 ----
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index d4eb67b..dadbfb5 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef enum PLpgSQL_datum_type
*** 63,70 ****
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
! PLPGSQL_DTYPE_ARRAYELEM,
! PLPGSQL_DTYPE_EXPR
} PLpgSQL_datum_type;
/*
--- 63,69 ----
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
! PLPGSQL_DTYPE_ARRAYELEM
} PLpgSQL_datum_type;
/*
*************** typedef struct PLpgSQL_type
*** 189,226 ****
} PLpgSQL_type;
/*
- * Generic datum array item
- *
- * PLpgSQL_datum is the common supertype for PLpgSQL_expr, PLpgSQL_var,
- * PLpgSQL_row, PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem
- */
- typedef struct PLpgSQL_datum
- {
- PLpgSQL_datum_type dtype;
- int dno;
- } PLpgSQL_datum;
-
- /*
- * Scalar or composite variable
- *
- * The variants PLpgSQL_var, PLpgSQL_row, and PLpgSQL_rec share these
- * fields
- */
- typedef struct PLpgSQL_variable
- {
- PLpgSQL_datum_type dtype;
- int dno;
- char *refname;
- int lineno;
- } PLpgSQL_variable;
-
- /*
* SQL Query to plan and execute
*/
typedef struct PLpgSQL_expr
{
- PLpgSQL_datum_type dtype;
- int dno;
char *query;
SPIPlanPtr plan;
Bitmapset *paramnos; /* all dnos referenced by this query */
--- 188,197 ----
*************** typedef struct PLpgSQL_expr
*** 250,255 ****
--- 221,252 ----
} PLpgSQL_expr;
/*
+ * Generic datum array item
+ *
+ * PLpgSQL_datum is the common supertype for PLpgSQL_var, PLpgSQL_row,
+ * PLpgSQL_rec, PLpgSQL_recfield, and PLpgSQL_arrayelem.
+ */
+ typedef struct PLpgSQL_datum
+ {
+ PLpgSQL_datum_type dtype;
+ int dno;
+ } PLpgSQL_datum;
+
+ /*
+ * Scalar or composite variable
+ *
+ * The variants PLpgSQL_var, PLpgSQL_row, and PLpgSQL_rec share these
+ * fields.
+ */
+ typedef struct PLpgSQL_variable
+ {
+ PLpgSQL_datum_type dtype;
+ int dno;
+ char *refname;
+ int lineno;
+ } PLpgSQL_variable;
+
+ /*
* Scalar variable
*/
typedef struct PLpgSQL_var
*************** typedef struct PLpgSQL_var
*** 258,268 ****
int dno;
char *refname;
int lineno;
PLpgSQL_type *datatype;
- int isconst;
- int notnull;
PLpgSQL_expr *default_val;
PLpgSQL_expr *cursor_explicit_expr;
int cursor_explicit_argrow;
int cursor_options;
--- 255,272 ----
int dno;
char *refname;
int lineno;
+ /* end of PLpgSQL_variable fields */
+ bool isconst;
+ bool notnull;
PLpgSQL_type *datatype;
PLpgSQL_expr *default_val;
+
+ /*
+ * Variables declared as CURSOR FOR <query> are mostly like ordinary
+ * scalar variables of type refcursor, but they have these additional
+ * properties:
+ */
PLpgSQL_expr *cursor_explicit_expr;
int cursor_explicit_argrow;
int cursor_options;
*************** typedef struct PLpgSQL_row
*** 286,291 ****
--- 290,296 ----
int dno;
char *refname;
int lineno;
+ /* end of PLpgSQL_variable fields */
/*
* rowtupdesc is only set up if we might need to convert the row into a
*************** typedef struct PLpgSQL_rec
*** 308,313 ****
--- 313,320 ----
int dno;
char *refname;
int lineno;
+ /* end of PLpgSQL_variable fields */
+
Oid rectypeid; /* declared type of variable */
/* RECFIELDs for this record are chained together for easy access */
int firstfield; /* dno of first RECFIELD, or -1 if none */
*************** typedef struct PLpgSQL_recfield
*** 322,327 ****
--- 329,336 ----
{
PLpgSQL_datum_type dtype;
int dno;
+ /* end of PLpgSQL_datum fields */
+
char *fieldname; /* name of field */
int recparentno; /* dno of parent record */
int nextfield; /* dno of next child, or -1 if none */
*************** typedef struct PLpgSQL_arrayelem
*** 337,342 ****
--- 346,353 ----
{
PLpgSQL_datum_type dtype;
int dno;
+ /* end of PLpgSQL_datum fields */
+
PLpgSQL_expr *subscript;
int arrayparentno; /* dno of parent array variable */
*************** typedef struct PLpgSQL_function
*** 884,889 ****
--- 895,901 ----
/* the datums representing the function's local variables */
int ndatums;
PLpgSQL_datum **datums;
+ Size copiable_size; /* space for locally instantiated datums */
/* function body parsetree */
PLpgSQL_stmt_block *action;
*************** typedef struct PLpgSQL_execstate
*** 920,927 ****
ResourceOwner tuple_store_owner;
ReturnSetInfo *rsi;
- /* the datums representing the function's local variables */
int found_varno;
int ndatums;
PLpgSQL_datum **datums;
/* context containing variable values (same as func's SPI_proc context) */
--- 932,945 ----
ResourceOwner tuple_store_owner;
ReturnSetInfo *rsi;
int found_varno;
+
+ /*
+ * The datums representing the function's local variables. Some of these
+ * are local storage in this execstate, but some just point to the shared
+ * copy belonging to the PLpgSQL_function, depending on whether or not we
+ * need any per-execution state for the datum's dtype.
+ */
int ndatums;
PLpgSQL_datum **datums;
/* context containing variable values (same as func's SPI_proc context) */
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 */