diff options
author | David Rowley <drowley@postgresql.org> | 2021-11-24 14:56:18 +1300 |
---|---|---|
committer | David Rowley <drowley@postgresql.org> | 2021-11-24 14:56:18 +1300 |
commit | 1050048a315790a505465bfcceb26eaf8dbc7e2e (patch) | |
tree | 86093353545d96b85123fb067415854eaae36549 /src/backend/executor/nodeMemoize.c | |
parent | e502150f7d0be41e3c8784be007fa871a32d8a7f (diff) | |
download | postgresql-1050048a315790a505465bfcceb26eaf8dbc7e2e.tar.gz postgresql-1050048a315790a505465bfcceb26eaf8dbc7e2e.zip |
Flush Memoize cache when non-key parameters change
It's possible that a subplan below a Memoize node contains a parameter
from above the Memoize node. If this parameter changes then cache entries
may become out-dated due to the new parameter value.
Previously Memoize was mistakenly not aware of this. We fix this here by
flushing the cache whenever a parameter that's not part of the cache
key changes.
Bug: #17213
Reported by: Elvis Pranskevichus
Author: David Rowley
Discussion: https://postgr.es/m/17213-988ed34b225a2862@postgresql.org
Backpatch-through: 14, where Memoize was added
Diffstat (limited to 'src/backend/executor/nodeMemoize.c')
-rw-r--r-- | src/backend/executor/nodeMemoize.c | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/src/backend/executor/nodeMemoize.c b/src/backend/executor/nodeMemoize.c index 683502dd90e..31e39722239 100644 --- a/src/backend/executor/nodeMemoize.c +++ b/src/backend/executor/nodeMemoize.c @@ -368,6 +368,37 @@ remove_cache_entry(MemoizeState *mstate, MemoizeEntry *entry) } /* + * cache_purge_all + * Remove all items from the cache + */ +static void +cache_purge_all(MemoizeState *mstate) +{ + uint64 evictions = mstate->hashtable->members; + PlanState *pstate = (PlanState *) mstate; + + /* + * Likely the most efficient way to remove all items is to just reset the + * memory context for the cache and then rebuild a fresh hash table. This + * saves having to remove each item one by one and pfree each cached tuple + */ + MemoryContextReset(mstate->tableContext); + + /* Make the hash table the same size as the original size */ + build_hash_table(mstate, ((Memoize *) pstate->plan)->est_entries); + + /* reset the LRU list */ + dlist_init(&mstate->lru_list); + mstate->last_tuple = NULL; + mstate->entry = NULL; + + mstate->mem_used = 0; + + /* XXX should we add something new to track these purges? */ + mstate->stats.cache_evictions += evictions; /* Update Stats */ +} + +/* * cache_reduce_memory * Evict older and less recently used items from the cache in order to * reduce the memory consumption back to something below the @@ -979,6 +1010,7 @@ ExecInitMemoize(Memoize *node, EState *estate, int eflags) * getting the first tuple. This allows us to mark it as so. */ mstate->singlerow = node->singlerow; + mstate->keyparamids = node->keyparamids; /* * Record if the cache keys should be compared bit by bit, or logically @@ -1082,6 +1114,12 @@ ExecReScanMemoize(MemoizeState *node) if (outerPlan->chgParam == NULL) ExecReScan(outerPlan); + /* + * Purge the entire cache if a parameter changed that is not part of the + * cache key. + */ + if (bms_nonempty_difference(outerPlan->chgParam, node->keyparamids)) + cache_purge_all(node); } /* |