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