Re: plpgsql function startup-time improvements - Mailing list pgsql-hackers
From | Tom Lane |
---|---|
Subject | Re: plpgsql function startup-time improvements |
Date | |
Msg-id | 8376.1516835784@sss.pgh.pa.us Whole thread Raw |
In response to | Re: plpgsql function startup-time improvements (Pavel Stehule <pavel.stehule@gmail.com>) |
Responses |
Re: plpgsql function startup-time improvements
|
List | pgsql-hackers |
Pavel Stehule <pavel.stehule@gmail.com> 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/833.1516834367@sss.pgh.pa.us 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 */
pgsql-hackers by date: