@@ -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 bool exec_eval_simple_expr (PLpgSQL_execstate * estate ,
@@ -6036,12 +6037,18 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6036
6037
* release it, so we don't leak plans intra-transaction.
6037
6038
*/
6038
6039
if (expr -> expr_simple_plan_lxid == curlxid )
6039
- {
6040
6040
ReleaseCachedPlan (expr -> expr_simple_plan ,
6041
6041
estate -> simple_eval_resowner );
6042
- expr -> expr_simple_plan = NULL ;
6043
- expr -> expr_simple_plan_lxid = InvalidLocalTransactionId ;
6044
- }
6042
+
6043
+ /*
6044
+ * Reset to "not simple" to leave sane state (with no dangling
6045
+ * pointers) in case we fail while replanning. expr_simple_plansource
6046
+ * can be left alone however, as that cannot move.
6047
+ */
6048
+ expr -> expr_simple_expr = NULL ;
6049
+ expr -> expr_rw_param = NULL ;
6050
+ expr -> expr_simple_plan = NULL ;
6051
+ expr -> expr_simple_plan_lxid = InvalidLocalTransactionId ;
6045
6052
6046
6053
/* Do the replanning work in the eval_mcontext */
6047
6054
oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
@@ -6057,11 +6064,15 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6057
6064
Assert (cplan != NULL );
6058
6065
6059
6066
/*
6060
- * This test probably can't fail either, but if it does, cope by
6061
- * declaring the plan to be non-simple. On success, we'll acquire a
6062
- * refcount on the new plan, stored in simple_eval_resowner.
6067
+ * Recheck exec_is_simple_query, which could now report false in
6068
+ * edge-case scenarios such as a non-SRF having been replaced with a
6069
+ * SRF. Also recheck CachedPlanAllowsSimpleValidityCheck, just to be
6070
+ * sure. If either test fails, cope by declaring the plan to be
6071
+ * non-simple. On success, we'll acquire a refcount on the new plan,
6072
+ * stored in simple_eval_resowner.
6063
6073
*/
6064
- if (CachedPlanAllowsSimpleValidityCheck (expr -> expr_simple_plansource ,
6074
+ if (exec_is_simple_query (expr ) &&
6075
+ CachedPlanAllowsSimpleValidityCheck (expr -> expr_simple_plansource ,
6065
6076
cplan ,
6066
6077
estate -> simple_eval_resowner ))
6067
6078
{
@@ -6073,9 +6084,6 @@ exec_eval_simple_expr(PLpgSQL_execstate *estate,
6073
6084
{
6074
6085
/* Release SPI_plan_get_cached_plan's refcount */
6075
6086
ReleaseCachedPlan (cplan , CurrentResourceOwner );
6076
- /* Mark expression as non-simple, and fail */
6077
- expr -> expr_simple_expr = NULL ;
6078
- expr -> expr_rw_param = NULL ;
6079
6087
return false;
6080
6088
}
6081
6089
@@ -7919,7 +7927,6 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
7919
7927
{
7920
7928
List * plansources ;
7921
7929
CachedPlanSource * plansource ;
7922
- Query * query ;
7923
7930
CachedPlan * cplan ;
7924
7931
MemoryContext oldcontext ;
7925
7932
@@ -7935,31 +7942,88 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
7935
7942
* called immediately after creating the CachedPlanSource, we need not
7936
7943
* worry about the query being stale.
7937
7944
*/
7945
+ if (!exec_is_simple_query (expr ))
7946
+ return ;
7947
+
7948
+ /* exec_is_simple_query verified that there's just one CachedPlanSource */
7949
+ plansources = SPI_plan_get_plan_sources (expr -> plan );
7950
+ plansource = (CachedPlanSource * ) linitial (plansources );
7938
7951
7939
7952
/*
7940
- * We can only test queries that resulted in exactly one CachedPlanSource
7953
+ * Get the generic plan for the query. If replanning is needed, do that
7954
+ * work in the eval_mcontext. (Note that replanning could throw an error,
7955
+ * in which case the expr is left marked "not simple", which is fine.)
7956
+ */
7957
+ oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
7958
+ cplan = SPI_plan_get_cached_plan (expr -> plan );
7959
+ MemoryContextSwitchTo (oldcontext );
7960
+
7961
+ /* Can't fail, because we checked for a single CachedPlanSource above */
7962
+ Assert (cplan != NULL );
7963
+
7964
+ /*
7965
+ * Verify that plancache.c thinks the plan is simple enough to use
7966
+ * CachedPlanIsSimplyValid. Given the restrictions above, it's unlikely
7967
+ * that this could fail, but if it does, just treat plan as not simple. On
7968
+ * success, save a refcount on the plan in the simple-expression resowner.
7969
+ */
7970
+ if (CachedPlanAllowsSimpleValidityCheck (plansource , cplan ,
7971
+ estate -> simple_eval_resowner ))
7972
+ {
7973
+ /* Remember that we have the refcount */
7974
+ expr -> expr_simple_plansource = plansource ;
7975
+ expr -> expr_simple_plan = cplan ;
7976
+ expr -> expr_simple_plan_lxid = MyProc -> lxid ;
7977
+
7978
+ /* Share the remaining work with the replan code path */
7979
+ exec_save_simple_expr (expr , cplan );
7980
+ }
7981
+
7982
+ /*
7983
+ * Release the plan refcount obtained by SPI_plan_get_cached_plan. (This
7984
+ * refcount is held by the wrong resowner, so we can't just repurpose it.)
7985
+ */
7986
+ ReleaseCachedPlan (cplan , CurrentResourceOwner );
7987
+ }
7988
+
7989
+ /*
7990
+ * exec_is_simple_query - precheck a query tree to see if it might be simple
7991
+ *
7992
+ * Check the analyzed-and-rewritten form of a query to see if we will be
7993
+ * able to treat it as a simple expression. It is caller's responsibility
7994
+ * that the CachedPlanSource be up-to-date.
7995
+ */
7996
+ static bool
7997
+ exec_is_simple_query (PLpgSQL_expr * expr )
7998
+ {
7999
+ List * plansources ;
8000
+ CachedPlanSource * plansource ;
8001
+ Query * query ;
8002
+
8003
+ /*
8004
+ * We can only test queries that resulted in exactly one CachedPlanSource.
7941
8005
*/
7942
8006
plansources = SPI_plan_get_plan_sources (expr -> plan );
7943
8007
if (list_length (plansources ) != 1 )
7944
- return ;
8008
+ return false ;
7945
8009
plansource = (CachedPlanSource * ) linitial (plansources );
7946
8010
7947
8011
/*
7948
8012
* 1. There must be one single querytree.
7949
8013
*/
7950
8014
if (list_length (plansource -> query_list ) != 1 )
7951
- return ;
8015
+ return false ;
7952
8016
query = (Query * ) linitial (plansource -> query_list );
7953
8017
7954
8018
/*
7955
- * 2. It must be a plain SELECT query without any input tables
8019
+ * 2. It must be a plain SELECT query without any input tables.
7956
8020
*/
7957
8021
if (!IsA (query , Query ))
7958
- return ;
8022
+ return false ;
7959
8023
if (query -> commandType != CMD_SELECT )
7960
- return ;
8024
+ return false ;
7961
8025
if (query -> rtable != NIL )
7962
- return ;
8026
+ return false ;
7963
8027
7964
8028
/*
7965
8029
* 3. Can't have any subplans, aggregates, qual clauses either. (These
@@ -7983,51 +8047,18 @@ exec_simple_check_plan(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
7983
8047
query -> limitOffset ||
7984
8048
query -> limitCount ||
7985
8049
query -> setOperations )
7986
- return ;
8050
+ return false ;
7987
8051
7988
8052
/*
7989
- * 4. The query must have a single attribute as result
8053
+ * 4. The query must have a single attribute as result.
7990
8054
*/
7991
8055
if (list_length (query -> targetList ) != 1 )
7992
- return ;
8056
+ return false ;
7993
8057
7994
8058
/*
7995
8059
* OK, we can treat it as a simple plan.
7996
- *
7997
- * Get the generic plan for the query. If replanning is needed, do that
7998
- * work in the eval_mcontext. (Note that replanning could throw an error,
7999
- * in which case the expr is left marked "not simple", which is fine.)
8000
- */
8001
- oldcontext = MemoryContextSwitchTo (get_eval_mcontext (estate ));
8002
- cplan = SPI_plan_get_cached_plan (expr -> plan );
8003
- MemoryContextSwitchTo (oldcontext );
8004
-
8005
- /* Can't fail, because we checked for a single CachedPlanSource above */
8006
- Assert (cplan != NULL );
8007
-
8008
- /*
8009
- * Verify that plancache.c thinks the plan is simple enough to use
8010
- * CachedPlanIsSimplyValid. Given the restrictions above, it's unlikely
8011
- * that this could fail, but if it does, just treat plan as not simple. On
8012
- * success, save a refcount on the plan in the simple-expression resowner.
8013
- */
8014
- if (CachedPlanAllowsSimpleValidityCheck (plansource , cplan ,
8015
- estate -> simple_eval_resowner ))
8016
- {
8017
- /* Remember that we have the refcount */
8018
- expr -> expr_simple_plansource = plansource ;
8019
- expr -> expr_simple_plan = cplan ;
8020
- expr -> expr_simple_plan_lxid = MyProc -> lxid ;
8021
-
8022
- /* Share the remaining work with the replan code path */
8023
- exec_save_simple_expr (expr , cplan );
8024
- }
8025
-
8026
- /*
8027
- * Release the plan refcount obtained by SPI_plan_get_cached_plan. (This
8028
- * refcount is held by the wrong resowner, so we can't just repurpose it.)
8029
8060
*/
8030
- ReleaseCachedPlan ( cplan , CurrentResourceOwner ) ;
8061
+ return true ;
8031
8062
}
8032
8063
8033
8064
/*
0 commit comments