Commit 1c1cbe27 authored by Tom Lane's avatar Tom Lane

Rethink the "read/write parameter" mechanism in pl/pgsql.

Performance issues with the preceding patch to re-implement array
element assignment within pl/pgsql led me to realize that the read/write
parameter mechanism is misdesigned.  Instead of requiring the assignment
source expression to be such that *all* its references to the target
variable could be passed as R/W, we really want to identify *one*
reference to the target variable to be passed as R/W, allowing any other
ones to be passed read/only as they would be by default.  As long as the
R/W reference is a direct argument to the top-level (hence last to be
executed) function in the expression, there is no harm in R/O references
being passed to other lower parts of the expression.  Nor is there any
use-case for more than one argument of the top-level function being R/W.

Hence, rewrite that logic to identify one single Param that references
the target variable, and make only that Param pass a read/write
reference, not any other Params referencing the target variable.

Discussion: https://postgr.es/m/4165684.1607707277@sss.pgh.pa.us
parent 1788828d
...@@ -2582,8 +2582,11 @@ array_set_element_expanded(Datum arraydatum, ...@@ -2582,8 +2582,11 @@ array_set_element_expanded(Datum arraydatum,
/* /*
* Copy new element into array's context, if needed (we assume it's * Copy new element into array's context, if needed (we assume it's
* already detoasted, so no junk should be created). If we fail further * already detoasted, so no junk should be created). Doing this before
* down, this memory is leaked, but that's reasonably harmless. * we've made any significant changes ensures that our behavior is sane
* even when the source is a reference to some element of this same array.
* If we fail further down, this memory is leaked, but that's reasonably
* harmless.
*/ */
if (!eah->typbyval && !isNull) if (!eah->typbyval && !isNull)
{ {
......
This diff is collapsed.
...@@ -2820,7 +2820,7 @@ read_sql_construct(int until, ...@@ -2820,7 +2820,7 @@ read_sql_construct(int until,
expr->parseMode = parsemode; expr->parseMode = parsemode;
expr->plan = NULL; expr->plan = NULL;
expr->paramnos = NULL; expr->paramnos = NULL;
expr->rwparam = -1; expr->target_param = -1;
expr->ns = plpgsql_ns_top(); expr->ns = plpgsql_ns_top();
pfree(ds.data); pfree(ds.data);
...@@ -3067,7 +3067,7 @@ make_execsql_stmt(int firsttoken, int location) ...@@ -3067,7 +3067,7 @@ make_execsql_stmt(int firsttoken, int location)
expr->parseMode = RAW_PARSE_DEFAULT; expr->parseMode = RAW_PARSE_DEFAULT;
expr->plan = NULL; expr->plan = NULL;
expr->paramnos = NULL; expr->paramnos = NULL;
expr->rwparam = -1; expr->target_param = -1;
expr->ns = plpgsql_ns_top(); expr->ns = plpgsql_ns_top();
pfree(ds.data); pfree(ds.data);
...@@ -3949,7 +3949,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until) ...@@ -3949,7 +3949,7 @@ read_cursor_args(PLpgSQL_var *cursor, int until)
expr->parseMode = RAW_PARSE_PLPGSQL_EXPR; expr->parseMode = RAW_PARSE_PLPGSQL_EXPR;
expr->plan = NULL; expr->plan = NULL;
expr->paramnos = NULL; expr->paramnos = NULL;
expr->rwparam = -1; expr->target_param = -1;
expr->ns = plpgsql_ns_top(); expr->ns = plpgsql_ns_top();
pfree(ds.data); pfree(ds.data);
......
...@@ -221,7 +221,6 @@ typedef struct PLpgSQL_expr ...@@ -221,7 +221,6 @@ typedef struct PLpgSQL_expr
RawParseMode parseMode; /* raw_parser() mode to use */ RawParseMode parseMode; /* raw_parser() mode to use */
SPIPlanPtr plan; /* plan, or NULL if not made yet */ SPIPlanPtr plan; /* plan, or NULL if not made yet */
Bitmapset *paramnos; /* all dnos referenced by this query */ Bitmapset *paramnos; /* all dnos referenced by this query */
int rwparam; /* dno of read/write param, or -1 if none */
/* function containing this expr (not set until we first parse query) */ /* function containing this expr (not set until we first parse query) */
struct PLpgSQL_function *func; struct PLpgSQL_function *func;
...@@ -235,6 +234,17 @@ typedef struct PLpgSQL_expr ...@@ -235,6 +234,17 @@ typedef struct PLpgSQL_expr
int32 expr_simple_typmod; /* result typmod, if simple */ int32 expr_simple_typmod; /* result typmod, if simple */
bool expr_simple_mutable; /* true if simple expr is mutable */ bool expr_simple_mutable; /* true if simple expr is mutable */
/*
* These fields are used to optimize assignments to expanded-datum
* variables. If this expression is the source of an assignment to a
* simple variable, target_param holds that variable's dno; else it's -1.
* If we match a Param within expr_simple_expr to such a variable, that
* Param's address is stored in expr_rw_param; then expression code
* generation will allow the value for that Param to be passed read/write.
*/
int target_param; /* dno of assign target, or -1 if none */
Param *expr_rw_param; /* read/write Param within expr, if any */
/* /*
* If the expression was ever determined to be simple, we remember its * If the expression was ever determined to be simple, we remember its
* CachedPlanSource and CachedPlan here. If expr_simple_plan_lxid matches * CachedPlanSource and CachedPlan here. If expr_simple_plan_lxid matches
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment