mirror of
https://github.com/postgres/postgres.git
synced 2025-05-21 15:54:08 +03:00
Speed up plpgsql function startup by doing fewer pallocs.
Previously, copy_plpgsql_datum did a separate palloc for each variable needing instance-local storage. In simple benchmarks this made for a noticeable fraction of the total runtime. Improve it by precalculating the space needed for all of a function's variables and doing just one palloc for all of them. In passing, remove PLPGSQL_DTYPE_EXPR from the list of plpgsql "datum" types, since in fact it has nothing in common with the others, and there is noplace that needs to discriminate on the basis of dtype between an expression and any type of datum. And add comments clarifying which datum struct fields are generic and which aren't. Tom Lane, reviewed by Pavel Stehule Discussion: https://postgr.es/m/11986.1514407114@sss.pgh.pa.us
This commit is contained in:
parent
4b93f57999
commit
40301c1c8b
@ -2183,12 +2183,29 @@ plpgsql_adddatum(PLpgSQL_datum *new)
|
|||||||
static void
|
static void
|
||||||
plpgsql_finish_datums(PLpgSQL_function *function)
|
plpgsql_finish_datums(PLpgSQL_function *function)
|
||||||
{
|
{
|
||||||
|
Size copiable_size = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
function->ndatums = plpgsql_nDatums;
|
function->ndatums = plpgsql_nDatums;
|
||||||
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
|
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
|
||||||
for (i = 0; i < plpgsql_nDatums; i++)
|
for (i = 0; i < plpgsql_nDatums; i++)
|
||||||
|
{
|
||||||
function->datums[i] = plpgsql_Datums[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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,7 +235,8 @@ static HTAB *shared_cast_hash = NULL;
|
|||||||
static void coerce_function_result_tuple(PLpgSQL_execstate *estate,
|
static void coerce_function_result_tuple(PLpgSQL_execstate *estate,
|
||||||
TupleDesc tupdesc);
|
TupleDesc tupdesc);
|
||||||
static void plpgsql_exec_error_callback(void *arg);
|
static void plpgsql_exec_error_callback(void *arg);
|
||||||
static PLpgSQL_datum *copy_plpgsql_datum(PLpgSQL_datum *datum);
|
static void copy_plpgsql_datums(PLpgSQL_execstate *estate,
|
||||||
|
PLpgSQL_function *func);
|
||||||
static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
|
static MemoryContext get_stmt_mcontext(PLpgSQL_execstate *estate);
|
||||||
static void push_stmt_mcontext(PLpgSQL_execstate *estate);
|
static void push_stmt_mcontext(PLpgSQL_execstate *estate);
|
||||||
static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
|
static void pop_stmt_mcontext(PLpgSQL_execstate *estate);
|
||||||
@ -458,8 +459,7 @@ plpgsql_exec_function(PLpgSQL_function *func, FunctionCallInfo fcinfo,
|
|||||||
* Make local execution copies of all the datums
|
* Make local execution copies of all the datums
|
||||||
*/
|
*/
|
||||||
estate.err_text = gettext_noop("during initialization of execution state");
|
estate.err_text = gettext_noop("during initialization of execution state");
|
||||||
for (i = 0; i < estate.ndatums; i++)
|
copy_plpgsql_datums(&estate, func);
|
||||||
estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Store the actual call argument values into the appropriate variables
|
* Store the actual call argument values into the appropriate variables
|
||||||
@ -859,8 +859,7 @@ plpgsql_exec_trigger(PLpgSQL_function *func,
|
|||||||
* Make local execution copies of all the datums
|
* Make local execution copies of all the datums
|
||||||
*/
|
*/
|
||||||
estate.err_text = gettext_noop("during initialization of execution state");
|
estate.err_text = gettext_noop("during initialization of execution state");
|
||||||
for (i = 0; i < estate.ndatums; i++)
|
copy_plpgsql_datums(&estate, func);
|
||||||
estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Put the OLD and NEW tuples into record variables
|
* Put the OLD and NEW tuples into record variables
|
||||||
@ -1153,7 +1152,6 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
|
|||||||
{
|
{
|
||||||
PLpgSQL_execstate estate;
|
PLpgSQL_execstate estate;
|
||||||
ErrorContextCallback plerrcontext;
|
ErrorContextCallback plerrcontext;
|
||||||
int i;
|
|
||||||
int rc;
|
int rc;
|
||||||
PLpgSQL_var *var;
|
PLpgSQL_var *var;
|
||||||
|
|
||||||
@ -1174,8 +1172,7 @@ plpgsql_exec_event_trigger(PLpgSQL_function *func, EventTriggerData *trigdata)
|
|||||||
* Make local execution copies of all the datums
|
* Make local execution copies of all the datums
|
||||||
*/
|
*/
|
||||||
estate.err_text = gettext_noop("during initialization of execution state");
|
estate.err_text = gettext_noop("during initialization of execution state");
|
||||||
for (i = 0; i < estate.ndatums; i++)
|
copy_plpgsql_datums(&estate, func);
|
||||||
estate.datums[i] = copy_plpgsql_datum(func->datums[i]);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Assign the special tg_ variables
|
* Assign the special tg_ variables
|
||||||
@ -1290,57 +1287,73 @@ plpgsql_exec_error_callback(void *arg)
|
|||||||
* Support function for initializing local execution variables
|
* Support function for initializing local execution variables
|
||||||
* ----------
|
* ----------
|
||||||
*/
|
*/
|
||||||
static PLpgSQL_datum *
|
static void
|
||||||
copy_plpgsql_datum(PLpgSQL_datum *datum)
|
copy_plpgsql_datums(PLpgSQL_execstate *estate,
|
||||||
|
PLpgSQL_function *func)
|
||||||
{
|
{
|
||||||
PLpgSQL_datum *result;
|
int ndatums = estate->ndatums;
|
||||||
|
PLpgSQL_datum **indatums;
|
||||||
|
PLpgSQL_datum **outdatums;
|
||||||
|
char *workspace;
|
||||||
|
char *ws_next;
|
||||||
|
int i;
|
||||||
|
|
||||||
switch (datum->dtype)
|
/* 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++)
|
||||||
{
|
{
|
||||||
case PLPGSQL_DTYPE_VAR:
|
PLpgSQL_datum *indatum = indatums[i];
|
||||||
{
|
PLpgSQL_datum *outdatum;
|
||||||
PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
|
|
||||||
|
|
||||||
memcpy(new, datum, sizeof(PLpgSQL_var));
|
/* This must agree with plpgsql_finish_datums on what is copiable */
|
||||||
/* should be preset to null/non-freeable */
|
switch (indatum->dtype)
|
||||||
Assert(new->isnull);
|
{
|
||||||
Assert(!new->freeval);
|
case PLPGSQL_DTYPE_VAR:
|
||||||
|
outdatum = (PLpgSQL_datum *) ws_next;
|
||||||
|
memcpy(outdatum, indatum, sizeof(PLpgSQL_var));
|
||||||
|
ws_next += MAXALIGN(sizeof(PLpgSQL_var));
|
||||||
|
break;
|
||||||
|
|
||||||
result = (PLpgSQL_datum *) new;
|
case PLPGSQL_DTYPE_REC:
|
||||||
}
|
outdatum = (PLpgSQL_datum *) ws_next;
|
||||||
break;
|
memcpy(outdatum, indatum, sizeof(PLpgSQL_rec));
|
||||||
|
ws_next += MAXALIGN(sizeof(PLpgSQL_rec));
|
||||||
|
break;
|
||||||
|
|
||||||
case PLPGSQL_DTYPE_REC:
|
case PLPGSQL_DTYPE_ROW:
|
||||||
{
|
case PLPGSQL_DTYPE_RECFIELD:
|
||||||
PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
|
case PLPGSQL_DTYPE_ARRAYELEM:
|
||||||
|
|
||||||
memcpy(new, datum, sizeof(PLpgSQL_rec));
|
/*
|
||||||
/* should be preset to empty */
|
* These datum records are read-only at runtime, so no need to
|
||||||
Assert(new->erh == NULL);
|
* copy them (well, RECFIELD and ARRAYELEM contain cached
|
||||||
|
* data, but we'd just as soon centralize the caching anyway).
|
||||||
|
*/
|
||||||
|
outdatum = indatum;
|
||||||
|
break;
|
||||||
|
|
||||||
result = (PLpgSQL_datum *) new;
|
default:
|
||||||
}
|
elog(ERROR, "unrecognized dtype: %d", indatum->dtype);
|
||||||
break;
|
outdatum = NULL; /* keep compiler quiet */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case PLPGSQL_DTYPE_ROW:
|
outdatums[i] = outdatum;
|
||||||
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;
|
Assert(ws_next == workspace + func->copiable_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3504,8 +3517,8 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
|
|||||||
|
|
||||||
estate->found_varno = func->found_varno;
|
estate->found_varno = func->found_varno;
|
||||||
estate->ndatums = func->ndatums;
|
estate->ndatums = func->ndatums;
|
||||||
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
|
estate->datums = NULL;
|
||||||
/* caller is expected to fill the datums array */
|
/* the datums array will be filled by copy_plpgsql_datums() */
|
||||||
estate->datum_context = CurrentMemoryContext;
|
estate->datum_context = CurrentMemoryContext;
|
||||||
|
|
||||||
/* initialize our ParamListInfo with appropriate hook functions */
|
/* initialize our ParamListInfo with appropriate hook functions */
|
||||||
|
@ -564,7 +564,6 @@ decl_statement : decl_varname decl_const decl_datatype decl_collate decl_notnull
|
|||||||
|
|
||||||
curname_def = palloc0(sizeof(PLpgSQL_expr));
|
curname_def = palloc0(sizeof(PLpgSQL_expr));
|
||||||
|
|
||||||
curname_def->dtype = PLPGSQL_DTYPE_EXPR;
|
|
||||||
strcpy(buf, "SELECT ");
|
strcpy(buf, "SELECT ");
|
||||||
cp1 = new->refname;
|
cp1 = new->refname;
|
||||||
cp2 = buf + strlen(buf);
|
cp2 = buf + strlen(buf);
|
||||||
@ -2697,7 +2696,6 @@ read_sql_construct(int until,
|
|||||||
}
|
}
|
||||||
|
|
||||||
expr = palloc0(sizeof(PLpgSQL_expr));
|
expr = palloc0(sizeof(PLpgSQL_expr));
|
||||||
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
|
||||||
expr->query = pstrdup(ds.data);
|
expr->query = pstrdup(ds.data);
|
||||||
expr->plan = NULL;
|
expr->plan = NULL;
|
||||||
expr->paramnos = NULL;
|
expr->paramnos = NULL;
|
||||||
@ -2944,7 +2942,6 @@ make_execsql_stmt(int firsttoken, int location)
|
|||||||
ds.data[--ds.len] = '\0';
|
ds.data[--ds.len] = '\0';
|
||||||
|
|
||||||
expr = palloc0(sizeof(PLpgSQL_expr));
|
expr = palloc0(sizeof(PLpgSQL_expr));
|
||||||
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
|
||||||
expr->query = pstrdup(ds.data);
|
expr->query = pstrdup(ds.data);
|
||||||
expr->plan = NULL;
|
expr->plan = NULL;
|
||||||
expr->paramnos = NULL;
|
expr->paramnos = NULL;
|
||||||
@ -3816,7 +3813,6 @@ read_cursor_args(PLpgSQL_var *cursor, int until, const char *expected)
|
|||||||
appendStringInfoChar(&ds, ';');
|
appendStringInfoChar(&ds, ';');
|
||||||
|
|
||||||
expr = palloc0(sizeof(PLpgSQL_expr));
|
expr = palloc0(sizeof(PLpgSQL_expr));
|
||||||
expr->dtype = PLPGSQL_DTYPE_EXPR;
|
|
||||||
expr->query = pstrdup(ds.data);
|
expr->query = pstrdup(ds.data);
|
||||||
expr->plan = NULL;
|
expr->plan = NULL;
|
||||||
expr->paramnos = NULL;
|
expr->paramnos = NULL;
|
||||||
|
@ -63,8 +63,7 @@ typedef enum PLpgSQL_datum_type
|
|||||||
PLPGSQL_DTYPE_ROW,
|
PLPGSQL_DTYPE_ROW,
|
||||||
PLPGSQL_DTYPE_REC,
|
PLPGSQL_DTYPE_REC,
|
||||||
PLPGSQL_DTYPE_RECFIELD,
|
PLPGSQL_DTYPE_RECFIELD,
|
||||||
PLPGSQL_DTYPE_ARRAYELEM,
|
PLPGSQL_DTYPE_ARRAYELEM
|
||||||
PLPGSQL_DTYPE_EXPR
|
|
||||||
} PLpgSQL_datum_type;
|
} PLpgSQL_datum_type;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -188,39 +187,11 @@ typedef struct PLpgSQL_type
|
|||||||
int32 atttypmod; /* typmod (taken from someplace else) */
|
int32 atttypmod; /* typmod (taken from someplace else) */
|
||||||
} PLpgSQL_type;
|
} 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
|
* SQL Query to plan and execute
|
||||||
*/
|
*/
|
||||||
typedef struct PLpgSQL_expr
|
typedef struct PLpgSQL_expr
|
||||||
{
|
{
|
||||||
PLpgSQL_datum_type dtype;
|
|
||||||
int dno;
|
|
||||||
char *query;
|
char *query;
|
||||||
SPIPlanPtr plan;
|
SPIPlanPtr plan;
|
||||||
Bitmapset *paramnos; /* all dnos referenced by this query */
|
Bitmapset *paramnos; /* all dnos referenced by this query */
|
||||||
@ -249,6 +220,32 @@ typedef struct PLpgSQL_expr
|
|||||||
LocalTransactionId expr_simple_lxid;
|
LocalTransactionId expr_simple_lxid;
|
||||||
} PLpgSQL_expr;
|
} 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
|
* Scalar variable
|
||||||
*/
|
*/
|
||||||
@ -258,11 +255,18 @@ typedef struct PLpgSQL_var
|
|||||||
int dno;
|
int dno;
|
||||||
char *refname;
|
char *refname;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
/* end of PLpgSQL_variable fields */
|
||||||
|
|
||||||
|
bool isconst;
|
||||||
|
bool notnull;
|
||||||
PLpgSQL_type *datatype;
|
PLpgSQL_type *datatype;
|
||||||
int isconst;
|
|
||||||
int notnull;
|
|
||||||
PLpgSQL_expr *default_val;
|
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;
|
PLpgSQL_expr *cursor_explicit_expr;
|
||||||
int cursor_explicit_argrow;
|
int cursor_explicit_argrow;
|
||||||
int cursor_options;
|
int cursor_options;
|
||||||
@ -286,6 +290,7 @@ typedef struct PLpgSQL_row
|
|||||||
int dno;
|
int dno;
|
||||||
char *refname;
|
char *refname;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
/* end of PLpgSQL_variable fields */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rowtupdesc is only set up if we might need to convert the row into a
|
* rowtupdesc is only set up if we might need to convert the row into a
|
||||||
@ -308,6 +313,8 @@ typedef struct PLpgSQL_rec
|
|||||||
int dno;
|
int dno;
|
||||||
char *refname;
|
char *refname;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
/* end of PLpgSQL_variable fields */
|
||||||
|
|
||||||
Oid rectypeid; /* declared type of variable */
|
Oid rectypeid; /* declared type of variable */
|
||||||
/* RECFIELDs for this record are chained together for easy access */
|
/* RECFIELDs for this record are chained together for easy access */
|
||||||
int firstfield; /* dno of first RECFIELD, or -1 if none */
|
int firstfield; /* dno of first RECFIELD, or -1 if none */
|
||||||
@ -322,6 +329,8 @@ typedef struct PLpgSQL_recfield
|
|||||||
{
|
{
|
||||||
PLpgSQL_datum_type dtype;
|
PLpgSQL_datum_type dtype;
|
||||||
int dno;
|
int dno;
|
||||||
|
/* end of PLpgSQL_datum fields */
|
||||||
|
|
||||||
char *fieldname; /* name of field */
|
char *fieldname; /* name of field */
|
||||||
int recparentno; /* dno of parent record */
|
int recparentno; /* dno of parent record */
|
||||||
int nextfield; /* dno of next child, or -1 if none */
|
int nextfield; /* dno of next child, or -1 if none */
|
||||||
@ -337,6 +346,8 @@ typedef struct PLpgSQL_arrayelem
|
|||||||
{
|
{
|
||||||
PLpgSQL_datum_type dtype;
|
PLpgSQL_datum_type dtype;
|
||||||
int dno;
|
int dno;
|
||||||
|
/* end of PLpgSQL_datum fields */
|
||||||
|
|
||||||
PLpgSQL_expr *subscript;
|
PLpgSQL_expr *subscript;
|
||||||
int arrayparentno; /* dno of parent array variable */
|
int arrayparentno; /* dno of parent array variable */
|
||||||
|
|
||||||
@ -884,6 +895,7 @@ typedef struct PLpgSQL_function
|
|||||||
/* the datums representing the function's local variables */
|
/* the datums representing the function's local variables */
|
||||||
int ndatums;
|
int ndatums;
|
||||||
PLpgSQL_datum **datums;
|
PLpgSQL_datum **datums;
|
||||||
|
Size copiable_size; /* space for locally instantiated datums */
|
||||||
|
|
||||||
/* function body parsetree */
|
/* function body parsetree */
|
||||||
PLpgSQL_stmt_block *action;
|
PLpgSQL_stmt_block *action;
|
||||||
@ -920,8 +932,14 @@ typedef struct PLpgSQL_execstate
|
|||||||
ResourceOwner tuple_store_owner;
|
ResourceOwner tuple_store_owner;
|
||||||
ReturnSetInfo *rsi;
|
ReturnSetInfo *rsi;
|
||||||
|
|
||||||
/* the datums representing the function's local variables */
|
|
||||||
int found_varno;
|
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;
|
int ndatums;
|
||||||
PLpgSQL_datum **datums;
|
PLpgSQL_datum **datums;
|
||||||
/* context containing variable values (same as func's SPI_proc context) */
|
/* context containing variable values (same as func's SPI_proc context) */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user