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 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);
* 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]);
+ copy_plpgsql_datums(&estate, func);
/*
* Store the actual call argument values into the appropriate variables
* 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]);
+ copy_plpgsql_datums(&estate, func);
/*
* Put the OLD and NEW tuples into record variables
{
PLpgSQL_execstate estate;
ErrorContextCallback plerrcontext;
- int i;
int rc;
PLpgSQL_var *var;
* 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]);
+ copy_plpgsql_datums(&estate, func);
/*
* Assign the special tg_ variables
* Support function for initializing local execution variables
* ----------
*/
-static PLpgSQL_datum *
-copy_plpgsql_datum(PLpgSQL_datum *datum)
+static void
+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)
- {
- case PLPGSQL_DTYPE_VAR:
- {
- PLpgSQL_var *new = palloc(sizeof(PLpgSQL_var));
+ /* Allocate local datum-pointer array */
+ estate->datums = (PLpgSQL_datum **)
+ palloc(sizeof(PLpgSQL_datum *) * ndatums);
- memcpy(new, datum, sizeof(PLpgSQL_var));
- /* should be preset to null/non-freeable */
- Assert(new->isnull);
- Assert(!new->freeval);
+ /*
+ * 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;
- result = (PLpgSQL_datum *) new;
- }
- break;
+ /* 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;
- case PLPGSQL_DTYPE_REC:
- {
- PLpgSQL_rec *new = palloc(sizeof(PLpgSQL_rec));
+ /* 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;
- memcpy(new, datum, sizeof(PLpgSQL_rec));
- /* should be preset to empty */
- Assert(new->erh == NULL);
+ case PLPGSQL_DTYPE_REC:
+ outdatum = (PLpgSQL_datum *) ws_next;
+ memcpy(outdatum, indatum, sizeof(PLpgSQL_rec));
+ ws_next += MAXALIGN(sizeof(PLpgSQL_rec));
+ break;
- result = (PLpgSQL_datum *) new;
- }
- break;
+ case PLPGSQL_DTYPE_ROW:
+ case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ARRAYELEM:
- 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;
- /*
- * 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", indatum->dtype);
+ outdatum = NULL; /* keep compiler quiet */
+ break;
+ }
- default:
- elog(ERROR, "unrecognized dtype: %d", datum->dtype);
- result = NULL; /* keep compiler quiet */
- break;
+ outdatums[i] = outdatum;
}
- return result;
+ Assert(ws_next == workspace + func->copiable_size);
}
/*
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->datums = NULL;
+ /* the datums array will be filled by copy_plpgsql_datums() */
estate->datum_context = CurrentMemoryContext;
/* initialize our ParamListInfo with appropriate hook functions */
PLPGSQL_DTYPE_ROW,
PLPGSQL_DTYPE_REC,
PLPGSQL_DTYPE_RECFIELD,
- PLPGSQL_DTYPE_ARRAYELEM,
- PLPGSQL_DTYPE_EXPR
+ PLPGSQL_DTYPE_ARRAYELEM
} PLpgSQL_datum_type;
/*
int32 atttypmod; /* typmod (taken from someplace else) */
} 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 */
LocalTransactionId expr_simple_lxid;
} 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
*/
int dno;
char *refname;
int lineno;
+ /* end of PLpgSQL_variable fields */
+ bool isconst;
+ bool notnull;
PLpgSQL_type *datatype;
- int isconst;
- int notnull;
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;
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
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 */
{
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 */
{
PLpgSQL_datum_type dtype;
int dno;
+ /* end of PLpgSQL_datum fields */
+
PLpgSQL_expr *subscript;
int arrayparentno; /* dno of parent array variable */
/* 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;
ResourceOwner tuple_store_owner;
ReturnSetInfo *rsi;
- /* the datums representing the function's local variables */
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) */