Skip to content

Commit 1cd9356

Browse files
committed
Fix caching of foreign-key-checking queries so that when a replan is needed,
we regenerate the SQL query text not merely the plan derived from it. This is needed to handle contingencies such as renaming of a table or column used in an FK. Pre-8.3, such cases worked despite the lack of replanning (because the cached plan needn't actually change), so this is a regression. Per bug #4417 from Benjamin Bihler.
1 parent 448950b commit 1cd9356

File tree

5 files changed

+103
-7
lines changed

5 files changed

+103
-7
lines changed

src/backend/executor/spi.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.197 2008/07/18 20:26:06 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.198 2008/09/15 23:37:39 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1367,6 +1367,36 @@ SPI_is_cursor_plan(SPIPlanPtr plan)
13671367
return false;
13681368
}
13691369

1370+
/*
1371+
* SPI_plan_is_valid --- test whether a SPI plan is currently valid
1372+
* (that is, not marked as being in need of revalidation).
1373+
*
1374+
* See notes for CachedPlanIsValid before using this.
1375+
*/
1376+
bool
1377+
SPI_plan_is_valid(SPIPlanPtr plan)
1378+
{
1379+
Assert(plan->magic == _SPI_PLAN_MAGIC);
1380+
if (plan->saved)
1381+
{
1382+
ListCell *lc;
1383+
1384+
foreach(lc, plan->plancache_list)
1385+
{
1386+
CachedPlanSource *plansource = (CachedPlanSource *) lfirst(lc);
1387+
1388+
if (!CachedPlanIsValid(plansource))
1389+
return false;
1390+
}
1391+
return true;
1392+
}
1393+
else
1394+
{
1395+
/* An unsaved plan is assumed valid for its (short) lifetime */
1396+
return true;
1397+
}
1398+
}
1399+
13701400
/*
13711401
* SPI_result_code_string --- convert any SPI return code to a string
13721402
*

src/backend/utils/adt/ri_triggers.c

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
1717
*
18-
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.109 2008/05/19 04:14:24 tgl Exp $
18+
* $PostgreSQL: pgsql/src/backend/utils/adt/ri_triggers.c,v 1.110 2008/09/15 23:37:39 tgl Exp $
1919
*
2020
* ----------
2121
*/
@@ -3615,6 +3615,7 @@ static SPIPlanPtr
36153615
ri_FetchPreparedPlan(RI_QueryKey *key)
36163616
{
36173617
RI_QueryHashEntry *entry;
3618+
SPIPlanPtr plan;
36183619

36193620
/*
36203621
* On the first call initialize the hashtable
@@ -3630,7 +3631,30 @@ ri_FetchPreparedPlan(RI_QueryKey *key)
36303631
HASH_FIND, NULL);
36313632
if (entry == NULL)
36323633
return NULL;
3633-
return entry->plan;
3634+
3635+
/*
3636+
* Check whether the plan is still valid. If it isn't, we don't want
3637+
* to simply rely on plancache.c to regenerate it; rather we should
3638+
* start from scratch and rebuild the query text too. This is to cover
3639+
* cases such as table/column renames. We depend on the plancache
3640+
* machinery to detect possible invalidations, though.
3641+
*
3642+
* CAUTION: this check is only trustworthy if the caller has already
3643+
* locked both FK and PK rels.
3644+
*/
3645+
plan = entry->plan;
3646+
if (plan && SPI_plan_is_valid(plan))
3647+
return plan;
3648+
3649+
/*
3650+
* Otherwise we might as well flush the cached plan now, to free a
3651+
* little memory space before we make a new one.
3652+
*/
3653+
entry->plan = NULL;
3654+
if (plan)
3655+
SPI_freeplan(plan);
3656+
3657+
return NULL;
36343658
}
36353659

36363660

@@ -3653,11 +3677,13 @@ ri_HashPreparedPlan(RI_QueryKey *key, SPIPlanPtr plan)
36533677
ri_InitHashTables();
36543678

36553679
/*
3656-
* Add the new plan.
3680+
* Add the new plan. We might be overwriting an entry previously
3681+
* found invalid by ri_FetchPreparedPlan.
36573682
*/
36583683
entry = (RI_QueryHashEntry *) hash_search(ri_query_cache,
36593684
(void *) key,
36603685
HASH_ENTER, &found);
3686+
Assert(!found || entry->plan == NULL);
36613687
entry->plan = plan;
36623688
}
36633689

src/backend/utils/cache/plancache.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
* Portions Copyright (c) 1994, Regents of the University of California
3636
*
3737
* IDENTIFICATION
38-
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.21 2008/09/09 18:58:08 tgl Exp $
38+
* $PostgreSQL: pgsql/src/backend/utils/cache/plancache.c,v 1.22 2008/09/15 23:37:39 tgl Exp $
3939
*
4040
*-------------------------------------------------------------------------
4141
*/
@@ -571,6 +571,44 @@ ReleaseCachedPlan(CachedPlan *plan, bool useResOwner)
571571
MemoryContextDelete(plan->context);
572572
}
573573

574+
/*
575+
* CachedPlanIsValid: test whether the plan within a CachedPlanSource is
576+
* currently valid (that is, not marked as being in need of revalidation).
577+
*
578+
* This result is only trustworthy (ie, free from race conditions) if
579+
* the caller has acquired locks on all the relations used in the plan.
580+
*/
581+
bool
582+
CachedPlanIsValid(CachedPlanSource *plansource)
583+
{
584+
CachedPlan *plan;
585+
586+
/* Validity check that we were given a CachedPlanSource */
587+
Assert(list_member_ptr(cached_plans_list, plansource));
588+
589+
plan = plansource->plan;
590+
if (plan && !plan->dead)
591+
{
592+
/*
593+
* Plan must have positive refcount because it is referenced by
594+
* plansource; so no need to fear it disappears under us here.
595+
*/
596+
Assert(plan->refcount > 0);
597+
598+
/*
599+
* Although we don't want to acquire locks here, it still seems
600+
* useful to check for expiration of a transient plan.
601+
*/
602+
if (TransactionIdIsValid(plan->saved_xmin) &&
603+
!TransactionIdEquals(plan->saved_xmin, TransactionXmin))
604+
plan->dead = true;
605+
else
606+
return true;
607+
}
608+
609+
return false;
610+
}
611+
574612
/*
575613
* AcquireExecutorLocks: acquire locks needed for execution of a fully-planned
576614
* cached plan; or release them if acquire is false.

src/include/executor/spi.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
77
* Portions Copyright (c) 1994, Regents of the University of California
88
*
9-
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.66 2008/04/01 03:09:30 tgl Exp $
9+
* $PostgreSQL: pgsql/src/include/executor/spi.h,v 1.67 2008/09/15 23:37:40 tgl Exp $
1010
*
1111
*-------------------------------------------------------------------------
1212
*/
@@ -118,6 +118,7 @@ extern int SPI_freeplan(SPIPlanPtr plan);
118118
extern Oid SPI_getargtypeid(SPIPlanPtr plan, int argIndex);
119119
extern int SPI_getargcount(SPIPlanPtr plan);
120120
extern bool SPI_is_cursor_plan(SPIPlanPtr plan);
121+
extern bool SPI_plan_is_valid(SPIPlanPtr plan);
121122
extern const char *SPI_result_code_string(int code);
122123

123124
extern HeapTuple SPI_copytuple(HeapTuple tuple);

src/include/utils/plancache.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.13 2008/09/09 18:58:09 tgl Exp $
11+
* $PostgreSQL: pgsql/src/include/utils/plancache.h,v 1.14 2008/09/15 23:37:40 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -109,6 +109,7 @@ extern void DropCachedPlan(CachedPlanSource *plansource);
109109
extern CachedPlan *RevalidateCachedPlan(CachedPlanSource *plansource,
110110
bool useResOwner);
111111
extern void ReleaseCachedPlan(CachedPlan *plan, bool useResOwner);
112+
extern bool CachedPlanIsValid(CachedPlanSource *plansource);
112113
extern TupleDesc PlanCacheComputeResultDesc(List *stmt_list);
113114

114115
extern void ResetPlanCache(void);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy