PostgreSQL Source Code  git master
nodeResultCache.c File Reference
#include "postgres.h"
#include "common/hashfn.h"
#include "executor/executor.h"
#include "executor/nodeResultCache.h"
#include "lib/ilist.h"
#include "miscadmin.h"
#include "utils/lsyscache.h"
#include "lib/simplehash.h"
Include dependency graph for nodeResultCache.c:

Go to the source code of this file.

Data Structures

struct  ResultCacheTuple
 
struct  ResultCacheKey
 
struct  ResultCacheEntry
 

Macros

#define RC_CACHE_LOOKUP   1 /* Attempt to perform a cache lookup */
 
#define RC_CACHE_FETCH_NEXT_TUPLE   2 /* Get another tuple from the cache */
 
#define RC_FILLING_CACHE   3 /* Read outer node to fill cache */
 
#define RC_CACHE_BYPASS_MODE
 
#define RC_END_OF_SCAN   5 /* Ready for rescan */
 
#define EMPTY_ENTRY_MEMORY_BYTES(e)
 
#define CACHE_TUPLE_BYTES(t)
 
#define SH_PREFIX   resultcache
 
#define SH_ELEMENT_TYPE   ResultCacheEntry
 
#define SH_KEY_TYPE   ResultCacheKey *
 
#define SH_SCOPE   static inline
 
#define SH_DECLARE
 
#define SH_PREFIX   resultcache
 
#define SH_ELEMENT_TYPE   ResultCacheEntry
 
#define SH_KEY_TYPE   ResultCacheKey *
 
#define SH_KEY   key
 
#define SH_HASH_KEY(tb, key)   ResultCacheHash_hash(tb, key)
 
#define SH_EQUAL(tb, a, b)   (ResultCacheHash_equal(tb, a, b) == 0)
 
#define SH_SCOPE   static inline
 
#define SH_STORE_HASH
 
#define SH_GET_HASH(tb, a)   a->hash
 
#define SH_DEFINE
 

Typedefs

typedef struct ResultCacheTuple ResultCacheTuple
 
typedef struct ResultCacheKey ResultCacheKey
 
typedef struct ResultCacheEntry ResultCacheEntry
 

Functions

static uint32 ResultCacheHash_hash (struct resultcache_hash *tb, const ResultCacheKey *key)
 
static int ResultCacheHash_equal (struct resultcache_hash *tb, const ResultCacheKey *params1, const ResultCacheKey *params2)
 
static void build_hash_table (ResultCacheState *rcstate, uint32 size)
 
static void prepare_probe_slot (ResultCacheState *rcstate, ResultCacheKey *key)
 
static void entry_purge_tuples (ResultCacheState *rcstate, ResultCacheEntry *entry)
 
static void remove_cache_entry (ResultCacheState *rcstate, ResultCacheEntry *entry)
 
static bool cache_reduce_memory (ResultCacheState *rcstate, ResultCacheKey *specialkey)
 
static ResultCacheEntrycache_lookup (ResultCacheState *rcstate, bool *found)
 
static bool cache_store_tuple (ResultCacheState *rcstate, TupleTableSlot *slot)
 
static TupleTableSlotExecResultCache (PlanState *pstate)
 
ResultCacheStateExecInitResultCache (ResultCache *node, EState *estate, int eflags)
 
void ExecEndResultCache (ResultCacheState *node)
 
void ExecReScanResultCache (ResultCacheState *node)
 
double ExecEstimateCacheEntryOverheadBytes (double ntuples)
 
void ExecResultCacheEstimate (ResultCacheState *node, ParallelContext *pcxt)
 
void ExecResultCacheInitializeDSM (ResultCacheState *node, ParallelContext *pcxt)
 
void ExecResultCacheInitializeWorker (ResultCacheState *node, ParallelWorkerContext *pwcxt)
 
void ExecResultCacheRetrieveInstrumentation (ResultCacheState *node)
 

Macro Definition Documentation

◆ CACHE_TUPLE_BYTES

#define CACHE_TUPLE_BYTES (   t)
Value:
(sizeof(ResultCacheTuple) + \
(t)->mintuple->t_len)
struct ResultCacheTuple ResultCacheTuple

Definition at line 89 of file nodeResultCache.c.

Referenced by cache_store_tuple(), entry_purge_tuples(), and ExecEndResultCache().

◆ EMPTY_ENTRY_MEMORY_BYTES

#define EMPTY_ENTRY_MEMORY_BYTES (   e)
Value:
(sizeof(ResultCacheEntry) + \
sizeof(ResultCacheKey) + \
(e)->key->params->t_len);
struct ResultCacheEntry ResultCacheEntry
e
Definition: preproc-init.c:82

Definition at line 86 of file nodeResultCache.c.

Referenced by cache_lookup(), ExecEndResultCache(), and remove_cache_entry().

◆ RC_CACHE_BYPASS_MODE

#define RC_CACHE_BYPASS_MODE
Value:
4 /* Bypass mode. Just read from our
* subplan without caching anything */

Definition at line 80 of file nodeResultCache.c.

Referenced by ExecResultCache().

◆ RC_CACHE_FETCH_NEXT_TUPLE

#define RC_CACHE_FETCH_NEXT_TUPLE   2 /* Get another tuple from the cache */

Definition at line 78 of file nodeResultCache.c.

Referenced by ExecResultCache().

◆ RC_CACHE_LOOKUP

#define RC_CACHE_LOOKUP   1 /* Attempt to perform a cache lookup */

Definition at line 77 of file nodeResultCache.c.

Referenced by ExecInitResultCache(), ExecReScanResultCache(), and ExecResultCache().

◆ RC_END_OF_SCAN

#define RC_END_OF_SCAN   5 /* Ready for rescan */

Definition at line 82 of file nodeResultCache.c.

Referenced by ExecResultCache().

◆ RC_FILLING_CACHE

#define RC_FILLING_CACHE   3 /* Read outer node to fill cache */

Definition at line 79 of file nodeResultCache.c.

Referenced by ExecResultCache().

◆ SH_DECLARE

#define SH_DECLARE

Definition at line 129 of file nodeResultCache.c.

◆ SH_DEFINE

#define SH_DEFINE

Definition at line 147 of file nodeResultCache.c.

◆ SH_ELEMENT_TYPE [1/2]

#define SH_ELEMENT_TYPE   ResultCacheEntry

Definition at line 139 of file nodeResultCache.c.

◆ SH_ELEMENT_TYPE [2/2]

#define SH_ELEMENT_TYPE   ResultCacheEntry

Definition at line 139 of file nodeResultCache.c.

◆ SH_EQUAL

#define SH_EQUAL (   tb,
  a,
 
)    (ResultCacheHash_equal(tb, a, b) == 0)

Definition at line 143 of file nodeResultCache.c.

◆ SH_GET_HASH

#define SH_GET_HASH (   tb,
 
)    a->hash

Definition at line 146 of file nodeResultCache.c.

◆ SH_HASH_KEY

#define SH_HASH_KEY (   tb,
  key 
)    ResultCacheHash_hash(tb, key)

Definition at line 142 of file nodeResultCache.c.

◆ SH_KEY

#define SH_KEY   key

Definition at line 141 of file nodeResultCache.c.

◆ SH_KEY_TYPE [1/2]

#define SH_KEY_TYPE   ResultCacheKey *

Definition at line 140 of file nodeResultCache.c.

◆ SH_KEY_TYPE [2/2]

#define SH_KEY_TYPE   ResultCacheKey *

Definition at line 140 of file nodeResultCache.c.

◆ SH_PREFIX [1/2]

#define SH_PREFIX   resultcache

Definition at line 138 of file nodeResultCache.c.

◆ SH_PREFIX [2/2]

#define SH_PREFIX   resultcache

Definition at line 138 of file nodeResultCache.c.

◆ SH_SCOPE [1/2]

#define SH_SCOPE   static inline

Definition at line 144 of file nodeResultCache.c.

◆ SH_SCOPE [2/2]

#define SH_SCOPE   static inline

Definition at line 144 of file nodeResultCache.c.

◆ SH_STORE_HASH

#define SH_STORE_HASH

Definition at line 145 of file nodeResultCache.c.

Typedef Documentation

◆ ResultCacheEntry

◆ ResultCacheKey

◆ ResultCacheTuple

Function Documentation

◆ build_hash_table()

static void build_hash_table ( ResultCacheState rcstate,
uint32  size 
)
static

Definition at line 212 of file nodeResultCache.c.

References ResultCacheState::hashtable, and ResultCacheState::tableContext.

Referenced by ExecInitResultCache().

213 {
214  /* Make a guess at a good size when we're not given a valid size. */
215  if (size == 0)
216  size = 1024;
217 
218  /* resultcache_create will convert the size to a power of 2 */
219  rcstate->hashtable = resultcache_create(rcstate->tableContext, size,
220  rcstate);
221 }
MemoryContext tableContext
Definition: execnodes.h:2100
struct resultcache_hash * hashtable
Definition: execnodes.h:2089

◆ cache_lookup()

static ResultCacheEntry* cache_lookup ( ResultCacheState rcstate,
bool found 
)
static

Definition at line 413 of file nodeResultCache.c.

References Assert, cache_reduce_memory(), ResultCacheEntry::complete, dlist_move_tail(), dlist_push_tail(), EMPTY_ENTRY_MEMORY_BYTES, ExecCopySlotMinimalTuple(), ResultCacheState::hashtable, sort-test::key, ResultCacheEntry::key, ResultCacheState::last_tuple, ResultCacheState::lru_list, ResultCacheKey::lru_node, ResultCacheState::mem_limit, ResultCacheState::mem_used, MemoryContextSwitchTo(), palloc(), ResultCacheKey::params, prepare_probe_slot(), ResultCacheState::probeslot, ResultCacheEntry::status, ResultCacheState::tableContext, ResultCacheEntry::tuplehead, and unlikely.

Referenced by ExecResultCache().

414 {
416  ResultCacheEntry *entry;
417  MemoryContext oldcontext;
418 
419  /* prepare the probe slot with the current scan parameters */
420  prepare_probe_slot(rcstate, NULL);
421 
422  /*
423  * Add the new entry to the cache. No need to pass a valid key since the
424  * hash function uses rcstate's probeslot, which we populated above.
425  */
426  entry = resultcache_insert(rcstate->hashtable, NULL, found);
427 
428  if (*found)
429  {
430  /*
431  * Move existing entry to the tail of the LRU list to mark it as the
432  * most recently used item.
433  */
434  dlist_move_tail(&rcstate->lru_list, &entry->key->lru_node);
435 
436  return entry;
437  }
438 
439  oldcontext = MemoryContextSwitchTo(rcstate->tableContext);
440 
441  /* Allocate a new key */
442  entry->key = key = (ResultCacheKey *) palloc(sizeof(ResultCacheKey));
443  key->params = ExecCopySlotMinimalTuple(rcstate->probeslot);
444 
445  /* Update the total cache memory utilization */
446  rcstate->mem_used += EMPTY_ENTRY_MEMORY_BYTES(entry);
447 
448  /* Initialize this entry */
449  entry->complete = false;
450  entry->tuplehead = NULL;
451 
452  /*
453  * Since this is the most recently used entry, push this entry onto the
454  * end of the LRU list.
455  */
456  dlist_push_tail(&rcstate->lru_list, &entry->key->lru_node);
457 
458  rcstate->last_tuple = NULL;
459 
460  MemoryContextSwitchTo(oldcontext);
461 
462  /*
463  * If we've gone over our memory budget, then we'll free up some space in
464  * the cache.
465  */
466  if (rcstate->mem_used > rcstate->mem_limit)
467  {
468  /*
469  * Try to free up some memory. It's highly unlikely that we'll fail
470  * to do so here since the entry we've just added is yet to contain
471  * any tuples and we're able to remove any other entry to reduce the
472  * memory consumption.
473  */
474  if (unlikely(!cache_reduce_memory(rcstate, key)))
475  return NULL;
476 
477  /*
478  * The process of removing entries from the cache may have caused the
479  * code in simplehash.h to shuffle elements to earlier buckets in the
480  * hash table. If it has, we'll need to find the entry again by
481  * performing a lookup. Fortunately, we can detect if this has
482  * happened by seeing if the entry is still in use and that the key
483  * pointer matches our expected key.
484  */
485  if (entry->status != resultcache_SH_IN_USE || entry->key != key)
486  {
487  /*
488  * We need to repopulate the probeslot as lookups performed during
489  * the cache evictions above will have stored some other key.
490  */
491  prepare_probe_slot(rcstate, key);
492 
493  /* Re-find the newly added entry */
494  entry = resultcache_lookup(rcstate->hashtable, NULL);
495  Assert(entry != NULL);
496  }
497  }
498 
499  return entry;
500 }
MemoryContext tableContext
Definition: execnodes.h:2100
ResultCacheTuple * tuplehead
struct resultcache_hash * hashtable
Definition: execnodes.h:2089
static void dlist_push_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:317
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
dlist_node lru_node
ResultCacheKey * key
dlist_head lru_list
Definition: execnodes.h:2101
static MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Definition: tuptable.h:463
#define EMPTY_ENTRY_MEMORY_BYTES(e)
struct ResultCacheTuple * last_tuple
Definition: execnodes.h:2102
MinimalTuple params
static void dlist_move_tail(dlist_head *head, dlist_node *node)
Definition: ilist.h:404
#define Assert(condition)
Definition: c.h:804
static bool cache_reduce_memory(ResultCacheState *rcstate, ResultCacheKey *specialkey)
void * palloc(Size size)
Definition: mcxt.c:1062
static void prepare_probe_slot(ResultCacheState *rcstate, ResultCacheKey *key)
TupleTableSlot * probeslot
Definition: execnodes.h:2092
#define unlikely(x)
Definition: c.h:273

◆ cache_reduce_memory()

static bool cache_reduce_memory ( ResultCacheState rcstate,
ResultCacheKey specialkey 
)
static

Definition at line 328 of file nodeResultCache.c.

References Assert, ResultCacheInstrumentation::cache_evictions, dlist_mutable_iter::cur, dlist_container, dlist_foreach_modify, ResultCacheState::hashtable, sort-test::key, ResultCacheEntry::key, ResultCacheState::lru_list, ResultCacheState::mem_limit, ResultCacheInstrumentation::mem_peak, ResultCacheState::mem_used, prepare_probe_slot(), remove_cache_entry(), and ResultCacheState::stats.

Referenced by cache_lookup(), and cache_store_tuple().

329 {
330  bool specialkey_intact = true; /* for now */
331  dlist_mutable_iter iter;
332  uint64 evictions = 0;
333 
334  /* Update peak memory usage */
335  if (rcstate->mem_used > rcstate->stats.mem_peak)
336  rcstate->stats.mem_peak = rcstate->mem_used;
337 
338  /* We expect only to be called when we've gone over budget on memory */
339  Assert(rcstate->mem_used > rcstate->mem_limit);
340 
341  /* Start the eviction process starting at the head of the LRU list. */
342  dlist_foreach_modify(iter, &rcstate->lru_list)
343  {
345  iter.cur);
346  ResultCacheEntry *entry;
347 
348  /*
349  * Populate the hash probe slot in preparation for looking up this LRU
350  * entry.
351  */
352  prepare_probe_slot(rcstate, key);
353 
354  /*
355  * Ideally the LRU list pointers would be stored in the entry itself
356  * rather than in the key. Unfortunately, we can't do that as the
357  * simplehash.h code may resize the table and allocate new memory for
358  * entries which would result in those pointers pointing to the old
359  * buckets. However, it's fine to use the key to store this as that's
360  * only referenced by a pointer in the entry, which of course follows
361  * the entry whenever the hash table is resized. Since we only have a
362  * pointer to the key here, we must perform a hash table lookup to
363  * find the entry that the key belongs to.
364  */
365  entry = resultcache_lookup(rcstate->hashtable, NULL);
366 
367  /* A good spot to check for corruption of the table and LRU list. */
368  Assert(entry != NULL);
369  Assert(entry->key == key);
370 
371  /*
372  * If we're being called to free memory while the cache is being
373  * populated with new tuples, then we'd better take some care as we
374  * could end up freeing the entry which 'specialkey' belongs to.
375  * Generally callers will pass 'specialkey' as the key for the cache
376  * entry which is currently being populated, so we must set
377  * 'specialkey_intact' to false to inform the caller the specialkey
378  * entry has been removed.
379  */
380  if (key == specialkey)
381  specialkey_intact = false;
382 
383  /*
384  * Finally remove the entry. This will remove from the LRU list too.
385  */
386  remove_cache_entry(rcstate, entry);
387 
388  evictions++;
389 
390  /* Exit if we've freed enough memory */
391  if (rcstate->mem_used <= rcstate->mem_limit)
392  break;
393  }
394 
395  rcstate->stats.cache_evictions += evictions; /* Update Stats */
396 
397  return specialkey_intact;
398 }
dlist_node * cur
Definition: ilist.h:180
#define dlist_foreach_modify(iter, lhead)
Definition: ilist.h:543
struct resultcache_hash * hashtable
Definition: execnodes.h:2089
ResultCacheInstrumentation stats
Definition: execnodes.h:2110
#define dlist_container(type, membername, ptr)
Definition: ilist.h:496
ResultCacheKey * key
dlist_head lru_list
Definition: execnodes.h:2101
#define Assert(condition)
Definition: c.h:804
static void remove_cache_entry(ResultCacheState *rcstate, ResultCacheEntry *entry)
static void prepare_probe_slot(ResultCacheState *rcstate, ResultCacheKey *key)

◆ cache_store_tuple()

static bool cache_store_tuple ( ResultCacheState rcstate,
TupleTableSlot slot 
)
static

Definition at line 510 of file nodeResultCache.c.

References Assert, cache_reduce_memory(), CACHE_TUPLE_BYTES, ResultCacheState::entry, ExecCopySlotMinimalTuple(), ResultCacheState::hashtable, sort-test::key, ResultCacheEntry::key, ResultCacheState::last_tuple, ResultCacheState::mem_limit, ResultCacheState::mem_used, MemoryContextSwitchTo(), ResultCacheTuple::mintuple, ResultCacheTuple::next, palloc(), prepare_probe_slot(), ResultCacheEntry::status, ResultCacheState::tableContext, and ResultCacheEntry::tuplehead.

Referenced by ExecResultCache().

511 {
512  ResultCacheTuple *tuple;
513  ResultCacheEntry *entry = rcstate->entry;
514  MemoryContext oldcontext;
515 
516  Assert(slot != NULL);
517  Assert(entry != NULL);
518 
519  oldcontext = MemoryContextSwitchTo(rcstate->tableContext);
520 
521  tuple = (ResultCacheTuple *) palloc(sizeof(ResultCacheTuple));
522  tuple->mintuple = ExecCopySlotMinimalTuple(slot);
523  tuple->next = NULL;
524 
525  /* Account for the memory we just consumed */
526  rcstate->mem_used += CACHE_TUPLE_BYTES(tuple);
527 
528  if (entry->tuplehead == NULL)
529  {
530  /*
531  * This is the first tuple for this entry, so just point the list head
532  * to it.
533  */
534  entry->tuplehead = tuple;
535  }
536  else
537  {
538  /* push this tuple onto the tail of the list */
539  rcstate->last_tuple->next = tuple;
540  }
541 
542  rcstate->last_tuple = tuple;
543  MemoryContextSwitchTo(oldcontext);
544 
545  /*
546  * If we've gone over our memory budget then free up some space in the
547  * cache.
548  */
549  if (rcstate->mem_used > rcstate->mem_limit)
550  {
551  ResultCacheKey *key = entry->key;
552 
553  if (!cache_reduce_memory(rcstate, key))
554  return false;
555 
556  /*
557  * The process of removing entries from the cache may have caused the
558  * code in simplehash.h to shuffle elements to earlier buckets in the
559  * hash table. If it has, we'll need to find the entry again by
560  * performing a lookup. Fortunately, we can detect if this has
561  * happened by seeing if the entry is still in use and that the key
562  * pointer matches our expected key.
563  */
564  if (entry->status != resultcache_SH_IN_USE || entry->key != key)
565  {
566  /*
567  * We need to repopulate the probeslot as lookups performed during
568  * the cache evictions above will have stored some other key.
569  */
570  prepare_probe_slot(rcstate, key);
571 
572  /* Re-find the entry */
573  rcstate->entry = entry = resultcache_lookup(rcstate->hashtable,
574  NULL);
575  Assert(entry != NULL);
576  }
577  }
578 
579  return true;
580 }
MemoryContext tableContext
Definition: execnodes.h:2100
ResultCacheTuple * tuplehead
struct resultcache_hash * hashtable
Definition: execnodes.h:2089
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
ResultCacheKey * key
static MinimalTuple ExecCopySlotMinimalTuple(TupleTableSlot *slot)
Definition: tuptable.h:463
struct ResultCacheEntry * entry
Definition: execnodes.h:2106
MinimalTuple mintuple
struct ResultCacheTuple * last_tuple
Definition: execnodes.h:2102
#define CACHE_TUPLE_BYTES(t)
#define Assert(condition)
Definition: c.h:804
static bool cache_reduce_memory(ResultCacheState *rcstate, ResultCacheKey *specialkey)
void * palloc(Size size)
Definition: mcxt.c:1062
static void prepare_probe_slot(ResultCacheState *rcstate, ResultCacheKey *key)
struct ResultCacheTuple * next

◆ entry_purge_tuples()

static void entry_purge_tuples ( ResultCacheState rcstate,
ResultCacheEntry entry 
)
inlinestatic

Definition at line 265 of file nodeResultCache.c.

References CACHE_TUPLE_BYTES, ResultCacheEntry::complete, ResultCacheState::mem_used, ResultCacheTuple::mintuple, ResultCacheTuple::next, pfree(), and ResultCacheEntry::tuplehead.

Referenced by ExecResultCache(), and remove_cache_entry().

266 {
267  ResultCacheTuple *tuple = entry->tuplehead;
268  uint64 freed_mem = 0;
269 
270  while (tuple != NULL)
271  {
272  ResultCacheTuple *next = tuple->next;
273 
274  freed_mem += CACHE_TUPLE_BYTES(tuple);
275 
276  /* Free memory used for this tuple */
277  pfree(tuple->mintuple);
278  pfree(tuple);
279 
280  tuple = next;
281  }
282 
283  entry->complete = false;
284  entry->tuplehead = NULL;
285 
286  /* Update the memory accounting */
287  rcstate->mem_used -= freed_mem;
288 }
static int32 next
Definition: blutils.c:219
ResultCacheTuple * tuplehead
void pfree(void *pointer)
Definition: mcxt.c:1169
MinimalTuple mintuple
#define CACHE_TUPLE_BYTES(t)
struct ResultCacheTuple * next

◆ ExecEndResultCache()

void ExecEndResultCache ( ResultCacheState node)

Definition at line 943 of file nodeResultCache.c.

References Assert, CACHE_TUPLE_BYTES, EMPTY_ENTRY_MEMORY_BYTES, ExecClearTuple(), ExecEndNode(), ExecFreeExprContext(), ResultCacheState::hashtable, i, IsParallelWorker, ResultCacheInstrumentation::mem_peak, ResultCacheState::mem_used, MemoryContextDelete(), ResultCacheTuple::next, outerPlanState, ParallelWorkerNumber, ScanState::ps, PlanState::ps_ResultTupleSlot, ResultCacheState::shared_info, SharedResultCacheInfo::sinstrument, ResultCacheState::ss, ScanState::ss_ScanTupleSlot, ResultCacheState::stats, ResultCacheState::tableContext, and ResultCacheEntry::tuplehead.

Referenced by ExecEndNode().

944 {
945 #ifdef USE_ASSERT_CHECKING
946  /* Validate the memory accounting code is correct in assert builds. */
947  {
948  int count;
949  uint64 mem = 0;
950  resultcache_iterator i;
951  ResultCacheEntry *entry;
952 
953  resultcache_start_iterate(node->hashtable, &i);
954 
955  count = 0;
956  while ((entry = resultcache_iterate(node->hashtable, &i)) != NULL)
957  {
958  ResultCacheTuple *tuple = entry->tuplehead;
959 
960  mem += EMPTY_ENTRY_MEMORY_BYTES(entry);
961  while (tuple != NULL)
962  {
963  mem += CACHE_TUPLE_BYTES(tuple);
964  tuple = tuple->next;
965  }
966  count++;
967  }
968 
969  Assert(count == node->hashtable->members);
970  Assert(mem == node->mem_used);
971  }
972 #endif
973 
974  /*
975  * When ending a parallel worker, copy the statistics gathered by the
976  * worker back into shared memory so that it can be picked up by the main
977  * process to report in EXPLAIN ANALYZE.
978  */
979  if (node->shared_info != NULL && IsParallelWorker())
980  {
982 
983  /* Make mem_peak available for EXPLAIN */
984  if (node->stats.mem_peak == 0)
985  node->stats.mem_peak = node->mem_used;
986 
987  Assert(ParallelWorkerNumber <= node->shared_info->num_workers);
989  memcpy(si, &node->stats, sizeof(ResultCacheInstrumentation));
990  }
991 
992  /* Remove the cache context */
994 
996  /* must drop pointer to cache result tuple */
998 
999  /*
1000  * free exprcontext
1001  */
1002  ExecFreeExprContext(&node->ss.ps);
1003 
1004  /*
1005  * shut down the subplan
1006  */
1007  ExecEndNode(outerPlanState(node));
1008 }
MemoryContext tableContext
Definition: execnodes.h:2100
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
ScanState ss
Definition: execnodes.h:2086
ResultCacheTuple * tuplehead
void ExecEndNode(PlanState *node)
Definition: execProcnode.c:556
struct resultcache_hash * hashtable
Definition: execnodes.h:2089
TupleTableSlot * ss_ScanTupleSlot
Definition: execnodes.h:1379
SharedResultCacheInfo * shared_info
Definition: execnodes.h:2111
ResultCacheInstrumentation sinstrument[FLEXIBLE_ARRAY_MEMBER]
Definition: execnodes.h:2074
void ExecFreeExprContext(PlanState *planstate)
Definition: execUtils.c:650
ResultCacheInstrumentation stats
Definition: execnodes.h:2110
PlanState ps
Definition: execnodes.h:1376
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1003
#define outerPlanState(node)
Definition: execnodes.h:1061
#define EMPTY_ENTRY_MEMORY_BYTES(e)
int ParallelWorkerNumber
Definition: parallel.c:112
#define IsParallelWorker()
Definition: parallel.h:61
#define CACHE_TUPLE_BYTES(t)
#define Assert(condition)
Definition: c.h:804
int i
struct ResultCacheTuple * next

◆ ExecEstimateCacheEntryOverheadBytes()

double ExecEstimateCacheEntryOverheadBytes ( double  ntuples)

Definition at line 1037 of file nodeResultCache.c.

Referenced by cost_resultcache_rescan().

1038 {
1039  return sizeof(ResultCacheEntry) + sizeof(ResultCacheKey) +
1040  sizeof(ResultCacheTuple) * ntuples;
1041 }
struct ResultCacheEntry ResultCacheEntry
struct ResultCacheTuple ResultCacheTuple

◆ ExecInitResultCache()

ResultCacheState* ExecInitResultCache ( ResultCache node,
EState estate,
int  eflags 
)

Definition at line 825 of file nodeResultCache.c.

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, Assert, build_hash_table(), ResultCacheState::cache_eq_expr, ResultCache::collations, ResultCacheState::collations, CurrentMemoryContext, dlist_init(), elog, ERROR, ResultCache::est_entries, EXEC_FLAG_BACKWARD, EXEC_FLAG_MARK, ExecAssignExprContext(), ExecBuildParamSetEqual(), ExecCreateScanSlotFromOuterPlan(), ExecInitExpr(), ExecInitNode(), ExecInitResultTupleSlotTL(), PlanState::ExecProcNode, ExecResultCache(), ExecTypeFromExprList(), fmgr_info(), get_hash_mem(), get_op_hash_functions(), get_opcode(), ResultCacheState::hashfunctions, ResultCacheState::hashkeydesc, ResultCache::hashOperators, i, list_nth(), makeNode, MakeSingleTupleTableSlot(), ResultCacheState::nkeys, ResultCache::numKeys, outerPlan, outerPlanState, palloc(), ResultCache::param_exprs, ResultCacheState::param_exprs, pfree(), PlanState::plan, ResultCacheState::probeslot, ScanState::ps, PlanState::ps_ProjInfo, RC_CACHE_LOOKUP, ResultCacheState::rc_status, ResultCache::singlerow, ResultCacheState::ss, PlanState::state, ResultCacheState::tableslot, TTSOpsMinimalTuple, and TTSOpsVirtual.

Referenced by ExecInitNode().

826 {
828  Plan *outerNode;
829  int i;
830  int nkeys;
831  Oid *eqfuncoids;
832 
833  /* check for unsupported flags */
834  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
835 
836  rcstate->ss.ps.plan = (Plan *) node;
837  rcstate->ss.ps.state = estate;
838  rcstate->ss.ps.ExecProcNode = ExecResultCache;
839 
840  /*
841  * Miscellaneous initialization
842  *
843  * create expression context for node
844  */
845  ExecAssignExprContext(estate, &rcstate->ss.ps);
846 
847  outerNode = outerPlan(node);
848  outerPlanState(rcstate) = ExecInitNode(outerNode, estate, eflags);
849 
850  /*
851  * Initialize return slot and type. No need to initialize projection info
852  * because this node doesn't do projections.
853  */
855  rcstate->ss.ps.ps_ProjInfo = NULL;
856 
857  /*
858  * Initialize scan slot and type.
859  */
861 
862  /*
863  * Set the state machine to lookup the cache. We won't find anything
864  * until we cache something, but this saves a special case to create the
865  * first entry.
866  */
867  rcstate->rc_status = RC_CACHE_LOOKUP;
868 
869  rcstate->nkeys = nkeys = node->numKeys;
870  rcstate->hashkeydesc = ExecTypeFromExprList(node->param_exprs);
871  rcstate->tableslot = MakeSingleTupleTableSlot(rcstate->hashkeydesc,
873  rcstate->probeslot = MakeSingleTupleTableSlot(rcstate->hashkeydesc,
874  &TTSOpsVirtual);
875 
876  rcstate->param_exprs = (ExprState **) palloc(nkeys * sizeof(ExprState *));
877  rcstate->collations = node->collations; /* Just point directly to the plan
878  * data */
879  rcstate->hashfunctions = (FmgrInfo *) palloc(nkeys * sizeof(FmgrInfo));
880 
881  eqfuncoids = palloc(nkeys * sizeof(Oid));
882 
883  for (i = 0; i < nkeys; i++)
884  {
885  Oid hashop = node->hashOperators[i];
886  Oid left_hashfn;
887  Oid right_hashfn;
888  Expr *param_expr = (Expr *) list_nth(node->param_exprs, i);
889 
890  if (!get_op_hash_functions(hashop, &left_hashfn, &right_hashfn))
891  elog(ERROR, "could not find hash function for hash operator %u",
892  hashop);
893 
894  fmgr_info(left_hashfn, &rcstate->hashfunctions[i]);
895 
896  rcstate->param_exprs[i] = ExecInitExpr(param_expr, (PlanState *) rcstate);
897  eqfuncoids[i] = get_opcode(hashop);
898  }
899 
902  &TTSOpsVirtual,
903  eqfuncoids,
904  node->collations,
905  node->param_exprs,
906  (PlanState *) rcstate);
907 
908  pfree(eqfuncoids);
909  rcstate->mem_used = 0;
910 
911  /* Limit the total memory consumed by the cache to this */
912  rcstate->mem_limit = get_hash_mem() * 1024L;
913 
914  /* A memory context dedicated for the cache */
916  "ResultCacheHashTable",
918 
919  dlist_init(&rcstate->lru_list);
920  rcstate->last_tuple = NULL;
921  rcstate->entry = NULL;
922 
923  /*
924  * Mark if we can assume the cache entry is completed after we get the
925  * first record for it. Some callers might not call us again after
926  * getting the first match. e.g. A join operator performing a unique join
927  * is able to skip to the next outer tuple after getting the first
928  * matching inner tuple. In this case, the cache entry is complete after
929  * getting the first tuple. This allows us to mark it as so.
930  */
931  rcstate->singlerow = node->singlerow;
932 
933  /* Zero the statistics counters */
934  memset(&rcstate->stats, 0, sizeof(ResultCacheInstrumentation));
935 
936  /* Allocate and set up the actual cache */
937  build_hash_table(rcstate, node->est_entries);
938 
939  return rcstate;
940 }
MemoryContext tableContext
Definition: execnodes.h:2100
uint32 est_entries
Definition: plannodes.h:799
Definition: fmgr.h:56
#define AllocSetContextCreate
Definition: memutils.h:173
bool get_op_hash_functions(Oid opno, RegProcedure *lhs_procno, RegProcedure *rhs_procno)
Definition: lsyscache.c:508
ProjectionInfo * ps_ProjInfo
Definition: execnodes.h:1005
ScanState ss
Definition: execnodes.h:2086
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
TupleTableSlot * tableslot
Definition: execnodes.h:2091
ExprState * ExecBuildParamSetEqual(TupleDesc desc, const TupleTableSlotOps *lops, const TupleTableSlotOps *rops, const Oid *eqfunctions, const Oid *collations, const List *param_exprs, PlanState *parent)
Definition: execExpr.c:3840
const TupleTableSlotOps TTSOpsVirtual
Definition: execTuples.c:83
FmgrInfo * hashfunctions
Definition: execnodes.h:2096
EState * state
Definition: execnodes.h:967
unsigned int Oid
Definition: postgres_ext.h:31
Oid * hashOperators
Definition: plannodes.h:793
ResultCacheInstrumentation stats
Definition: execnodes.h:2110
PlanState ps
Definition: execnodes.h:1376
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ERROR
Definition: elog.h:46
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
TupleDesc hashkeydesc
Definition: execnodes.h:2090
void fmgr_info(Oid functionId, FmgrInfo *finfo)
Definition: fmgr.c:126
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
#define EXEC_FLAG_BACKWARD
Definition: executor.h:58
#define outerPlanState(node)
Definition: execnodes.h:1061
ExprState ** param_exprs
Definition: execnodes.h:2094
dlist_head lru_list
Definition: execnodes.h:2101
bool singlerow
Definition: plannodes.h:796
struct ResultCacheEntry * entry
Definition: execnodes.h:2106
struct ResultCacheTuple * last_tuple
Definition: execnodes.h:2102
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
#define outerPlan(node)
Definition: plannodes.h:171
#define RC_CACHE_LOOKUP
ExecProcNodeMtd ExecProcNode
Definition: execnodes.h:971
Plan * plan
Definition: execnodes.h:965
static void dlist_init(dlist_head *head)
Definition: ilist.h:278
RegProcedure get_opcode(Oid opno)
Definition: lsyscache.c:1256
#define makeNode(_type_)
Definition: nodes.h:587
#define Assert(condition)
Definition: c.h:804
static void build_hash_table(ResultCacheState *rcstate, uint32 size)
#define EXEC_FLAG_MARK
Definition: executor.h:59
Oid * collations
Definition: plannodes.h:794
ExprState * cache_eq_expr
Definition: execnodes.h:2093
void ExecAssignExprContext(EState *estate, PlanState *planstate)
Definition: execUtils.c:480
void ExecInitResultTupleSlotTL(PlanState *planstate, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1799
List * param_exprs
Definition: plannodes.h:795
TupleDesc ExecTypeFromExprList(List *exprList)
Definition: execTuples.c:1997
void * palloc(Size size)
Definition: mcxt.c:1062
static TupleTableSlot * ExecResultCache(PlanState *pstate)
#define elog(elevel,...)
Definition: elog.h:232
int i
TupleTableSlot * probeslot
Definition: execnodes.h:2092
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:123
void ExecCreateScanSlotFromOuterPlan(EState *estate, ScanState *scanstate, const TupleTableSlotOps *tts_ops)
Definition: execUtils.c:682
PlanState * ExecInitNode(Plan *node, EState *estate, int eflags)
Definition: execProcnode.c:141
const TupleTableSlotOps TTSOpsMinimalTuple
Definition: execTuples.c:85
int get_hash_mem(void)
Definition: nodeHash.c:3389

◆ ExecReScanResultCache()

void ExecReScanResultCache ( ResultCacheState node)

Definition at line 1011 of file nodeResultCache.c.

References PlanState::chgParam, ResultCacheState::entry, ExecReScan(), ResultCacheState::last_tuple, outerPlan, outerPlanState, RC_CACHE_LOOKUP, and ResultCacheState::rc_status.

Referenced by ExecReScan().

1012 {
1014 
1015  /* Mark that we must lookup the cache for a new set of parameters */
1016  node->rc_status = RC_CACHE_LOOKUP;
1017 
1018  /* nullify pointers used for the last scan */
1019  node->entry = NULL;
1020  node->last_tuple = NULL;
1021 
1022  /*
1023  * if chgParam of subnode is not null then plan will be re-scanned by
1024  * first ExecProcNode.
1025  */
1026  if (outerPlan->chgParam == NULL)
1027  ExecReScan(outerPlan);
1028 
1029 }
void ExecReScan(PlanState *node)
Definition: execAmi.c:78
#define outerPlanState(node)
Definition: execnodes.h:1061
struct ResultCacheEntry * entry
Definition: execnodes.h:2106
struct ResultCacheTuple * last_tuple
Definition: execnodes.h:2102
Bitmapset * chgParam
Definition: execnodes.h:997
#define outerPlan(node)
Definition: plannodes.h:171
#define RC_CACHE_LOOKUP

◆ ExecResultCache()

static TupleTableSlot* ExecResultCache ( PlanState pstate)
static

Definition at line 583 of file nodeResultCache.c.

References Assert, ResultCacheInstrumentation::cache_hits, cache_lookup(), ResultCacheInstrumentation::cache_misses, ResultCacheInstrumentation::cache_overflows, cache_store_tuple(), castNode, ResultCacheEntry::complete, elog, ResultCacheState::entry, entry_purge_tuples(), ERROR, ExecCopySlot(), ExecProcNode(), ExecStoreMinimalTuple(), ResultCacheState::last_tuple, likely, ResultCacheTuple::mintuple, ResultCacheTuple::next, outerPlanState, ScanState::ps, PlanState::ps_ResultTupleSlot, RC_CACHE_BYPASS_MODE, RC_CACHE_FETCH_NEXT_TUPLE, RC_CACHE_LOOKUP, RC_END_OF_SCAN, RC_FILLING_CACHE, ResultCacheState::rc_status, ResultCacheState::singlerow, ResultCacheState::ss, ResultCacheState::stats, TupIsNull, ResultCacheEntry::tuplehead, and unlikely.

Referenced by ExecInitResultCache().

584 {
585  ResultCacheState *node = castNode(ResultCacheState, pstate);
586  PlanState *outerNode;
587  TupleTableSlot *slot;
588 
589  switch (node->rc_status)
590  {
591  case RC_CACHE_LOOKUP:
592  {
593  ResultCacheEntry *entry;
594  TupleTableSlot *outerslot;
595  bool found;
596 
597  Assert(node->entry == NULL);
598 
599  /*
600  * We're only ever in this state for the first call of the
601  * scan. Here we have a look to see if we've already seen the
602  * current parameters before and if we have already cached a
603  * complete set of records that the outer plan will return for
604  * these parameters.
605  *
606  * When we find a valid cache entry, we'll return the first
607  * tuple from it. If not found, we'll create a cache entry and
608  * then try to fetch a tuple from the outer scan. If we find
609  * one there, we'll try to cache it.
610  */
611 
612  /* see if we've got anything cached for the current parameters */
613  entry = cache_lookup(node, &found);
614 
615  if (found && entry->complete)
616  {
617  node->stats.cache_hits += 1; /* stats update */
618 
619  /*
620  * Set last_tuple and entry so that the state
621  * RC_CACHE_FETCH_NEXT_TUPLE can easily find the next
622  * tuple for these parameters.
623  */
624  node->last_tuple = entry->tuplehead;
625  node->entry = entry;
626 
627  /* Fetch the first cached tuple, if there is one */
628  if (entry->tuplehead)
629  {
631 
632  slot = node->ss.ps.ps_ResultTupleSlot;
634  slot, false);
635 
636  return slot;
637  }
638 
639  /* The cache entry is void of any tuples. */
640  node->rc_status = RC_END_OF_SCAN;
641  return NULL;
642  }
643 
644  /* Handle cache miss */
645  node->stats.cache_misses += 1; /* stats update */
646 
647  if (found)
648  {
649  /*
650  * A cache entry was found, but the scan for that entry
651  * did not run to completion. We'll just remove all
652  * tuples and start again. It might be tempting to
653  * continue where we left off, but there's no guarantee
654  * the outer node will produce the tuples in the same
655  * order as it did last time.
656  */
657  entry_purge_tuples(node, entry);
658  }
659 
660  /* Scan the outer node for a tuple to cache */
661  outerNode = outerPlanState(node);
662  outerslot = ExecProcNode(outerNode);
663  if (TupIsNull(outerslot))
664  {
665  /*
666  * cache_lookup may have returned NULL due to failure to
667  * free enough cache space, so ensure we don't do anything
668  * here that assumes it worked. There's no need to go into
669  * bypass mode here as we're setting rc_status to end of
670  * scan.
671  */
672  if (likely(entry))
673  entry->complete = true;
674 
675  node->rc_status = RC_END_OF_SCAN;
676  return NULL;
677  }
678 
679  node->entry = entry;
680 
681  /*
682  * If we failed to create the entry or failed to store the
683  * tuple in the entry, then go into bypass mode.
684  */
685  if (unlikely(entry == NULL ||
686  !cache_store_tuple(node, outerslot)))
687  {
688  node->stats.cache_overflows += 1; /* stats update */
689 
691 
692  /*
693  * No need to clear out last_tuple as we'll stay in bypass
694  * mode until the end of the scan.
695  */
696  }
697  else
698  {
699  /*
700  * If we only expect a single row from this scan then we
701  * can mark that we're not expecting more. This allows
702  * cache lookups to work even when the scan has not been
703  * executed to completion.
704  */
705  entry->complete = node->singlerow;
706  node->rc_status = RC_FILLING_CACHE;
707  }
708 
709  slot = node->ss.ps.ps_ResultTupleSlot;
710  ExecCopySlot(slot, outerslot);
711  return slot;
712  }
713 
715  {
716  /* We shouldn't be in this state if these are not set */
717  Assert(node->entry != NULL);
718  Assert(node->last_tuple != NULL);
719 
720  /* Skip to the next tuple to output */
721  node->last_tuple = node->last_tuple->next;
722 
723  /* No more tuples in the cache */
724  if (node->last_tuple == NULL)
725  {
726  node->rc_status = RC_END_OF_SCAN;
727  return NULL;
728  }
729 
730  slot = node->ss.ps.ps_ResultTupleSlot;
732  false);
733 
734  return slot;
735  }
736 
737  case RC_FILLING_CACHE:
738  {
739  TupleTableSlot *outerslot;
740  ResultCacheEntry *entry = node->entry;
741 
742  /* entry should already have been set by RC_CACHE_LOOKUP */
743  Assert(entry != NULL);
744 
745  /*
746  * When in the RC_FILLING_CACHE state, we've just had a cache
747  * miss and are populating the cache with the current scan
748  * tuples.
749  */
750  outerNode = outerPlanState(node);
751  outerslot = ExecProcNode(outerNode);
752  if (TupIsNull(outerslot))
753  {
754  /* No more tuples. Mark it as complete */
755  entry->complete = true;
756  node->rc_status = RC_END_OF_SCAN;
757  return NULL;
758  }
759 
760  /*
761  * Validate if the planner properly set the singlerow flag. It
762  * should only set that if each cache entry can, at most,
763  * return 1 row. XXX maybe this should be an Assert?
764  */
765  if (unlikely(entry->complete))
766  elog(ERROR, "cache entry already complete");
767 
768  /* Record the tuple in the current cache entry */
769  if (unlikely(!cache_store_tuple(node, outerslot)))
770  {
771  /* Couldn't store it? Handle overflow */
772  node->stats.cache_overflows += 1; /* stats update */
773 
775 
776  /*
777  * No need to clear out entry or last_tuple as we'll stay
778  * in bypass mode until the end of the scan.
779  */
780  }
781 
782  slot = node->ss.ps.ps_ResultTupleSlot;
783  ExecCopySlot(slot, outerslot);
784  return slot;
785  }
786 
788  {
789  TupleTableSlot *outerslot;
790 
791  /*
792  * When in bypass mode we just continue to read tuples without
793  * caching. We need to wait until the next rescan before we
794  * can come out of this mode.
795  */
796  outerNode = outerPlanState(node);
797  outerslot = ExecProcNode(outerNode);
798  if (TupIsNull(outerslot))
799  {
800  node->rc_status = RC_END_OF_SCAN;
801  return NULL;
802  }
803 
804  slot = node->ss.ps.ps_ResultTupleSlot;
805  ExecCopySlot(slot, outerslot);
806  return slot;
807  }
808 
809  case RC_END_OF_SCAN:
810 
811  /*
812  * We've already returned NULL for this scan, but just in case
813  * something calls us again by mistake.
814  */
815  return NULL;
816 
817  default:
818  elog(ERROR, "unrecognized resultcache state: %d",
819  (int) node->rc_status);
820  return NULL;
821  } /* switch */
822 }
static TupleTableSlot * ExecCopySlot(TupleTableSlot *dstslot, TupleTableSlot *srcslot)
Definition: tuptable.h:475
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1446
#define likely(x)
Definition: c.h:272
#define RC_FILLING_CACHE
ScanState ss
Definition: execnodes.h:2086
ResultCacheTuple * tuplehead
#define castNode(_type_, nodeptr)
Definition: nodes.h:608
static bool cache_store_tuple(ResultCacheState *rcstate, TupleTableSlot *slot)
ResultCacheInstrumentation stats
Definition: execnodes.h:2110
PlanState ps
Definition: execnodes.h:1376
TupleTableSlot * ps_ResultTupleSlot
Definition: execnodes.h:1003
#define ERROR
Definition: elog.h:46
#define RC_CACHE_BYPASS_MODE
#define outerPlanState(node)
Definition: execnodes.h:1061
struct ResultCacheEntry * entry
Definition: execnodes.h:2106
#define TupIsNull(slot)
Definition: tuptable.h:292
MinimalTuple mintuple
struct ResultCacheTuple * last_tuple
Definition: execnodes.h:2102
#define RC_CACHE_LOOKUP
static void entry_purge_tuples(ResultCacheState *rcstate, ResultCacheEntry *entry)
static TupleTableSlot * ExecProcNode(PlanState *node)
Definition: executor.h:252
#define RC_END_OF_SCAN
#define Assert(condition)
Definition: c.h:804
static ResultCacheEntry * cache_lookup(ResultCacheState *rcstate, bool *found)
#define elog(elevel,...)
Definition: elog.h:232
#define unlikely(x)
Definition: c.h:273
#define RC_CACHE_FETCH_NEXT_TUPLE
struct ResultCacheTuple * next

◆ ExecResultCacheEstimate()

void ExecResultCacheEstimate ( ResultCacheState node,
ParallelContext pcxt 
)

Definition at line 1055 of file nodeResultCache.c.

References add_size(), ParallelContext::estimator, PlanState::instrument, mul_size(), ParallelContext::nworkers, offsetof, ScanState::ps, shm_toc_estimate_chunk, shm_toc_estimate_keys, and ResultCacheState::ss.

Referenced by ExecParallelEstimate().

1056 {
1057  Size size;
1058 
1059  /* don't need this if not instrumenting or no workers */
1060  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1061  return;
1062 
1063  size = mul_size(pcxt->nworkers, sizeof(ResultCacheInstrumentation));
1064  size = add_size(size, offsetof(SharedResultCacheInfo, sinstrument));
1065  shm_toc_estimate_chunk(&pcxt->estimator, size);
1066  shm_toc_estimate_keys(&pcxt->estimator, 1);
1067 }
Instrumentation * instrument
Definition: execnodes.h:975
ScanState ss
Definition: execnodes.h:2086
shm_toc_estimator estimator
Definition: parallel.h:42
#define shm_toc_estimate_chunk(e, sz)
Definition: shm_toc.h:51
PlanState ps
Definition: execnodes.h:1376
Size mul_size(Size s1, Size s2)
Definition: shmem.c:519
Size add_size(Size s1, Size s2)
Definition: shmem.c:502
size_t Size
Definition: c.h:540
#define shm_toc_estimate_keys(e, cnt)
Definition: shm_toc.h:53
#define offsetof(type, field)
Definition: c.h:727

◆ ExecResultCacheInitializeDSM()

void ExecResultCacheInitializeDSM ( ResultCacheState node,
ParallelContext pcxt 
)

Definition at line 1076 of file nodeResultCache.c.

References PlanState::instrument, SharedResultCacheInfo::num_workers, ParallelContext::nworkers, offsetof, PlanState::plan, Plan::plan_node_id, ScanState::ps, ResultCacheState::shared_info, shm_toc_allocate(), shm_toc_insert(), ResultCacheState::ss, and ParallelContext::toc.

Referenced by ExecParallelInitializeDSM().

1077 {
1078  Size size;
1079 
1080  /* don't need this if not instrumenting or no workers */
1081  if (!node->ss.ps.instrument || pcxt->nworkers == 0)
1082  return;
1083 
1084  size = offsetof(SharedResultCacheInfo, sinstrument)
1085  + pcxt->nworkers * sizeof(ResultCacheInstrumentation);
1086  node->shared_info = shm_toc_allocate(pcxt->toc, size);
1087  /* ensure any unfilled slots will contain zeroes */
1088  memset(node->shared_info, 0, size);
1089  node->shared_info->num_workers = pcxt->nworkers;
1090  shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id,
1091  node->shared_info);
1092 }
Instrumentation * instrument
Definition: execnodes.h:975
ScanState ss
Definition: execnodes.h:2086
struct ResultCacheInstrumentation ResultCacheInstrumentation
int plan_node_id
Definition: plannodes.h:140
SharedResultCacheInfo * shared_info
Definition: execnodes.h:2111
PlanState ps
Definition: execnodes.h:1376
Plan * plan
Definition: execnodes.h:965
size_t Size
Definition: c.h:540
void * shm_toc_allocate(shm_toc *toc, Size nbytes)
Definition: shm_toc.c:88
void shm_toc_insert(shm_toc *toc, uint64 key, void *address)
Definition: shm_toc.c:171
#define offsetof(type, field)
Definition: c.h:727
shm_toc * toc
Definition: parallel.h:45

◆ ExecResultCacheInitializeWorker()

void ExecResultCacheInitializeWorker ( ResultCacheState node,
ParallelWorkerContext pwcxt 
)

Definition at line 1101 of file nodeResultCache.c.

References PlanState::plan, Plan::plan_node_id, ScanState::ps, ResultCacheState::shared_info, shm_toc_lookup(), ResultCacheState::ss, and ParallelWorkerContext::toc.

Referenced by ExecParallelInitializeWorker().

1102 {
1103  node->shared_info =
1104  shm_toc_lookup(pwcxt->toc, node->ss.ps.plan->plan_node_id, true);
1105 }
ScanState ss
Definition: execnodes.h:2086
int plan_node_id
Definition: plannodes.h:140
SharedResultCacheInfo * shared_info
Definition: execnodes.h:2111
PlanState ps
Definition: execnodes.h:1376
Plan * plan
Definition: execnodes.h:965
void * shm_toc_lookup(shm_toc *toc, uint64 key, bool noError)
Definition: shm_toc.c:232

◆ ExecResultCacheRetrieveInstrumentation()

void ExecResultCacheRetrieveInstrumentation ( ResultCacheState node)

Definition at line 1114 of file nodeResultCache.c.

References SharedResultCacheInfo::num_workers, offsetof, palloc(), and ResultCacheState::shared_info.

Referenced by ExecParallelRetrieveInstrumentation().

1115 {
1116  Size size;
1118 
1119  if (node->shared_info == NULL)
1120  return;
1121 
1122  size = offsetof(SharedResultCacheInfo, sinstrument)
1124  si = palloc(size);
1125  memcpy(si, node->shared_info, size);
1126  node->shared_info = si;
1127 }
struct ResultCacheInstrumentation ResultCacheInstrumentation
SharedResultCacheInfo * shared_info
Definition: execnodes.h:2111
size_t Size
Definition: c.h:540
void * palloc(Size size)
Definition: mcxt.c:1062
#define offsetof(type, field)
Definition: c.h:727

◆ prepare_probe_slot()

static void prepare_probe_slot ( ResultCacheState rcstate,
ResultCacheKey key 
)
inlinestatic

Definition at line 230 of file nodeResultCache.c.

References ExecClearTuple(), ExecEvalExpr(), ExecStoreMinimalTuple(), ExecStoreVirtualTuple(), i, ResultCacheState::nkeys, ResultCacheState::param_exprs, ResultCacheKey::params, ResultCacheState::probeslot, ScanState::ps, PlanState::ps_ExprContext, slot_getallattrs(), ResultCacheState::ss, ResultCacheState::tableslot, TupleTableSlot::tts_isnull, and TupleTableSlot::tts_values.

Referenced by cache_lookup(), cache_reduce_memory(), and cache_store_tuple().

231 {
232  TupleTableSlot *pslot = rcstate->probeslot;
233  TupleTableSlot *tslot = rcstate->tableslot;
234  int numKeys = rcstate->nkeys;
235 
236  ExecClearTuple(pslot);
237 
238  if (key == NULL)
239  {
240  /* Set the probeslot's values based on the current parameter values */
241  for (int i = 0; i < numKeys; i++)
242  pslot->tts_values[i] = ExecEvalExpr(rcstate->param_exprs[i],
243  rcstate->ss.ps.ps_ExprContext,
244  &pslot->tts_isnull[i]);
245  }
246  else
247  {
248  /* Process the key's MinimalTuple and store the values in probeslot */
249  ExecStoreMinimalTuple(key->params, tslot, false);
250  slot_getallattrs(tslot);
251  memcpy(pslot->tts_values, tslot->tts_values, sizeof(Datum) * numKeys);
252  memcpy(pslot->tts_isnull, tslot->tts_isnull, sizeof(bool) * numKeys);
253  }
254 
255  ExecStoreVirtualTuple(pslot);
256 }
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1446
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:425
ScanState ss
Definition: execnodes.h:2086
TupleTableSlot * tableslot
Definition: execnodes.h:2091
ExprContext * ps_ExprContext
Definition: execnodes.h:1004
Datum * tts_values
Definition: tuptable.h:126
PlanState ps
Definition: execnodes.h:1376
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:354
ExprState ** param_exprs
Definition: execnodes.h:2094
bool * tts_isnull
Definition: tuptable.h:128
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:316
MinimalTuple params
uintptr_t Datum
Definition: postgres.h:411
int i
TupleTableSlot * probeslot
Definition: execnodes.h:2092
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1552

◆ remove_cache_entry()

static void remove_cache_entry ( ResultCacheState rcstate,
ResultCacheEntry entry 
)
static

Definition at line 295 of file nodeResultCache.c.

References dlist_delete(), EMPTY_ENTRY_MEMORY_BYTES, entry_purge_tuples(), ResultCacheState::hashtable, sort-test::key, ResultCacheEntry::key, ResultCacheKey::lru_node, ResultCacheState::mem_used, ResultCacheKey::params, and pfree().

Referenced by cache_reduce_memory().

296 {
297  ResultCacheKey *key = entry->key;
298 
299  dlist_delete(&entry->key->lru_node);
300 
301  /* Remove all of the tuples from this entry */
302  entry_purge_tuples(rcstate, entry);
303 
304  /*
305  * Update memory accounting. entry_purge_tuples should have already
306  * subtracted the memory used for each cached tuple. Here we just update
307  * the amount used by the entry itself.
308  */
309  rcstate->mem_used -= EMPTY_ENTRY_MEMORY_BYTES(entry);
310 
311  /* Remove the entry from the cache */
312  resultcache_delete_item(rcstate->hashtable, entry);
313 
314  pfree(key->params);
315  pfree(key);
316 }
struct resultcache_hash * hashtable
Definition: execnodes.h:2089
dlist_node lru_node
void pfree(void *pointer)
Definition: mcxt.c:1169
ResultCacheKey * key
#define EMPTY_ENTRY_MEMORY_BYTES(e)
static void dlist_delete(dlist_node *node)
Definition: ilist.h:358
MinimalTuple params
static void entry_purge_tuples(ResultCacheState *rcstate, ResultCacheEntry *entry)

◆ ResultCacheHash_equal()

static int ResultCacheHash_equal ( struct resultcache_hash *  tb,
const ResultCacheKey params1,
const ResultCacheKey params2 
)
static

Definition at line 191 of file nodeResultCache.c.

References ResultCacheState::cache_eq_expr, ExecQualAndReset(), ExecStoreMinimalTuple(), ResultCacheKey::params, ResultCacheState::probeslot, ScanState::ps, PlanState::ps_ExprContext, ResultCacheState::ss, and ResultCacheState::tableslot.

193 {
194  ResultCacheState *rcstate = (ResultCacheState *) tb->private_data;
195  ExprContext *econtext = rcstate->ss.ps.ps_ExprContext;
196  TupleTableSlot *tslot = rcstate->tableslot;
197  TupleTableSlot *pslot = rcstate->probeslot;
198 
199  /* probeslot should have already been prepared by prepare_probe_slot() */
200 
201  ExecStoreMinimalTuple(key1->params, tslot, false);
202 
203  econtext->ecxt_innertuple = tslot;
204  econtext->ecxt_outertuple = pslot;
205  return !ExecQualAndReset(rcstate->cache_eq_expr, econtext);
206 }
TupleTableSlot * ExecStoreMinimalTuple(MinimalTuple mtup, TupleTableSlot *slot, bool shouldFree)
Definition: execTuples.c:1446
ScanState ss
Definition: execnodes.h:2086
TupleTableSlot * tableslot
Definition: execnodes.h:2091
ExprContext * ps_ExprContext
Definition: execnodes.h:1004
PlanState ps
Definition: execnodes.h:1376
static bool ExecQualAndReset(ExprState *state, ExprContext *econtext)
Definition: executor.h:423
ExprState * cache_eq_expr
Definition: execnodes.h:2093
TupleTableSlot * probeslot
Definition: execnodes.h:2092

◆ ResultCacheHash_hash()

static uint32 ResultCacheHash_hash ( struct resultcache_hash *  tb,
const ResultCacheKey key 
)
static

Definition at line 157 of file nodeResultCache.c.

References ResultCacheState::collations, DatumGetUInt32, FunctionCall1Coll(), ResultCacheState::hashfunctions, i, murmurhash32(), ResultCacheState::nkeys, and ResultCacheState::probeslot.

158 {
159  ResultCacheState *rcstate = (ResultCacheState *) tb->private_data;
160  TupleTableSlot *pslot = rcstate->probeslot;
161  uint32 hashkey = 0;
162  int numkeys = rcstate->nkeys;
163  FmgrInfo *hashfunctions = rcstate->hashfunctions;
164  Oid *collations = rcstate->collations;
165 
166  for (int i = 0; i < numkeys; i++)
167  {
168  /* rotate hashkey left 1 bit at each step */
169  hashkey = (hashkey << 1) | ((hashkey & 0x80000000) ? 1 : 0);
170 
171  if (!pslot->tts_isnull[i]) /* treat nulls as having hash key 0 */
172  {
173  uint32 hkey;
174 
175  hkey = DatumGetUInt32(FunctionCall1Coll(&hashfunctions[i],
176  collations[i], pslot->tts_values[i]));
177  hashkey ^= hkey;
178  }
179  }
180 
181  return murmurhash32(hashkey);
182 }
#define DatumGetUInt32(X)
Definition: postgres.h:530
Definition: fmgr.h:56
FmgrInfo * hashfunctions
Definition: execnodes.h:2096
static uint32 murmurhash32(uint32 data)
Definition: hashfn.h:92
unsigned int Oid
Definition: postgres_ext.h:31
unsigned int uint32
Definition: c.h:441
Datum FunctionCall1Coll(FmgrInfo *flinfo, Oid collation, Datum arg1)
Definition: fmgr.c:1128
int i
TupleTableSlot * probeslot
Definition: execnodes.h:2092