Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit411137a

Browse files
committed
Flush Memoize cache when non-key parameters change, take 2
It's possible that a subplan below a Memoize node contains a parameterfrom above the Memoize node. If this parameter changes then cache entriesmay become out-dated due to the new parameter value.Previously Memoize was mistakenly not aware of this. We fix this here byflushing the cache whenever a parameter that's not part of the cachekey changes.Bug: #17213Reported by: Elvis PranskevichusAuthor: David RowleyDiscussion:https://postgr.es/m/17213-988ed34b225a2862@postgresql.orgBackpatch-through: 14, where Memoize was added
1 parentfb5961f commit411137a

File tree

12 files changed

+145
-3
lines changed

12 files changed

+145
-3
lines changed

‎src/backend/executor/nodeMemoize.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,37 @@ remove_cache_entry(MemoizeState *mstate, MemoizeEntry *entry)
367367
pfree(key);
368368
}
369369

370+
/*
371+
* cache_purge_all
372+
*Remove all items from the cache
373+
*/
374+
staticvoid
375+
cache_purge_all(MemoizeState*mstate)
376+
{
377+
uint64evictions=mstate->hashtable->members;
378+
PlanState*pstate= (PlanState*)mstate;
379+
380+
/*
381+
* Likely the most efficient way to remove all items is to just reset the
382+
* memory context for the cache and then rebuild a fresh hash table. This
383+
* saves having to remove each item one by one and pfree each cached tuple
384+
*/
385+
MemoryContextReset(mstate->tableContext);
386+
387+
/* Make the hash table the same size as the original size */
388+
build_hash_table(mstate, ((Memoize*)pstate->plan)->est_entries);
389+
390+
/* reset the LRU list */
391+
dlist_init(&mstate->lru_list);
392+
mstate->last_tuple=NULL;
393+
mstate->entry=NULL;
394+
395+
mstate->mem_used=0;
396+
397+
/* XXX should we add something new to track these purges? */
398+
mstate->stats.cache_evictions+=evictions;/* Update Stats */
399+
}
400+
370401
/*
371402
* cache_reduce_memory
372403
*Evict older and less recently used items from the cache in order to
@@ -979,6 +1010,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags)
9791010
* getting the first tuple. This allows us to mark it as so.
9801011
*/
9811012
mstate->singlerow=node->singlerow;
1013+
mstate->keyparamids=node->keyparamids;
9821014

9831015
/*
9841016
* Record if the cache keys should be compared bit by bit, or logically
@@ -1082,6 +1114,12 @@ ExecReScanMemoize(MemoizeState *node)
10821114
if (outerPlan->chgParam==NULL)
10831115
ExecReScan(outerPlan);
10841116

1117+
/*
1118+
* Purge the entire cache if a parameter changed that is not part of the
1119+
* cache key.
1120+
*/
1121+
if (bms_nonempty_difference(outerPlan->chgParam,node->keyparamids))
1122+
cache_purge_all(node);
10851123
}
10861124

10871125
/*

‎src/backend/nodes/bitmapset.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,8 @@ bms_overlap_list(const Bitmapset *a, const List *b)
540540

541541
/*
542542
* bms_nonempty_difference - do sets have a nonempty difference?
543+
*
544+
* i.e., are any members set in 'a' that are not also set in 'b'.
543545
*/
544546
bool
545547
bms_nonempty_difference(constBitmapset*a,constBitmapset*b)

‎src/backend/nodes/copyfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,7 @@ _copyMemoize(const Memoize *from)
973973
COPY_SCALAR_FIELD(singlerow);
974974
COPY_SCALAR_FIELD(binary_mode);
975975
COPY_SCALAR_FIELD(est_entries);
976+
COPY_BITMAPSET_FIELD(keyparamids);
976977

977978
returnnewnode;
978979
}

‎src/backend/nodes/outfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,7 @@ _outMemoize(StringInfo str, const Memoize *node)
868868
WRITE_BOOL_FIELD(singlerow);
869869
WRITE_BOOL_FIELD(binary_mode);
870870
WRITE_UINT_FIELD(est_entries);
871+
WRITE_BITMAPSET_FIELD(keyparamids);
871872
}
872873

873874
staticvoid

‎src/backend/nodes/readfuncs.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2232,6 +2232,7 @@ _readMemoize(void)
22322232
READ_BOOL_FIELD(singlerow);
22332233
READ_BOOL_FIELD(binary_mode);
22342234
READ_UINT_FIELD(est_entries);
2235+
READ_BITMAPSET_FIELD(keyparamids);
22352236

22362237
READ_DONE();
22372238
}

‎src/backend/optimizer/plan/createplan.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ static Material *make_material(Plan *lefttree);
280280
staticMemoize*make_memoize(Plan*lefttree,Oid*hashoperators,
281281
Oid*collations,List*param_exprs,
282282
boolsinglerow,boolbinary_mode,
283-
uint32est_entries);
283+
uint32est_entries,Bitmapset*keyparamids);
284284
staticWindowAgg*make_windowagg(List*tlist,Indexwinref,
285285
intpartNumCols,AttrNumber*partColIdx,Oid*partOperators,Oid*partCollations,
286286
intordNumCols,AttrNumber*ordColIdx,Oid*ordOperators,Oid*ordCollations,
@@ -1586,6 +1586,7 @@ static Memoize *
15861586
create_memoize_plan(PlannerInfo*root,MemoizePath*best_path,intflags)
15871587
{
15881588
Memoize*plan;
1589+
Bitmapset*keyparamids;
15891590
Plan*subplan;
15901591
Oid*operators;
15911592
Oid*collations;
@@ -1617,9 +1618,11 @@ create_memoize_plan(PlannerInfo *root, MemoizePath *best_path, int flags)
16171618
i++;
16181619
}
16191620

1621+
keyparamids=pull_paramids((Expr*)param_exprs);
1622+
16201623
plan=make_memoize(subplan,operators,collations,param_exprs,
16211624
best_path->singlerow,best_path->binary_mode,
1622-
best_path->est_entries);
1625+
best_path->est_entries,keyparamids);
16231626

16241627
copy_generic_path_info(&plan->plan, (Path*)best_path);
16251628

@@ -6420,7 +6423,7 @@ materialize_finished_plan(Plan *subplan)
64206423
staticMemoize*
64216424
make_memoize(Plan*lefttree,Oid*hashoperators,Oid*collations,
64226425
List*param_exprs,boolsinglerow,boolbinary_mode,
6423-
uint32est_entries)
6426+
uint32est_entries,Bitmapset*keyparamids)
64246427
{
64256428
Memoize*node=makeNode(Memoize);
64266429
Plan*plan=&node->plan;
@@ -6437,6 +6440,7 @@ make_memoize(Plan *lefttree, Oid *hashoperators, Oid *collations,
64376440
node->singlerow=singlerow;
64386441
node->binary_mode=binary_mode;
64396442
node->est_entries=est_entries;
6443+
node->keyparamids=keyparamids;
64406444

64416445
returnnode;
64426446
}

‎src/backend/optimizer/util/clauses.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ static Query *substitute_actual_srf_parameters(Query *expr,
152152
intnargs,List*args);
153153
staticNode*substitute_actual_srf_parameters_mutator(Node*node,
154154
substitute_actual_srf_parameters_context*context);
155+
staticboolpull_paramids_walker(Node*node,Bitmapset**context);
155156

156157

157158
/*****************************************************************************
@@ -5214,3 +5215,33 @@ substitute_actual_srf_parameters_mutator(Node *node,
52145215
substitute_actual_srf_parameters_mutator,
52155216
(void*)context);
52165217
}
5218+
5219+
/*
5220+
* pull_paramids
5221+
*Returns a Bitmapset containing the paramids of all Params in 'expr'.
5222+
*/
5223+
Bitmapset*
5224+
pull_paramids(Expr*expr)
5225+
{
5226+
Bitmapset*result=NULL;
5227+
5228+
(void)pull_paramids_walker((Node*)expr,&result);
5229+
5230+
returnresult;
5231+
}
5232+
5233+
staticbool
5234+
pull_paramids_walker(Node*node,Bitmapset**context)
5235+
{
5236+
if (node==NULL)
5237+
return false;
5238+
if (IsA(node,Param))
5239+
{
5240+
Param*param= (Param*)node;
5241+
5242+
*context=bms_add_member(*context,param->paramid);
5243+
return false;
5244+
}
5245+
returnexpression_tree_walker(node,pull_paramids_walker,
5246+
(void*)context);
5247+
}

‎src/include/nodes/execnodes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2113,6 +2113,8 @@ typedef struct MemoizeState
21132113
* by bit, false when using hash equality ops */
21142114
MemoizeInstrumentationstats;/* execution statistics */
21152115
SharedMemoizeInfo*shared_info;/* statistics for parallel workers */
2116+
Bitmapset*keyparamids;/* Param->paramids of expressions belonging to
2117+
* param_exprs */
21162118
}MemoizeState;
21172119

21182120
/* ----------------

‎src/include/nodes/plannodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ typedef struct Memoize
804804
uint32est_entries;/* The maximum number of entries that the
805805
* planner expects will fit in the cache, or 0
806806
* if unknown */
807+
Bitmapset*keyparamids;/* paramids from param_exprs */
807808
}Memoize;
808809

809810
/* ----------------

‎src/include/optimizer/clauses.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,6 @@ extern void CommuteOpExpr(OpExpr *clause);
5353
externQuery*inline_set_returning_function(PlannerInfo*root,
5454
RangeTblEntry*rte);
5555

56+
externBitmapset*pull_paramids(Expr*expr);
57+
5658
#endif/* CLAUSES_H */

‎src/test/regress/expected/memoize.out

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,45 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
196196
(8 rows)
197197

198198
DROP TABLE strtest;
199+
-- Exercise Memoize code that flushes the cache when a parameter changes which
200+
-- is not part of the cache key.
201+
-- Ensure we get a Memoize plan
202+
EXPLAIN (COSTS OFF)
203+
SELECT unique1 FROM tenk1 t0
204+
WHERE unique1 < 3
205+
AND EXISTS (
206+
SELECT 1 FROM tenk1 t1
207+
INNER JOIN tenk1 t2 ON t1.unique1 = t2.hundred
208+
WHERE t0.ten = t1.twenty AND t0.two <> t2.four OFFSET 0);
209+
QUERY PLAN
210+
----------------------------------------------------------------
211+
Index Scan using tenk1_unique1 on tenk1 t0
212+
Index Cond: (unique1 < 3)
213+
Filter: (SubPlan 1)
214+
SubPlan 1
215+
-> Nested Loop
216+
-> Index Scan using tenk1_hundred on tenk1 t2
217+
Filter: (t0.two <> four)
218+
-> Memoize
219+
Cache Key: t2.hundred
220+
Cache Mode: logical
221+
-> Index Scan using tenk1_unique1 on tenk1 t1
222+
Index Cond: (unique1 = t2.hundred)
223+
Filter: (t0.ten = twenty)
224+
(13 rows)
225+
226+
-- Ensure the above query returns the correct result
227+
SELECT unique1 FROM tenk1 t0
228+
WHERE unique1 < 3
229+
AND EXISTS (
230+
SELECT 1 FROM tenk1 t1
231+
INNER JOIN tenk1 t2 ON t1.unique1 = t2.hundred
232+
WHERE t0.ten = t1.twenty AND t0.two <> t2.four OFFSET 0);
233+
unique1
234+
---------
235+
2
236+
(1 row)
237+
199238
RESET enable_seqscan;
200239
RESET enable_mergejoin;
201240
RESET work_mem;

‎src/test/regress/sql/memoize.sql

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ SELECT * FROM strtest s1 INNER JOIN strtest s2 ON s1.t >= s2.t;', false);
103103

104104
DROPTABLE strtest;
105105

106+
-- Exercise Memoize code that flushes the cache when a parameter changes which
107+
-- is not part of the cache key.
108+
109+
-- Ensure we get a Memoize plan
110+
EXPLAIN (COSTS OFF)
111+
SELECT unique1FROM tenk1 t0
112+
WHERE unique1<3
113+
AND EXISTS (
114+
SELECT1FROM tenk1 t1
115+
INNER JOIN tenk1 t2ONt1.unique1=t2.hundred
116+
WHEREt0.ten=t1.twentyANDt0.two<>t2.four OFFSET0);
117+
118+
-- Ensure the above query returns the correct result
119+
SELECT unique1FROM tenk1 t0
120+
WHERE unique1<3
121+
AND EXISTS (
122+
SELECT1FROM tenk1 t1
123+
INNER JOIN tenk1 t2ONt1.unique1=t2.hundred
124+
WHEREt0.ten=t1.twentyANDt0.two<>t2.four OFFSET0);
125+
106126
RESET enable_seqscan;
107127
RESET enable_mergejoin;
108128
RESET work_mem;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp