@@ -339,6 +339,7 @@ static void exec_eval_cleanup(PLpgSQL_execstate *estate);
339
339
static void exec_prepare_plan (PLpgSQL_execstate * estate ,
340
340
PLpgSQL_expr * expr , int cursorOptions );
341
341
static void exec_simple_check_plan (PLpgSQL_execstate * estate , PLpgSQL_expr * expr );
342
+ static bool exec_is_simple_query (PLpgSQL_expr * expr );
342
343
static void exec_save_simple_expr (PLpgSQL_expr * expr , CachedPlan * cplan );
343
344
static void exec_check_rw_parameter (PLpgSQL_expr * expr );
344
345
static void exec_check_assignable (PLpgSQL_execstate * estate , int dno );
@@ -6094,12 +6095,18 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6094
6095
* release it, so we don't leak plans intra-transaction.
6095
6096
*/
6096
6097
if (expr -> expr_simple_plan_lxid == curlxid )
6097
- {
6098
6098
ReleaseCachedPlan (expr -> expr_simple_plan ,
6099
6099
estate -> simple_eval_resowner );
6100
- expr -> expr_simple_plan = NULL ;
6101
- expr -> expr_simple_plan_lxid = InvalidLocalTransactionId ;
6102
- }
6100
+
6101
+ /*
6102
+ * Reset to "not simple" to leave sane state (with no dangling
6103
+ * pointers) in case we fail while replanning. expr_simple_plansource
6104
+ * can be left alone however, as that cannot move.
6105
+ */
6106
+ expr -> expr_simple_expr = NULL ;
6107
+ expr -> expr_rw_param = NULL ;
6108
+ expr -> expr_simple_plan = NULL ;
6109
+ expr -> expr_simple_plan_lxid = InvalidLocalTransactionId ;
6103
6110
6104
6111
/* Do the replanning work in the eval_mcontext */
6105
6112
oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
@@ -6115,11 +6122,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6115
6122
Assert (cplan != NULL );
6116
6123
6117
6124
/*
6118
- * This test probably can't fail either, but if it does, cope by
6119
- * declaring the plan to be non-simple. On success, we'll acquire a
6120
- * refcount on the new plan, stored in simple_eval_resowner.
6125
+ * Recheck exec_is_simple_query, which could now report false in
6126
+ * edge-case scenarios such as a non-SRF having been replaced with a
6127
+ * SRF. Also recheck CachedPlanAllowsSimpleValidityCheck, just to be
6128
+ * sure. If either test fails, cope by declaring the plan to be
6129
+ * non-simple. On success, we'll acquire a refcount on the new plan,
6130
+ * stored in simple_eval_resowner.
6121
6131
*/
6122
- if (CachedPlanAllowsSimpleValidityCheck (expr -> expr_simple_plansource ,
6132
+ if (exec_is_simple_query (expr ) &&
6133
+ CachedPlanAllowsSimpleValidityCheck (expr -> expr_simple_plansource ,
6123
6134
cplan ,
6124
6135
estate -> simple_eval_resowner ))
6125
6136
{
@@ -6131,9 +6142,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6131
6142
{
6132
6143
/* Release SPI_plan_get_cached_plan's refcount */
6133
6144
ReleaseCachedPlan (cplan , CurrentResourceOwner );
6134
- /* Mark expression as non-simple, and fail */
6135
- expr -> expr_simple_expr = NULL ;
6136
- expr -> expr_rw_param = NULL ;
6137
6145
return false;
6138
6146
}
6139
6147
@@ -7974,7 +7982,6 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
7974
7982
{
7975
7983
List * plansources ;
7976
7984
CachedPlanSource * plansource ;
7977
- Query * query ;
7978
7985
CachedPlan * cplan ;
7979
7986
MemoryContext oldcontext ;
7980
7987
@@ -7990,31 +7997,88 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
7990
7997
* called immediately after creating the CachedPlanSource, we need not
7991
7998
* worry about the query being stale.
7992
7999
*/
8000
+ if (!exec_is_simple_query (expr ))
8001
+ return ;
8002
+
8003
+ /* exec_is_simple_query verified that there's just one CachedPlanSource */
8004
+ plansources = SPI_plan_get_plan_sources (expr -> plan );
8005
+ plansource = (CachedPlanSource * ) linitial (plansources );
7993
8006
7994
8007
/*
7995
- * We can only test queries that resulted in exactly one CachedPlanSource
8008
+ * Get the generic plan for the query. If replanning is needed, do that
8009
+ * work in the eval_mcontext. (Note that replanning could throw an error,
8010
+ * in which case the expr is left marked "not simple", which is fine.)
8011
+ */
8012
+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
8013
+ cplan = SPI_plan_get_cached_plan (expr -> plan );
8014
+ MemoryContextSwitchTo (oldcontext );
8015
+
8016
+ /* Can't fail, because we checked for a single CachedPlanSource above */
8017
+ Assert (cplan != NULL );
8018
+
8019
+ /*
8020
+ * Verify that plancache.c thinks the plan is simple enough to use
8021
+ * CachedPlanIsSimplyValid. Given the restrictions above, it's unlikely
8022
+ * that this could fail, but if it does, just treat plan as not simple. On
8023
+ * success, save a refcount on the plan in the simple-expression resowner.
8024
+ */
8025
+ if (CachedPlanAllowsSimpleValidityCheck (plansource , cplan ,
8026
+ estate -> simple_eval_resowner ))
8027
+ {
8028
+ /* Remember that we have the refcount */
8029
+ expr -> expr_simple_plansource = plansource ;
8030
+ expr -> expr_simple_plan = cplan ;
8031
+ expr -> expr_simple_plan_lxid = MyProc -> vxid .lxid ;
8032
+
8033
+ /* Share the remaining work with the replan code path */
8034
+ exec_save_simple_expr (expr , cplan );
8035
+ }
8036
+
8037
+ /*
8038
+ * Release the plan refcount obtained by SPI_plan_get_cached_plan. (This
8039
+ * refcount is held by the wrong resowner, so we can't just repurpose it.)
8040
+ */
8041
+ ReleaseCachedPlan (cplan , CurrentResourceOwner );
8042
+ }
8043
+
8044
+ /*
8045
+ * exec_is_simple_query - precheck a query tree to see if it might be simple
8046
+ *
8047
+ * Check the analyzed-and-rewritten form of a query to see if we will be
8048
+ * able to treat it as a simple expression. It is caller's responsibility
8049
+ * that the CachedPlanSource be up-to-date.
8050
+ */
8051
+ static bool
8052
+ exec_is_simple_query (PLpgSQL_expr * expr )
8053
+ {
8054
+ List * plansources ;
8055
+ CachedPlanSource * plansource ;
8056
+ Query * query ;
8057
+
8058
+ /*
8059
+ * We can only test queries that resulted in exactly one CachedPlanSource.
7996
8060
*/
7997
8061
plansources = SPI_plan_get_plan_sources (expr -> plan );
7998
8062
if (list_length (plansources ) != 1 )
7999
- return ;
8063
+ return false ;
8000
8064
plansource = (CachedPlanSource * ) linitial (plansources );
8001
8065
8002
8066
/*
8003
8067
* 1. There must be one single querytree.
8004
8068
*/
8005
8069
if (list_length (plansource -> query_list ) != 1 )
8006
- return ;
8070
+ return false ;
8007
8071
query = (Query * ) linitial (plansource -> query_list );
8008
8072
8009
8073
/*
8010
- * 2. It must be a plain SELECT query without any input tables
8074
+ * 2. It must be a plain SELECT query without any input tables.
8011
8075
*/
8012
8076
if (!IsA (query , Query ))
8013
- return ;
8077
+ return false ;
8014
8078
if (query -> commandType != CMD_SELECT )
8015
- return ;
8079
+ return false ;
8016
8080
if (query -> rtable != NIL )
8017
- return ;
8081
+ return false ;
8018
8082
8019
8083
/*
8020
8084
* 3. Can't have any subplans, aggregates, qual clauses either. (These
@@ -8038,51 +8102,18 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
8038
8102
query -> limitOffset ||
8039
8103
query -> limitCount ||
8040
8104
query -> setOperations )
8041
- return ;
8105
+ return false ;
8042
8106
8043
8107
/*
8044
- * 4. The query must have a single attribute as result
8108
+ * 4. The query must have a single attribute as result.
8045
8109
*/
8046
8110
if (list_length (query -> targetList ) != 1 )
8047
- return ;
8111
+ return false ;
8048
8112
8049
8113
/*
8050
8114
* OK, we can treat it as a simple plan.
8051
- *
8052
- * Get the generic plan for the query. If replanning is needed, do that
8053
- * work in the eval_mcontext. (Note that replanning could throw an error,
8054
- * in which case the expr is left marked "not simple", which is fine.)
8055
- */
8056
- oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
8057
- cplan = SPI_plan_get_cached_plan (expr -> plan );
8058
- MemoryContextSwitchTo (oldcontext );
8059
-
8060
- /* Can't fail, because we checked for a single CachedPlanSource above */
8061
- Assert (cplan != NULL );
8062
-
8063
- /*
8064
- * Verify that plancache.c thinks the plan is simple enough to use
8065
- * CachedPlanIsSimplyValid. Given the restrictions above, it's unlikely
8066
- * that this could fail, but if it does, just treat plan as not simple. On
8067
- * success, save a refcount on the plan in the simple-expression resowner.
8068
- */
8069
- if (CachedPlanAllowsSimpleValidityCheck (plansource , cplan ,
8070
- estate -> simple_eval_resowner ))
8071
- {
8072
- /* Remember that we have the refcount */
8073
- expr -> expr_simple_plansource = plansource ;
8074
- expr -> expr_simple_plan = cplan ;
8075
- expr -> expr_simple_plan_lxid = MyProc -> vxid .lxid ;
8076
-
8077
- /* Share the remaining work with the replan code path */
8078
- exec_save_simple_expr (expr , cplan );
8079
- }
8080
-
8081
- /*
8082
- * Release the plan refcount obtained by SPI_plan_get_cached_plan. (This
8083
- * refcount is held by the wrong resowner, so we can't just repurpose it.)
8084
8115
*/
8085
- ReleaseCachedPlan ( cplan , CurrentResourceOwner ) ;
8116
+ return true ;
8086
8117
}
8087
8118
8088
8119
/*
0 commit comments