15
15
* that matches the event is marked invalid, as is its generic CachedPlan
16
16
* if it has one. When (and if) the next demand for a cached plan occurs,
17
17
* parse analysis and rewrite is repeated to build a new valid query tree,
18
- * and then planning is performed as normal.
18
+ * and then planning is performed as normal. We also force re-analysis and
19
+ * re-planning if the active search_path is different from the previous time.
19
20
*
20
21
* Note that if the sinval was a result of user DDL actions, parse analysis
21
22
* could throw an error, for example if a column referenced by the query is
22
- * no longer present. The creator of a cached plan can specify whether it
23
- * is allowable for the query to change output tupdesc on replan (this
24
- * could happen with "SELECT *" for example) --- if so, it's up to the
23
+ * no longer present. Another possibility is for the query's output tupdesc
24
+ * to change (for instance "SELECT *" might expand differently than before).
25
+ * The creator of a cached plan can specify whether it is allowable for the
26
+ * query to change output tupdesc on replan --- if so, it's up to the
25
27
* caller to notice changes and cope with them.
26
28
*
27
29
* Currently, we track exactly the dependencies of plans on relations and
@@ -174,11 +176,11 @@ CreateCachedPlan(Node *raw_parse_tree,
174
176
plansource -> cursor_options = 0 ;
175
177
plansource -> fixed_result = false;
176
178
plansource -> resultDesc = NULL ;
177
- plansource -> search_path = NULL ;
178
179
plansource -> context = source_context ;
179
180
plansource -> query_list = NIL ;
180
181
plansource -> relationOids = NIL ;
181
182
plansource -> invalItems = NIL ;
183
+ plansource -> search_path = NULL ;
182
184
plansource -> query_context = NULL ;
183
185
plansource -> gplan = NULL ;
184
186
plansource -> is_oneshot = false;
@@ -239,11 +241,11 @@ CreateOneShotCachedPlan(Node *raw_parse_tree,
239
241
plansource -> cursor_options = 0 ;
240
242
plansource -> fixed_result = false;
241
243
plansource -> resultDesc = NULL ;
242
- plansource -> search_path = NULL ;
243
244
plansource -> context = CurrentMemoryContext ;
244
245
plansource -> query_list = NIL ;
245
246
plansource -> relationOids = NIL ;
246
247
plansource -> invalItems = NIL ;
248
+ plansource -> search_path = NULL ;
247
249
plansource -> query_context = NULL ;
248
250
plansource -> gplan = NULL ;
249
251
plansource -> is_oneshot = true;
@@ -360,6 +362,14 @@ CompleteCachedPlan(CachedPlanSource *plansource,
360
362
& plansource -> relationOids ,
361
363
& plansource -> invalItems );
362
364
365
+ /*
366
+ * Also save the current search_path in the query_context. (This should
367
+ * not generate much extra cruft either, since almost certainly the path
368
+ * is already valid.) Again, don't really need it for one-shot plans.
369
+ */
370
+ if (!plansource -> is_oneshot )
371
+ plansource -> search_path = GetOverrideSearchPath (querytree_context );
372
+
363
373
/*
364
374
* Save the final parameter types (or other parameter specification data)
365
375
* into the source_context, as well as our other parameters. Also save
@@ -383,12 +393,6 @@ CompleteCachedPlan(CachedPlanSource *plansource,
383
393
384
394
MemoryContextSwitchTo (oldcxt );
385
395
386
- /*
387
- * Fetch current search_path into dedicated context, but do any
388
- * recalculation work required in caller's context.
389
- */
390
- plansource -> search_path = GetOverrideSearchPath (source_context );
391
-
392
396
plansource -> is_complete = true;
393
397
plansource -> is_valid = true;
394
398
}
@@ -546,6 +550,23 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
546
550
return NIL ;
547
551
}
548
552
553
+ /*
554
+ * If the query is currently valid, we should have a saved search_path ---
555
+ * check to see if that matches the current environment. If not, we want
556
+ * to force replan.
557
+ */
558
+ if (plansource -> is_valid )
559
+ {
560
+ Assert (plansource -> search_path != NULL );
561
+ if (!OverrideSearchPathMatchesCurrent (plansource -> search_path ))
562
+ {
563
+ /* Invalidate the querytree and generic plan */
564
+ plansource -> is_valid = false;
565
+ if (plansource -> gplan )
566
+ plansource -> gplan -> is_valid = false;
567
+ }
568
+ }
569
+
549
570
/*
550
571
* If the query is currently valid, acquire locks on the referenced
551
572
* objects; then check again. We need to do it this way to cover the race
@@ -578,6 +599,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
578
599
plansource -> query_list = NIL ;
579
600
plansource -> relationOids = NIL ;
580
601
plansource -> invalItems = NIL ;
602
+ plansource -> search_path = NULL ;
581
603
582
604
/*
583
605
* Free the query_context. We don't really expect MemoryContextDelete to
@@ -602,14 +624,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
602
624
*/
603
625
Assert (plansource -> is_complete );
604
626
605
- /*
606
- * Restore the search_path that was in use when the plan was made. See
607
- * comments for PushOverrideSearchPath about limitations of this.
608
- *
609
- * (XXX is there anything else we really need to restore?)
610
- */
611
- PushOverrideSearchPath (plansource -> search_path );
612
-
613
627
/*
614
628
* If a snapshot is already set (the normal case), we can just use that
615
629
* for parsing/planning. But if it isn't, install one. Note: no point in
@@ -645,9 +659,6 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
645
659
if (snapshot_set )
646
660
PopActiveSnapshot ();
647
661
648
- /* Now we can restore current search path */
649
- PopOverrideSearchPath ();
650
-
651
662
/*
652
663
* Check or update the result tupdesc. XXX should we use a weaker
653
664
* condition than equalTupleDescs() here?
@@ -699,6 +710,13 @@ RevalidateCachedQuery(CachedPlanSource *plansource)
699
710
& plansource -> relationOids ,
700
711
& plansource -> invalItems );
701
712
713
+ /*
714
+ * Also save the current search_path in the query_context. (This should
715
+ * not generate much extra cruft either, since almost certainly the path
716
+ * is already valid.)
717
+ */
718
+ plansource -> search_path = GetOverrideSearchPath (querytree_context );
719
+
702
720
MemoryContextSwitchTo (oldcxt );
703
721
704
722
/* Now reparent the finished query_context and save the links */
@@ -848,20 +866,6 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
848
866
qlist = plansource -> query_list ;
849
867
}
850
868
851
- /*
852
- * Restore the search_path that was in use when the plan was made. See
853
- * comments for PushOverrideSearchPath about limitations of this.
854
- *
855
- * (XXX is there anything else we really need to restore?)
856
- *
857
- * Note: it's a bit annoying to do this and snapshot-setting twice in the
858
- * case where we have to do both re-analysis and re-planning. However,
859
- * until there's some evidence that the cost is actually meaningful
860
- * compared to parse analysis + planning, I'm not going to contort the
861
- * code enough to avoid that.
862
- */
863
- PushOverrideSearchPath (plansource -> search_path );
864
-
865
869
/*
866
870
* If a snapshot is already set (the normal case), we can just use that
867
871
* for planning. But if it isn't, and we need one, install one.
@@ -894,9 +898,6 @@ BuildCachedPlan(CachedPlanSource *plansource, List *qlist,
894
898
if (snapshot_set )
895
899
PopActiveSnapshot ();
896
900
897
- /* Now we can restore current search path */
898
- PopOverrideSearchPath ();
899
-
900
901
/*
901
902
* Normally we make a dedicated memory context for the CachedPlan and its
902
903
* subsidiary data. (It's probably not going to be large, but just in
@@ -1268,7 +1269,6 @@ CopyCachedPlan(CachedPlanSource *plansource)
1268
1269
newsource -> resultDesc = CreateTupleDescCopy (plansource -> resultDesc );
1269
1270
else
1270
1271
newsource -> resultDesc = NULL ;
1271
- newsource -> search_path = CopyOverrideSearchPath (plansource -> search_path );
1272
1272
newsource -> context = source_context ;
1273
1273
1274
1274
querytree_context = AllocSetContextCreate (source_context ,
@@ -1280,6 +1280,8 @@ CopyCachedPlan(CachedPlanSource *plansource)
1280
1280
newsource -> query_list = (List * ) copyObject (plansource -> query_list );
1281
1281
newsource -> relationOids = (List * ) copyObject (plansource -> relationOids );
1282
1282
newsource -> invalItems = (List * ) copyObject (plansource -> invalItems );
1283
+ if (plansource -> search_path )
1284
+ newsource -> search_path = CopyOverrideSearchPath (plansource -> search_path );
1283
1285
newsource -> query_context = querytree_context ;
1284
1286
1285
1287
newsource -> gplan = NULL ;
0 commit comments