diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index b019bc1..ab95fcc 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -299,6 +299,7 @@ initscan(HeapScanDesc scan, ScanKey key, bool keep_startblock) ItemPointerSetInvalid(&scan->rs_ctup.t_self); scan->rs_cbuf = InvalidBuffer; scan->rs_cblock = InvalidBlockNumber; + scan->rs_scanslot = NULL; /* page-at-a-time fields are always invalid when not rs_inited */ @@ -660,7 +661,8 @@ heapgettup(HeapScanDesc scan, if (valid && key != NULL) HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd), - nkeys, key, valid); + NULL, 0, NULL, + nkeys, key, valid); if (valid) { @@ -955,7 +957,9 @@ heapgettup_pagemode(HeapScanDesc scan, bool valid; HeapKeyTest(tuple, RelationGetDescr(scan->rs_rd), - nkeys, key, valid); + scan->rs_scanslot, scan->rs_cbuf, + scan->per_tuple_context, + nkeys, key, valid); if (valid) { scan->rs_cindex = lineindex; @@ -1649,7 +1653,8 @@ heap_parallelscan_initialize(ParallelHeapScanDesc target, Relation relation, * ---------------- */ HeapScanDesc -heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan) +heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan, + int nkeys, ScanKey key) { Snapshot snapshot; @@ -1657,7 +1662,7 @@ heap_beginscan_parallel(Relation relation, ParallelHeapScanDesc parallel_scan) snapshot = RestoreSnapshot(parallel_scan->phs_snapshot_data); RegisterSnapshot(snapshot); - return heap_beginscan_internal(relation, snapshot, 0, NULL, parallel_scan, + return heap_beginscan_internal(relation, snapshot, nkeys, key, parallel_scan, true, true, true, false, false, true); } diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..3a12ec4 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!node->ps.qual || ExecQual(node->ps.qual, econtext, false)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..0f28443 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -30,9 +30,12 @@ #include "executor/execdebug.h" #include "executor/nodeSeqscan.h" #include "utils/rel.h" +#include "optimizer/clauses.h" static void InitScanRelation(SeqScanState *node, EState *estate, int eflags); static TupleTableSlot *SeqNext(SeqScanState *node); +static void get_scankey_from_qual(TupleDesc tupDesc, List *qual, PlanState *ps, + int *nkeys, ScanKey *rs_keys); /* ---------------------------------------------------------------- * Scan Support @@ -64,17 +67,39 @@ SeqNext(SeqScanState *node) if (scandesc == NULL) { + int nkeys = 0; + ScanKey keys = NULL; + + /* + * Pull out scan key from qual list + */ + get_scankey_from_qual(RelationGetDescr(node->ss.ss_currentRelation), + node->ss.ps.plan->qual, &node->ss.ps, + &nkeys, &keys); + /* * We reach here if the scan is not parallel, or if we're executing a * scan that was intended to be parallel serially. */ scandesc = heap_beginscan(node->ss.ss_currentRelation, estate->es_snapshot, - 0, NULL); + nkeys, keys); + node->ss.ss_currentScanDesc = scandesc; } /* + * If we have pushed down the scan keys to heap and scan slot is not + * yet stored in heap scan descriptor then do it. + */ + if (scandesc->rs_nkeys && !scandesc->rs_scanslot) + { + scandesc->rs_scanslot = slot; + scandesc->per_tuple_context = + node->ss.ps.ps_ExprContext->ecxt_per_tuple_memory; + } + + /* * get the next tuple from the table */ tuple = heap_getnext(scandesc, direction); @@ -86,16 +111,18 @@ SeqNext(SeqScanState *node) * not created with palloc() and so should not be pfree()'d. Note also * that ExecStoreTuple will increment the refcount of the buffer; the * refcount will not be dropped until the tuple table slot is cleared. + * + * If scandesc have valid scanslot means we would have already stored + * tuple in slot, so no need to do it again. */ - if (tuple) + if (!tuple) + ExecClearTuple(slot); + else if (!scandesc->rs_scanslot) ExecStoreTuple(tuple, /* tuple to store */ slot, /* slot to store in */ scandesc->rs_cbuf, /* buffer associated with this * tuple */ false); /* don't pfree this pointer */ - else - ExecClearTuple(slot); - return slot; } @@ -317,14 +344,24 @@ ExecSeqScanInitializeDSM(SeqScanState *node, { EState *estate = node->ss.ps.state; ParallelHeapScanDesc pscan; + int nkeys = 0; + ScanKey keys = NULL; + + /* + * Pull out scan key from qual list + */ + get_scankey_from_qual(RelationGetDescr(node->ss.ss_currentRelation), + node->ss.ps.plan->qual, &node->ss.ps, + &nkeys, &keys); pscan = shm_toc_allocate(pcxt->toc, node->pscan_len); heap_parallelscan_initialize(pscan, node->ss.ss_currentRelation, estate->es_snapshot); shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, pscan); - node->ss.ss_currentScanDesc = - heap_beginscan_parallel(node->ss.ss_currentRelation, pscan); + node->ss.ss_currentScanDesc = heap_beginscan_parallel( + node->ss.ss_currentRelation, pscan, + nkeys, keys); } /* ---------------------------------------------------------------- @@ -337,8 +374,129 @@ void ExecSeqScanInitializeWorker(SeqScanState *node, shm_toc *toc) { ParallelHeapScanDesc pscan; + int nkeys = 0; + ScanKey keys = NULL; pscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id); - node->ss.ss_currentScanDesc = - heap_beginscan_parallel(node->ss.ss_currentRelation, pscan); + + /* + * Pull out scan key from qual list + */ + get_scankey_from_qual(RelationGetDescr(node->ss.ss_currentRelation), + node->ss.ps.plan->qual, &node->ss.ps, + &nkeys, &keys); + + node->ss.ss_currentScanDesc = heap_beginscan_parallel( + node->ss.ss_currentRelation, pscan, + nkeys, keys); +} + +/* + * get_scankey_from_qual + * + * process complete QUAL list and create scankey entry for pushable quals. + * + * quals which can be pushed down: + * 1. qual should be of form VAR op CONST. + * 2. VAR should be for datatype with fixed length. + */ +static void +get_scankey_from_qual(TupleDesc tupDesc, List *qual, PlanState *ps, + int *nkeys, ScanKey *rs_keys) +{ + ListCell *l, *l1; + Expr *expr; + ListCell *prev = NULL; + ScanKey key; + Expr *leftop; + Expr *rightop; + AttrNumber varattno; + Datum scanvalue; + + /* + * Validate each qual whether this qual can be push down to heap node + * or not, If this can be pushed down then create a ScanKey entry + * and delete it from qual list of PlanState + */ + forboth(l, qual, l1, ps->qual) + { + expr = (Expr *) lfirst(l); + + if (!IsA(expr, OpExpr)) + { + prev = l1; + continue; + } + + + /* leftop should be the Var, possibly relabeled. */ + leftop = (Expr *) get_leftop(expr); + if (leftop && IsA(leftop, RelabelType)) + leftop = ((RelabelType *) leftop)->arg; + + Assert(leftop != NULL); + + /* If leftop is not var then continue and check next qual. */ + if (!(IsA(leftop, Var))) + { + prev = l1; + continue; + } + + varattno = ((Var *) leftop)->varattno; + + /* + * We don't want to push down the qual which can have variable + * length data. + */ + if (varattno <= 0) + { + prev = l1; + continue; + } + + rightop = (Expr *) get_rightop(expr); + if (rightop && IsA(rightop, RelabelType)) + rightop = ((RelabelType *) rightop)->arg; + + Assert(rightop != NULL); + + /* + * If right op is not constant then we can not push down this key + * so move to next qual. + */ + if (!IsA(rightop, Const)) + { + prev = l1; + continue; + } + + scanvalue = ((Const *) rightop)->constvalue; + + /* If we havn't yet allocated memory for scan keys, then do so. */ + if (*rs_keys == NULL) + { + *rs_keys = (ScanKey) palloc(sizeof(ScanKeyData) * + list_length(qual)); + key = *rs_keys; + } + + /* Create scan key entry */ + ScanKeyInit(key, + varattno, + InvalidStrategy, + ((OpExpr *) expr)->opfuncid, + scanvalue); + + key->sk_collation = ((OpExpr *) expr)->inputcollid; + + key++; + (*nkeys)++; + + /* + * We have created scankey for this qual entry so remove it from + * qual list. + */ + ps->qual = list_delete_cell(ps->qual, l1, prev); + } } diff --git a/src/backend/utils/cache/catcache.c b/src/backend/utils/cache/catcache.c index 6016d19..48e50ce 100644 --- a/src/backend/utils/cache/catcache.c +++ b/src/backend/utils/cache/catcache.c @@ -1168,6 +1168,7 @@ SearchCatCache(CatCache *cache, */ HeapKeyTest(&ct->tuple, cache->cc_tupdesc, + NULL, 0, NULL, cache->cc_nkeys, cur_skey, res); @@ -1459,6 +1460,7 @@ SearchCatCacheList(CatCache *cache, continue; HeapKeyTest(&cl->tuple, cache->cc_tupdesc, + NULL, 0, NULL, nkeys, cur_skey, res); diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 0d12bbb..d02f44a 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -130,7 +130,9 @@ extern HeapTuple heap_getnext(HeapScanDesc scan, ScanDirection direction); extern Size heap_parallelscan_estimate(Snapshot snapshot); extern void heap_parallelscan_initialize(ParallelHeapScanDesc target, Relation relation, Snapshot snapshot); -extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc); +extern HeapScanDesc heap_beginscan_parallel(Relation relation, + ParallelHeapScanDesc parallel_scan, int nkeys, + ScanKey key); extern bool heap_fetch(Relation relation, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, diff --git a/src/include/access/relscan.h b/src/include/access/relscan.h index de98dd6..f237ecd 100644 --- a/src/include/access/relscan.h +++ b/src/include/access/relscan.h @@ -20,7 +20,7 @@ #include "access/itup.h" #include "access/tupdesc.h" #include "storage/spin.h" - +#include "executor/tuptable.h" /* * Shared state for parallel heap scan. * @@ -70,6 +70,8 @@ typedef struct HeapScanDescData Buffer rs_cbuf; /* current buffer in scan, if any */ /* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer */ ParallelHeapScanDesc rs_parallel; /* parallel scan information */ + TupleTableSlot *rs_scanslot; + MemoryContext per_tuple_context; /* these fields only used in page-at-a-time mode and for bitmap scans */ int rs_cindex; /* current tuple's index in vistuples */ diff --git a/src/include/access/valid.h b/src/include/access/valid.h index 30af9df..b1d9bbb 100644 --- a/src/include/access/valid.h +++ b/src/include/access/valid.h @@ -21,14 +21,25 @@ */ #define HeapKeyTest(tuple, \ tupdesc, \ + slot, \ + buf, \ + context, \ nkeys, \ keys, \ result) \ do \ { \ /* Use underscores to protect the variables passed in as parameters */ \ - int __cur_nkeys = (nkeys); \ - ScanKey __cur_keys = (keys); \ + int __cur_nkeys = (nkeys); \ + ScanKey __cur_keys = (keys); \ + MemoryContext __oldcontext; \ + \ + if ((slot)) \ + ExecStoreTuple(tuple, /* tuple to store */ \ + (slot), /* slot to store in */ \ + (buf), /* buffer associated with this \ + * tuple */ \ + false); /* don't pfree this pointer */ \ \ (result) = true; /* may change */ \ for (; __cur_nkeys--; __cur_keys++) \ @@ -43,10 +54,13 @@ do \ break; \ } \ \ - __atp = heap_getattr((tuple), \ + if (!(slot)) \ + __atp = heap_getattr((tuple), \ __cur_keys->sk_attno, \ (tupdesc), \ &__isnull); \ + else \ + __atp = slot_getattr((slot), __cur_keys->sk_attno, &__isnull); \ \ if (__isnull) \ { \ @@ -54,9 +68,11 @@ do \ break; \ } \ \ + __oldcontext = MemoryContextSwitchTo(context); \ __test = FunctionCall2Coll(&__cur_keys->sk_func, \ __cur_keys->sk_collation, \ __atp, __cur_keys->sk_argument); \ + MemoryContextSwitchTo(__oldcontext); \ \ if (!DatumGetBool(__test)) \ { \