Commit 0414b26b authored by Robert Haas's avatar Robert Haas

Add optimizer and executor support for parallel index-only scans.

Commit 5262f7a4 added similar support
for parallel index scans; this extends that work to index-only scans.
As with parallel index scans, this requires support from the index AM,
so currently parallel index-only scans will only be possible for btree
indexes.

Rafia Sabih, reviewed and tested by Rahila Syed, Tushar Ahuja,
and Amit Kapila

Discussion: http://postgr.es/m/CAOGQiiPEAs4C=TBp0XShxBvnWXuzGL2u++Hm1=qnCpd6_Mf8Fw@mail.gmail.com
parent 16be2fd1
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include "executor/nodeForeignscan.h" #include "executor/nodeForeignscan.h"
#include "executor/nodeSeqscan.h" #include "executor/nodeSeqscan.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/tqueue.h" #include "executor/tqueue.h"
#include "nodes/nodeFuncs.h" #include "nodes/nodeFuncs.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
...@@ -202,6 +203,10 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e) ...@@ -202,6 +203,10 @@ ExecParallelEstimate(PlanState *planstate, ExecParallelEstimateContext *e)
ExecIndexScanEstimate((IndexScanState *) planstate, ExecIndexScanEstimate((IndexScanState *) planstate,
e->pcxt); e->pcxt);
break; break;
case T_IndexOnlyScanState:
ExecIndexOnlyScanEstimate((IndexOnlyScanState *) planstate,
e->pcxt);
break;
case T_ForeignScanState: case T_ForeignScanState:
ExecForeignScanEstimate((ForeignScanState *) planstate, ExecForeignScanEstimate((ForeignScanState *) planstate,
e->pcxt); e->pcxt);
...@@ -258,6 +263,10 @@ ExecParallelInitializeDSM(PlanState *planstate, ...@@ -258,6 +263,10 @@ ExecParallelInitializeDSM(PlanState *planstate,
ExecIndexScanInitializeDSM((IndexScanState *) planstate, ExecIndexScanInitializeDSM((IndexScanState *) planstate,
d->pcxt); d->pcxt);
break; break;
case T_IndexOnlyScanState:
ExecIndexOnlyScanInitializeDSM((IndexOnlyScanState *) planstate,
d->pcxt);
break;
case T_ForeignScanState: case T_ForeignScanState:
ExecForeignScanInitializeDSM((ForeignScanState *) planstate, ExecForeignScanInitializeDSM((ForeignScanState *) planstate,
d->pcxt); d->pcxt);
...@@ -737,6 +746,9 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc) ...@@ -737,6 +746,9 @@ ExecParallelInitializeWorker(PlanState *planstate, shm_toc *toc)
case T_IndexScanState: case T_IndexScanState:
ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc); ExecIndexScanInitializeWorker((IndexScanState *) planstate, toc);
break; break;
case T_IndexOnlyScanState:
ExecIndexOnlyScanInitializeWorker((IndexOnlyScanState *) planstate, toc);
break;
case T_ForeignScanState: case T_ForeignScanState:
ExecForeignScanInitializeWorker((ForeignScanState *) planstate, ExecForeignScanInitializeWorker((ForeignScanState *) planstate,
toc); toc);
......
...@@ -21,6 +21,11 @@ ...@@ -21,6 +21,11 @@
* ExecEndIndexOnlyScan releases all storage. * ExecEndIndexOnlyScan releases all storage.
* ExecIndexOnlyMarkPos marks scan position. * ExecIndexOnlyMarkPos marks scan position.
* ExecIndexOnlyRestrPos restores scan position. * ExecIndexOnlyRestrPos restores scan position.
* ExecIndexOnlyScanEstimate estimates DSM space needed for
* parallel index-only scan
* ExecIndexOnlyScanInitializeDSM initialize DSM for parallel
* index-only scan
* ExecIndexOnlyScanInitializeWorker attach to DSM info in parallel worker
*/ */
#include "postgres.h" #include "postgres.h"
...@@ -277,6 +282,16 @@ ExecIndexOnlyScan(IndexOnlyScanState *node) ...@@ -277,6 +282,16 @@ ExecIndexOnlyScan(IndexOnlyScanState *node)
void void
ExecReScanIndexOnlyScan(IndexOnlyScanState *node) ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
{ {
bool reset_parallel_scan = true;
/*
* If we are here to just update the scan keys, then don't reset parallel
* scan. For detailed reason behind this look in the comments for
* ExecReScanIndexScan.
*/
if (node->ioss_NumRuntimeKeys != 0 && !node->ioss_RuntimeKeysReady)
reset_parallel_scan = false;
/* /*
* If we are doing runtime key calculations (ie, any of the index key * If we are doing runtime key calculations (ie, any of the index key
* values weren't simple Consts), compute the new key values. But first, * values weren't simple Consts), compute the new key values. But first,
...@@ -296,10 +311,16 @@ ExecReScanIndexOnlyScan(IndexOnlyScanState *node) ...@@ -296,10 +311,16 @@ ExecReScanIndexOnlyScan(IndexOnlyScanState *node)
node->ioss_RuntimeKeysReady = true; node->ioss_RuntimeKeysReady = true;
/* reset index scan */ /* reset index scan */
index_rescan(node->ioss_ScanDesc, if (node->ioss_ScanDesc)
node->ioss_ScanKeys, node->ioss_NumScanKeys, {
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
index_rescan(node->ioss_ScanDesc,
node->ioss_ScanKeys, node->ioss_NumScanKeys,
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
if (reset_parallel_scan && node->ioss_ScanDesc->parallel_scan)
index_parallelrescan(node->ioss_ScanDesc);
}
ExecScanReScan(&node->ss); ExecScanReScan(&node->ss);
} }
...@@ -536,29 +557,124 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) ...@@ -536,29 +557,124 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
/* /*
* Initialize scan descriptor. * Initialize scan descriptor.
*/ */
indexstate->ioss_ScanDesc = index_beginscan(currentRelation, if (!node->scan.plan.parallel_aware)
indexstate->ioss_RelationDesc, {
estate->es_snapshot, indexstate->ioss_ScanDesc = index_beginscan(currentRelation,
indexstate->ioss_RelationDesc,
estate->es_snapshot,
indexstate->ioss_NumScanKeys, indexstate->ioss_NumScanKeys,
indexstate->ioss_NumOrderByKeys); indexstate->ioss_NumOrderByKeys);
/* Set it up for index-only scan */
indexstate->ioss_ScanDesc->xs_want_itup = true;
indexstate->ioss_VMBuffer = InvalidBuffer;
/* /* Set it up for index-only scan */
* If no run-time keys to calculate, go ahead and pass the scankeys to the indexstate->ioss_ScanDesc->xs_want_itup = true;
* index AM. indexstate->ioss_VMBuffer = InvalidBuffer;
*/
if (indexstate->ioss_NumRuntimeKeys == 0) /*
index_rescan(indexstate->ioss_ScanDesc, * If no run-time keys to calculate, go ahead and pass the scankeys to
indexstate->ioss_ScanKeys, * the index AM.
indexstate->ioss_NumScanKeys, */
indexstate->ioss_OrderByKeys, if (indexstate->ioss_NumRuntimeKeys == 0)
indexstate->ioss_NumOrderByKeys); index_rescan(indexstate->ioss_ScanDesc,
indexstate->ioss_ScanKeys,
indexstate->ioss_NumScanKeys,
indexstate->ioss_OrderByKeys,
indexstate->ioss_NumOrderByKeys);
}
/* /*
* all done. * all done.
*/ */
return indexstate; return indexstate;
} }
/* ----------------------------------------------------------------
* Parallel Index-only Scan Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecIndexOnlyScanEstimate
*
* estimates the space required to serialize index-only scan node.
* ----------------------------------------------------------------
*/
void
ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
ParallelContext *pcxt)
{
EState *estate = node->ss.ps.state;
node->ioss_PscanLen = index_parallelscan_estimate(node->ioss_RelationDesc,
estate->es_snapshot);
shm_toc_estimate_chunk(&pcxt->estimator, node->ioss_PscanLen);
shm_toc_estimate_keys(&pcxt->estimator, 1);
}
/* ----------------------------------------------------------------
* ExecIndexOnlyScanInitializeDSM
*
* Set up a parallel index-only scan descriptor.
* ----------------------------------------------------------------
*/
void
ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
ParallelContext *pcxt)
{
EState *estate = node->ss.ps.state;
ParallelIndexScanDesc piscan;
piscan = shm_toc_allocate(pcxt->toc, node->ioss_PscanLen);
index_parallelscan_initialize(node->ss.ss_currentRelation,
node->ioss_RelationDesc,
estate->es_snapshot,
piscan);
shm_toc_insert(pcxt->toc, node->ss.ps.plan->plan_node_id, piscan);
node->ioss_ScanDesc =
index_beginscan_parallel(node->ss.ss_currentRelation,
node->ioss_RelationDesc,
node->ioss_NumScanKeys,
node->ioss_NumOrderByKeys,
piscan);
node->ioss_ScanDesc->xs_want_itup = true;
node->ioss_VMBuffer = InvalidBuffer;
/*
* If no run-time keys to calculate, go ahead and pass the scankeys to
* the index AM.
*/
if (node->ioss_NumRuntimeKeys == 0)
index_rescan(node->ioss_ScanDesc,
node->ioss_ScanKeys, node->ioss_NumScanKeys,
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
}
/* ----------------------------------------------------------------
* ExecIndexOnlyScanInitializeWorker
*
* Copy relevant information from TOC into planstate.
* ----------------------------------------------------------------
*/
void
ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node, shm_toc *toc)
{
ParallelIndexScanDesc piscan;
piscan = shm_toc_lookup(toc, node->ss.ps.plan->plan_node_id);
node->ioss_ScanDesc =
index_beginscan_parallel(node->ss.ss_currentRelation,
node->ioss_RelationDesc,
node->ioss_NumScanKeys,
node->ioss_NumOrderByKeys,
piscan);
node->ioss_ScanDesc->xs_want_itup = true;
/*
* If no run-time keys to calculate, go ahead and pass the scankeys to the
* index AM.
*/
if (node->ioss_NumRuntimeKeys == 0)
index_rescan(node->ioss_ScanDesc,
node->ioss_ScanKeys, node->ioss_NumScanKeys,
node->ioss_OrderByKeys, node->ioss_NumOrderByKeys);
}
...@@ -1048,9 +1048,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, ...@@ -1048,9 +1048,9 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
/* /*
* If appropriate, consider parallel index scan. We don't allow * If appropriate, consider parallel index scan. We don't allow
* parallel index scan for bitmap or index only scans. * parallel index scan for bitmap index scans.
*/ */
if (index->amcanparallel && !index_only_scan && if (index->amcanparallel &&
rel->consider_parallel && outer_relids == NULL && rel->consider_parallel && outer_relids == NULL &&
scantype != ST_BITMAPSCAN) scantype != ST_BITMAPSCAN)
{ {
...@@ -1104,7 +1104,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel, ...@@ -1104,7 +1104,7 @@ build_index_paths(PlannerInfo *root, RelOptInfo *rel,
result = lappend(result, ipath); result = lappend(result, ipath);
/* If appropriate, consider parallel index scan */ /* If appropriate, consider parallel index scan */
if (index->amcanparallel && !index_only_scan && if (index->amcanparallel &&
rel->consider_parallel && outer_relids == NULL && rel->consider_parallel && outer_relids == NULL &&
scantype != ST_BITMAPSCAN) scantype != ST_BITMAPSCAN)
{ {
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#define NODEINDEXONLYSCAN_H #define NODEINDEXONLYSCAN_H
#include "nodes/execnodes.h" #include "nodes/execnodes.h"
#include "access/parallel.h"
extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags); extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags);
extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node); extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node);
...@@ -23,4 +24,12 @@ extern void ExecIndexOnlyMarkPos(IndexOnlyScanState *node); ...@@ -23,4 +24,12 @@ extern void ExecIndexOnlyMarkPos(IndexOnlyScanState *node);
extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node); extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node);
extern void ExecReScanIndexOnlyScan(IndexOnlyScanState *node); extern void ExecReScanIndexOnlyScan(IndexOnlyScanState *node);
/* Support functions for parallel index-only scans */
extern void ExecIndexOnlyScanEstimate(IndexOnlyScanState *node,
ParallelContext *pcxt);
extern void ExecIndexOnlyScanInitializeDSM(IndexOnlyScanState *node,
ParallelContext *pcxt);
extern void ExecIndexOnlyScanInitializeWorker(IndexOnlyScanState *node,
shm_toc *toc);
#endif /* NODEINDEXONLYSCAN_H */ #endif /* NODEINDEXONLYSCAN_H */
...@@ -1409,6 +1409,7 @@ typedef struct IndexScanState ...@@ -1409,6 +1409,7 @@ typedef struct IndexScanState
* ScanDesc index scan descriptor * ScanDesc index scan descriptor
* VMBuffer buffer in use for visibility map testing, if any * VMBuffer buffer in use for visibility map testing, if any
* HeapFetches number of tuples we were forced to fetch from heap * HeapFetches number of tuples we were forced to fetch from heap
* ioss_PscanLen Size of parallel index-only scan descriptor
* ---------------- * ----------------
*/ */
typedef struct IndexOnlyScanState typedef struct IndexOnlyScanState
...@@ -1427,6 +1428,7 @@ typedef struct IndexOnlyScanState ...@@ -1427,6 +1428,7 @@ typedef struct IndexOnlyScanState
IndexScanDesc ioss_ScanDesc; IndexScanDesc ioss_ScanDesc;
Buffer ioss_VMBuffer; Buffer ioss_VMBuffer;
long ioss_HeapFetches; long ioss_HeapFetches;
Size ioss_PscanLen;
} IndexOnlyScanState; } IndexOnlyScanState;
/* ---------------- /* ----------------
......
...@@ -92,12 +92,14 @@ explain (costs off) ...@@ -92,12 +92,14 @@ explain (costs off)
explain (costs off) explain (costs off)
select sum(parallel_restricted(unique1)) from tenk1 select sum(parallel_restricted(unique1)) from tenk1
group by(parallel_restricted(unique1)); group by(parallel_restricted(unique1));
QUERY PLAN QUERY PLAN
---------------------------------------------------- -------------------------------------------------------------------
HashAggregate HashAggregate
Group Key: parallel_restricted(unique1) Group Key: parallel_restricted(unique1)
-> Index Only Scan using tenk1_unique1 on tenk1 -> Gather
(3 rows) Workers Planned: 4
-> Parallel Index Only Scan using tenk1_unique1 on tenk1
(5 rows)
-- test parallel plans for queries containing un-correlated subplans. -- test parallel plans for queries containing un-correlated subplans.
alter table tenk2 set (parallel_workers = 0); alter table tenk2 set (parallel_workers = 0);
...@@ -146,6 +148,25 @@ select count((unique1)) from tenk1 where hundred > 1; ...@@ -146,6 +148,25 @@ select count((unique1)) from tenk1 where hundred > 1;
9800 9800
(1 row) (1 row)
-- test parallel index-only scans.
explain (costs off)
select count(*) from tenk1 where thousand > 95;
QUERY PLAN
--------------------------------------------------------------------------------
Finalize Aggregate
-> Gather
Workers Planned: 4
-> Partial Aggregate
-> Parallel Index Only Scan using tenk1_thous_tenthous on tenk1
Index Cond: (thousand > 95)
(6 rows)
select count(*) from tenk1 where thousand > 95;
count
-------
9040
(1 row)
reset enable_seqscan; reset enable_seqscan;
reset enable_bitmapscan; reset enable_bitmapscan;
set force_parallel_mode=1; set force_parallel_mode=1;
......
...@@ -56,6 +56,11 @@ explain (costs off) ...@@ -56,6 +56,11 @@ explain (costs off)
select count((unique1)) from tenk1 where hundred > 1; select count((unique1)) from tenk1 where hundred > 1;
select count((unique1)) from tenk1 where hundred > 1; select count((unique1)) from tenk1 where hundred > 1;
-- test parallel index-only scans.
explain (costs off)
select count(*) from tenk1 where thousand > 95;
select count(*) from tenk1 where thousand > 95;
reset enable_seqscan; reset enable_seqscan;
reset enable_bitmapscan; reset enable_bitmapscan;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment