Commit a0185461 authored by Tom Lane's avatar Tom Lane

Rearrange the implementation of index-only scans.

This commit changes index-only scans so that data is read directly from the
index tuple without first generating a faux heap tuple.  The only immediate
benefit is that indexes on system columns (such as OID) can be used in
index-only scans, but this is necessary infrastructure if we are ever to
support index-only scans on expression indexes.  The executor is now ready
for that, though the planner still needs substantial work to recognize
the possibility.

To do this, Vars in index-only plan nodes have to refer to index columns
not heap columns.  I introduced a new special varno, INDEX_VAR, to mark
such Vars to avoid confusion.  (In passing, this commit renames the two
existing special varnos to OUTER_VAR and INNER_VAR.)  This allows
ruleutils.c to handle them with logic similar to what we use for subplan
reference Vars.

Since index-only scans are now fundamentally different from regular
indexscans so far as their expression subtrees are concerned, I also chose
to change them to have their own plan node type (and hence, their own
executor source file).
parent fa351d5a
...@@ -79,6 +79,8 @@ static void show_instrumentation_count(const char *qlabel, int which, ...@@ -79,6 +79,8 @@ static void show_instrumentation_count(const char *qlabel, int which,
PlanState *planstate, ExplainState *es); PlanState *planstate, ExplainState *es);
static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es); static void show_foreignscan_info(ForeignScanState *fsstate, ExplainState *es);
static const char *explain_get_index_name(Oid indexId); static const char *explain_get_index_name(Oid indexId);
static void ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
ExplainState *es);
static void ExplainScanTarget(Scan *plan, ExplainState *es); static void ExplainScanTarget(Scan *plan, ExplainState *es);
static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es); static void ExplainModifyTarget(ModifyTable *plan, ExplainState *es);
static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es); static void ExplainTargetRel(Plan *plan, Index rti, ExplainState *es);
...@@ -656,10 +658,10 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -656,10 +658,10 @@ ExplainNode(PlanState *planstate, List *ancestors,
pname = sname = "Seq Scan"; pname = sname = "Seq Scan";
break; break;
case T_IndexScan: case T_IndexScan:
if (((IndexScan *) plan)->indexonly) pname = sname = "Index Scan";
pname = sname = "Index Only Scan"; break;
else case T_IndexOnlyScan:
pname = sname = "Index Scan"; pname = sname = "Index Only Scan";
break; break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
pname = sname = "Bitmap Index Scan"; pname = sname = "Bitmap Index Scan";
...@@ -793,42 +795,6 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -793,42 +795,6 @@ ExplainNode(PlanState *planstate, List *ancestors,
switch (nodeTag(plan)) switch (nodeTag(plan))
{ {
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
const char *indexname =
explain_get_index_name(indexscan->indexid);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (ScanDirectionIsBackward(indexscan->indexorderdir))
appendStringInfoString(es->str, " Backward");
appendStringInfo(es->str, " using %s", indexname);
}
else
{
const char *scandir;
switch (indexscan->indexorderdir)
{
case BackwardScanDirection:
scandir = "Backward";
break;
case NoMovementScanDirection:
scandir = "NoMovement";
break;
case ForwardScanDirection:
scandir = "Forward";
break;
default:
scandir = "???";
break;
}
ExplainPropertyText("Scan Direction", scandir, es);
ExplainPropertyText("Index Name", indexname, es);
}
}
/* FALL THRU */
case T_SeqScan: case T_SeqScan:
case T_BitmapHeapScan: case T_BitmapHeapScan:
case T_TidScan: case T_TidScan:
...@@ -840,6 +806,26 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -840,6 +806,26 @@ ExplainNode(PlanState *planstate, List *ancestors,
case T_ForeignScan: case T_ForeignScan:
ExplainScanTarget((Scan *) plan, es); ExplainScanTarget((Scan *) plan, es);
break; break;
case T_IndexScan:
{
IndexScan *indexscan = (IndexScan *) plan;
ExplainIndexScanDetails(indexscan->indexid,
indexscan->indexorderdir,
es);
ExplainScanTarget((Scan *) indexscan, es);
}
break;
case T_IndexOnlyScan:
{
IndexOnlyScan *indexonlyscan = (IndexOnlyScan *) plan;
ExplainIndexScanDetails(indexonlyscan->indexid,
indexonlyscan->indexorderdir,
es);
ExplainScanTarget((Scan *) indexonlyscan, es);
}
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
{ {
BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan; BitmapIndexScan *bitmapindexscan = (BitmapIndexScan *) plan;
...@@ -1014,6 +1000,19 @@ ExplainNode(PlanState *planstate, List *ancestors, ...@@ -1014,6 +1000,19 @@ ExplainNode(PlanState *planstate, List *ancestors,
show_instrumentation_count("Rows Removed by Filter", 1, show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es); planstate, es);
break; break;
case T_IndexOnlyScan:
show_scan_qual(((IndexOnlyScan *) plan)->indexqual,
"Index Cond", planstate, ancestors, es);
if (((IndexOnlyScan *) plan)->indexqual)
show_instrumentation_count("Rows Removed by Index Recheck", 2,
planstate, es);
show_scan_qual(((IndexOnlyScan *) plan)->indexorderby,
"Order By", planstate, ancestors, es);
show_scan_qual(plan->qual, "Filter", planstate, ancestors, es);
if (plan->qual)
show_instrumentation_count("Rows Removed by Filter", 1,
planstate, es);
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig, show_scan_qual(((BitmapIndexScan *) plan)->indexqualorig,
"Index Cond", planstate, ancestors, es); "Index Cond", planstate, ancestors, es);
...@@ -1626,6 +1625,45 @@ explain_get_index_name(Oid indexId) ...@@ -1626,6 +1625,45 @@ explain_get_index_name(Oid indexId)
return result; return result;
} }
/*
* Add some additional details about an IndexScan or IndexOnlyScan
*/
static void
ExplainIndexScanDetails(Oid indexid, ScanDirection indexorderdir,
ExplainState *es)
{
const char *indexname = explain_get_index_name(indexid);
if (es->format == EXPLAIN_FORMAT_TEXT)
{
if (ScanDirectionIsBackward(indexorderdir))
appendStringInfoString(es->str, " Backward");
appendStringInfo(es->str, " using %s", indexname);
}
else
{
const char *scandir;
switch (indexorderdir)
{
case BackwardScanDirection:
scandir = "Backward";
break;
case NoMovementScanDirection:
scandir = "NoMovement";
break;
case ForwardScanDirection:
scandir = "Forward";
break;
default:
scandir = "???";
break;
}
ExplainPropertyText("Scan Direction", scandir, es);
ExplainPropertyText("Index Name", indexname, es);
}
}
/* /*
* Show the target of a Scan node * Show the target of a Scan node
*/ */
...@@ -1670,6 +1708,7 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es) ...@@ -1670,6 +1708,7 @@ ExplainTargetRel(Plan *plan, Index rti, ExplainState *es)
{ {
case T_SeqScan: case T_SeqScan:
case T_IndexScan: case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan: case T_BitmapHeapScan:
case T_TidScan: case T_TidScan:
case T_ForeignScan: case T_ForeignScan:
......
...@@ -2734,9 +2734,9 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ...@@ -2734,9 +2734,9 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
oldContext = MemoryContextSwitchTo(estate->es_query_cxt); oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
tgqual = stringToNode(trigger->tgqual); tgqual = stringToNode(trigger->tgqual);
/* Change references to OLD and NEW to INNER and OUTER */ /* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
ChangeVarNodes(tgqual, PRS2_OLD_VARNO, INNER, 0); ChangeVarNodes(tgqual, PRS2_OLD_VARNO, INNER_VAR, 0);
ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER, 0); ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
/* ExecQual wants implicit-AND form */ /* ExecQual wants implicit-AND form */
tgqual = (Node *) make_ands_implicit((Expr *) tgqual); tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
...@@ -2783,7 +2783,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ...@@ -2783,7 +2783,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
/* /*
* Finally evaluate the expression, making the old and/or new tuples * Finally evaluate the expression, making the old and/or new tuples
* available as INNER/OUTER respectively. * available as INNER_VAR/OUTER_VAR respectively.
*/ */
econtext->ecxt_innertuple = oldslot; econtext->ecxt_innertuple = oldslot;
econtext->ecxt_outertuple = newslot; econtext->ecxt_outertuple = newslot;
......
...@@ -17,7 +17,8 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \ ...@@ -17,7 +17,8 @@ OBJS = execAmi.o execCurrent.o execGrouping.o execJunk.o execMain.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
nodeBitmapAnd.o nodeBitmapOr.o \ nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \ nodeBitmapHeapscan.o nodeBitmapIndexscan.o nodeHash.o \
nodeHashjoin.o nodeIndexscan.o nodeLimit.o nodeLockRows.o \ nodeHashjoin.o nodeIndexscan.o nodeIndexonlyscan.o \
nodeLimit.o nodeLockRows.o \
nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \ nodeMaterial.o nodeMergeAppend.o nodeMergejoin.o nodeModifyTable.o \
nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \ nodeNestloop.o nodeFunctionscan.o nodeRecursiveunion.o nodeResult.o \
nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \ nodeSeqscan.o nodeSetOp.o nodeSort.o nodeUnique.o \
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
#include "executor/nodeHash.h" #include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h" #include "executor/nodeHashjoin.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "executor/nodeLimit.h" #include "executor/nodeLimit.h"
#include "executor/nodeLockRows.h" #include "executor/nodeLockRows.h"
...@@ -155,6 +156,10 @@ ExecReScan(PlanState *node) ...@@ -155,6 +156,10 @@ ExecReScan(PlanState *node)
ExecReScanIndexScan((IndexScanState *) node); ExecReScanIndexScan((IndexScanState *) node);
break; break;
case T_IndexOnlyScanState:
ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
break;
case T_BitmapIndexScanState: case T_BitmapIndexScanState:
ExecReScanBitmapIndexScan((BitmapIndexScanState *) node); ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
break; break;
...@@ -273,6 +278,10 @@ ExecMarkPos(PlanState *node) ...@@ -273,6 +278,10 @@ ExecMarkPos(PlanState *node)
ExecIndexMarkPos((IndexScanState *) node); ExecIndexMarkPos((IndexScanState *) node);
break; break;
case T_IndexOnlyScanState:
ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
break;
case T_TidScanState: case T_TidScanState:
ExecTidMarkPos((TidScanState *) node); ExecTidMarkPos((TidScanState *) node);
break; break;
...@@ -326,6 +335,10 @@ ExecRestrPos(PlanState *node) ...@@ -326,6 +335,10 @@ ExecRestrPos(PlanState *node)
ExecIndexRestrPos((IndexScanState *) node); ExecIndexRestrPos((IndexScanState *) node);
break; break;
case T_IndexOnlyScanState:
ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
break;
case T_TidScanState: case T_TidScanState:
ExecTidRestrPos((TidScanState *) node); ExecTidRestrPos((TidScanState *) node);
break; break;
...@@ -371,6 +384,7 @@ ExecSupportsMarkRestore(NodeTag plantype) ...@@ -371,6 +384,7 @@ ExecSupportsMarkRestore(NodeTag plantype)
{ {
case T_SeqScan: case T_SeqScan:
case T_IndexScan: case T_IndexScan:
case T_IndexOnlyScan:
case T_TidScan: case T_TidScan:
case T_ValuesScan: case T_ValuesScan:
case T_Material: case T_Material:
...@@ -442,6 +456,10 @@ ExecSupportsBackwardScan(Plan *node) ...@@ -442,6 +456,10 @@ ExecSupportsBackwardScan(Plan *node)
return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) && return IndexSupportsBackwardScan(((IndexScan *) node)->indexid) &&
TargetListSupportsBackwardScan(node->targetlist); TargetListSupportsBackwardScan(node->targetlist);
case T_IndexOnlyScan:
return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid) &&
TargetListSupportsBackwardScan(node->targetlist);
case T_SubqueryScan: case T_SubqueryScan:
return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) && return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan) &&
TargetListSupportsBackwardScan(node->targetlist); TargetListSupportsBackwardScan(node->targetlist);
...@@ -474,7 +492,8 @@ TargetListSupportsBackwardScan(List *targetlist) ...@@ -474,7 +492,8 @@ TargetListSupportsBackwardScan(List *targetlist)
} }
/* /*
* An IndexScan node supports backward scan only if the index's AM does. * An IndexScan or IndexOnlyScan node supports backward scan only if the
* index's AM does.
*/ */
static bool static bool
IndexSupportsBackwardScan(Oid indexid) IndexSupportsBackwardScan(Oid indexid)
......
...@@ -262,6 +262,7 @@ search_plan_tree(PlanState *node, Oid table_oid) ...@@ -262,6 +262,7 @@ search_plan_tree(PlanState *node, Oid table_oid)
*/ */
case T_SeqScanState: case T_SeqScanState:
case T_IndexScanState: case T_IndexScanState:
case T_IndexOnlyScanState:
case T_BitmapHeapScanState: case T_BitmapHeapScanState:
case T_TidScanState: case T_TidScanState:
{ {
......
...@@ -89,6 +89,7 @@ ...@@ -89,6 +89,7 @@
#include "executor/nodeGroup.h" #include "executor/nodeGroup.h"
#include "executor/nodeHash.h" #include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h" #include "executor/nodeHashjoin.h"
#include "executor/nodeIndexonlyscan.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "executor/nodeLimit.h" #include "executor/nodeLimit.h"
#include "executor/nodeLockRows.h" #include "executor/nodeLockRows.h"
...@@ -192,6 +193,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags) ...@@ -192,6 +193,11 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
estate, eflags); estate, eflags);
break; break;
case T_IndexOnlyScan:
result = (PlanState *) ExecInitIndexOnlyScan((IndexOnlyScan *) node,
estate, eflags);
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
result = (PlanState *) ExecInitBitmapIndexScan((BitmapIndexScan *) node, result = (PlanState *) ExecInitBitmapIndexScan((BitmapIndexScan *) node,
estate, eflags); estate, eflags);
...@@ -397,6 +403,10 @@ ExecProcNode(PlanState *node) ...@@ -397,6 +403,10 @@ ExecProcNode(PlanState *node)
result = ExecIndexScan((IndexScanState *) node); result = ExecIndexScan((IndexScanState *) node);
break; break;
case T_IndexOnlyScanState:
result = ExecIndexOnlyScan((IndexOnlyScanState *) node);
break;
/* BitmapIndexScanState does not yield tuples */ /* BitmapIndexScanState does not yield tuples */
case T_BitmapHeapScanState: case T_BitmapHeapScanState:
...@@ -627,6 +637,10 @@ ExecEndNode(PlanState *node) ...@@ -627,6 +637,10 @@ ExecEndNode(PlanState *node)
ExecEndIndexScan((IndexScanState *) node); ExecEndIndexScan((IndexScanState *) node);
break; break;
case T_IndexOnlyScanState:
ExecEndIndexOnlyScan((IndexOnlyScanState *) node);
break;
case T_BitmapIndexScanState: case T_BitmapIndexScanState:
ExecEndBitmapIndexScan((BitmapIndexScanState *) node); ExecEndBitmapIndexScan((BitmapIndexScanState *) node);
break; break;
......
...@@ -578,14 +578,16 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext, ...@@ -578,14 +578,16 @@ ExecEvalVar(ExprState *exprstate, ExprContext *econtext,
/* Get the input slot and attribute number we want */ /* Get the input slot and attribute number we want */
switch (variable->varno) switch (variable->varno)
{ {
case INNER: /* get the tuple from the inner node */ case INNER_VAR: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple; slot = econtext->ecxt_innertuple;
break; break;
case OUTER: /* get the tuple from the outer node */ case OUTER_VAR: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple; slot = econtext->ecxt_outertuple;
break; break;
/* INDEX_VAR is handled by default case */
default: /* get the tuple from the relation being default: /* get the tuple from the relation being
* scanned */ * scanned */
slot = econtext->ecxt_scantuple; slot = econtext->ecxt_scantuple;
...@@ -761,14 +763,16 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext, ...@@ -761,14 +763,16 @@ ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
/* Get the input slot and attribute number we want */ /* Get the input slot and attribute number we want */
switch (variable->varno) switch (variable->varno)
{ {
case INNER: /* get the tuple from the inner node */ case INNER_VAR: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple; slot = econtext->ecxt_innertuple;
break; break;
case OUTER: /* get the tuple from the outer node */ case OUTER_VAR: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple; slot = econtext->ecxt_outertuple;
break; break;
/* INDEX_VAR is handled by default case */
default: /* get the tuple from the relation being default: /* get the tuple from the relation being
* scanned */ * scanned */
slot = econtext->ecxt_scantuple; slot = econtext->ecxt_scantuple;
...@@ -804,14 +808,16 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext, ...@@ -804,14 +808,16 @@ ExecEvalWholeRowVar(ExprState *exprstate, ExprContext *econtext,
/* Get the input slot we want */ /* Get the input slot we want */
switch (variable->varno) switch (variable->varno)
{ {
case INNER: /* get the tuple from the inner node */ case INNER_VAR: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple; slot = econtext->ecxt_innertuple;
break; break;
case OUTER: /* get the tuple from the outer node */ case OUTER_VAR: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple; slot = econtext->ecxt_outertuple;
break; break;
/* INDEX_VAR is handled by default case */
default: /* get the tuple from the relation being default: /* get the tuple from the relation being
* scanned */ * scanned */
slot = econtext->ecxt_scantuple; slot = econtext->ecxt_scantuple;
...@@ -873,14 +879,16 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext, ...@@ -873,14 +879,16 @@ ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
/* Get the input slot we want */ /* Get the input slot we want */
switch (variable->varno) switch (variable->varno)
{ {
case INNER: /* get the tuple from the inner node */ case INNER_VAR: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple; slot = econtext->ecxt_innertuple;
break; break;
case OUTER: /* get the tuple from the outer node */ case OUTER_VAR: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple; slot = econtext->ecxt_outertuple;
break; break;
/* INDEX_VAR is handled by default case */
default: /* get the tuple from the relation being default: /* get the tuple from the relation being
* scanned */ * scanned */
slot = econtext->ecxt_scantuple; slot = econtext->ecxt_scantuple;
......
...@@ -246,10 +246,17 @@ void ...@@ -246,10 +246,17 @@ void
ExecAssignScanProjectionInfo(ScanState *node) ExecAssignScanProjectionInfo(ScanState *node)
{ {
Scan *scan = (Scan *) node->ps.plan; Scan *scan = (Scan *) node->ps.plan;
Index varno;
/* Vars in an index-only scan's tlist should be INDEX_VAR */
if (IsA(scan, IndexOnlyScan))
varno = INDEX_VAR;
else
varno = scan->scanrelid;
if (tlist_matches_tupdesc(&node->ps, if (tlist_matches_tupdesc(&node->ps,
scan->plan.targetlist, scan->plan.targetlist,
scan->scanrelid, varno,
node->ss_ScanTupleSlot->tts_tupleDescriptor)) node->ss_ScanTupleSlot->tts_tupleDescriptor))
node->ps.ps_ProjInfo = NULL; node->ps.ps_ProjInfo = NULL;
else else
......
...@@ -566,20 +566,22 @@ ExecBuildProjectionInfo(List *targetList, ...@@ -566,20 +566,22 @@ ExecBuildProjectionInfo(List *targetList,
switch (variable->varno) switch (variable->varno)
{ {
case INNER: case INNER_VAR:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext, varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_innertuple); ecxt_innertuple);
if (projInfo->pi_lastInnerVar < attnum) if (projInfo->pi_lastInnerVar < attnum)
projInfo->pi_lastInnerVar = attnum; projInfo->pi_lastInnerVar = attnum;
break; break;
case OUTER: case OUTER_VAR:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext, varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_outertuple); ecxt_outertuple);
if (projInfo->pi_lastOuterVar < attnum) if (projInfo->pi_lastOuterVar < attnum)
projInfo->pi_lastOuterVar = attnum; projInfo->pi_lastOuterVar = attnum;
break; break;
/* INDEX_VAR is handled by default case */
default: default:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext, varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_scantuple); ecxt_scantuple);
...@@ -628,16 +630,18 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo) ...@@ -628,16 +630,18 @@ get_last_attnums(Node *node, ProjectionInfo *projInfo)
switch (variable->varno) switch (variable->varno)
{ {
case INNER: case INNER_VAR:
if (projInfo->pi_lastInnerVar < attnum) if (projInfo->pi_lastInnerVar < attnum)
projInfo->pi_lastInnerVar = attnum; projInfo->pi_lastInnerVar = attnum;
break; break;
case OUTER: case OUTER_VAR:
if (projInfo->pi_lastOuterVar < attnum) if (projInfo->pi_lastOuterVar < attnum)
projInfo->pi_lastOuterVar = attnum; projInfo->pi_lastOuterVar = attnum;
break; break;
/* INDEX_VAR is handled by default case */
default: default:
if (projInfo->pi_lastScanVar < attnum) if (projInfo->pi_lastScanVar < attnum)
projInfo->pi_lastScanVar = attnum; projInfo->pi_lastScanVar = attnum;
......
...@@ -806,8 +806,8 @@ find_unaggregated_cols_walker(Node *node, Bitmapset **colnos) ...@@ -806,8 +806,8 @@ find_unaggregated_cols_walker(Node *node, Bitmapset **colnos)
{ {
Var *var = (Var *) node; Var *var = (Var *) node;
/* setrefs.c should have set the varno to OUTER */ /* setrefs.c should have set the varno to OUTER_VAR */
Assert(var->varno == OUTER); Assert(var->varno == OUTER_VAR);
Assert(var->varlevelsup == 0); Assert(var->varlevelsup == 0);
*colnos = bms_add_member(*colnos, var->varattno); *colnos = bms_add_member(*colnos, var->varattno);
return false; return false;
......
...@@ -266,7 +266,6 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags) ...@@ -266,7 +266,6 @@ ExecInitBitmapIndexScan(BitmapIndexScan *node, EState *estate, int eflags)
*/ */
ExecIndexBuildScanKeys((PlanState *) indexstate, ExecIndexBuildScanKeys((PlanState *) indexstate,
indexstate->biss_RelationDesc, indexstate->biss_RelationDesc,
node->scan.scanrelid,
node->indexqual, node->indexqual,
false, false,
&indexstate->biss_ScanKeys, &indexstate->biss_ScanKeys,
......
...@@ -755,8 +755,8 @@ ExecHashTableInsert(HashJoinTable hashtable, ...@@ -755,8 +755,8 @@ ExecHashTableInsert(HashJoinTable hashtable,
* Compute the hash value for a tuple * Compute the hash value for a tuple
* *
* The tuple to be tested must be in either econtext->ecxt_outertuple or * The tuple to be tested must be in either econtext->ecxt_outertuple or
* econtext->ecxt_innertuple. Vars in the hashkeys expressions reference * econtext->ecxt_innertuple. Vars in the hashkeys expressions should have
* either OUTER or INNER. * varno either OUTER_VAR or INNER_VAR.
* *
* A TRUE result means the tuple's hash value has been successfully computed * A TRUE result means the tuple's hash value has been successfully computed
* and stored at *hashvalue. A FALSE result means the tuple cannot match * and stored at *hashvalue. A FALSE result means the tuple cannot match
......
This diff is collapsed.
...@@ -14,8 +14,8 @@ ...@@ -14,8 +14,8 @@
*/ */
/* /*
* INTERFACE ROUTINES * INTERFACE ROUTINES
* ExecIndexScan scans a relation using indices * ExecIndexScan scans a relation using an index
* ExecIndexNext using index to retrieve next tuple * IndexNext retrieve next tuple using index
* ExecInitIndexScan creates and initializes state info. * ExecInitIndexScan creates and initializes state info.
* ExecReScanIndexScan rescans the indexed relation. * ExecReScanIndexScan rescans the indexed relation.
* ExecEndIndexScan releases all storage. * ExecEndIndexScan releases all storage.
...@@ -26,7 +26,6 @@ ...@@ -26,7 +26,6 @@
#include "access/nbtree.h" #include "access/nbtree.h"
#include "access/relscan.h" #include "access/relscan.h"
#include "access/visibilitymap.h"
#include "executor/execdebug.h" #include "executor/execdebug.h"
#include "executor/nodeIndexscan.h" #include "executor/nodeIndexscan.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
...@@ -37,7 +36,6 @@ ...@@ -37,7 +36,6 @@
static TupleTableSlot *IndexNext(IndexScanState *node); static TupleTableSlot *IndexNext(IndexScanState *node);
static void IndexStoreHeapTuple(TupleTableSlot *slot, IndexScanDesc scandesc);
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
...@@ -56,7 +54,6 @@ IndexNext(IndexScanState *node) ...@@ -56,7 +54,6 @@ IndexNext(IndexScanState *node)
IndexScanDesc scandesc; IndexScanDesc scandesc;
HeapTuple tuple; HeapTuple tuple;
TupleTableSlot *slot; TupleTableSlot *slot;
ItemPointer tid;
/* /*
* extract necessary information from index scan node * extract necessary information from index scan node
...@@ -76,67 +73,23 @@ IndexNext(IndexScanState *node) ...@@ -76,67 +73,23 @@ IndexNext(IndexScanState *node)
slot = node->ss.ss_ScanTupleSlot; slot = node->ss.ss_ScanTupleSlot;
/* /*
* OK, now that we have what we need, fetch the next TID. * ok, now that we have what we need, fetch the next tuple.
*/ */
while ((tid = index_getnext_tid(scandesc, direction)) != NULL) while ((tuple = index_getnext(scandesc, direction)) != NULL)
{ {
/* /*
* Attempt index-only scan, if possible. For this, we need to have * Store the scanned tuple in the scan tuple slot of the scan state.
* gotten an index tuple from the AM, and we need the TID to reference * Note: we pass 'false' because tuples returned by amgetnext are
* a heap page on which all tuples are known visible to everybody. * pointers onto disk pages and must not be pfree()'d.
* If that's the case, we don't need to visit the heap page for tuple
* visibility testing, and we don't need any column values that are
* not available from the index.
*
* Note: in the index-only path, we are still holding pin on the
* scan's xs_cbuf, ie, the previously visited heap page. It's not
* clear whether it'd be better to release that pin.
*/ */
if (scandesc->xs_want_itup && ExecStoreTuple(tuple, /* tuple to store */
visibilitymap_test(scandesc->heapRelation, slot, /* slot to store in */
ItemPointerGetBlockNumber(tid), scandesc->xs_cbuf, /* buffer containing tuple */
&node->iss_VMBuffer)) false); /* don't pfree */
{
/*
* Convert index tuple to look like a heap tuple, and store the
* results in the scan tuple slot.
*/
IndexStoreHeapTuple(slot, scandesc);
}
else
{
/* Index-only approach not possible, so fetch heap tuple. */
tuple = index_fetch_heap(scandesc);
/* Tuple might not be visible. */
if (tuple == NULL)
continue;
/*
* Only MVCC snapshots are supported here, so there should be no
* need to keep following the HOT chain once a visible entry has
* been found. If we did want to allow that, we'd need to keep
* more state to remember not to call index_getnext_tid next time.
*/
if (scandesc->xs_continue_hot)
elog(ERROR, "unsupported use of non-MVCC snapshot in executor");
/*
* Store the scanned tuple in the scan tuple slot of the scan
* state.
*
* Note: we pass 'false' because tuples returned by amgetnext are
* pointers onto disk pages and must not be pfree()'d.
*/
ExecStoreTuple(tuple, /* tuple to store */
slot, /* slot to store in */
scandesc->xs_cbuf, /* buffer containing tuple */
false); /* don't pfree */
}
/* /*
* If the index was lossy, we have to recheck the index quals using * If the index was lossy, we have to recheck the index quals using
* the real tuple. * the fetched tuple.
*/ */
if (scandesc->xs_recheck) if (scandesc->xs_recheck)
{ {
...@@ -160,53 +113,6 @@ IndexNext(IndexScanState *node) ...@@ -160,53 +113,6 @@ IndexNext(IndexScanState *node)
return ExecClearTuple(slot); return ExecClearTuple(slot);
} }
/*
* IndexStoreHeapTuple
*
* When performing an index-only scan, we build a faux heap tuple
* from the index tuple. Columns not present in the index are set to
* NULL, which is OK because we know they won't be referenced.
*
* The faux tuple is built as a virtual tuple that depends on the
* scandesc's xs_itup, so that must remain valid for as long as we
* need the slot contents.
*/
static void
IndexStoreHeapTuple(TupleTableSlot *slot, IndexScanDesc scandesc)
{
Form_pg_index indexForm = scandesc->indexRelation->rd_index;
TupleDesc indexDesc = RelationGetDescr(scandesc->indexRelation);
int nindexatts = indexDesc->natts;
int nheapatts = slot->tts_tupleDescriptor->natts;
Datum *values = slot->tts_values;
bool *isnull = slot->tts_isnull;
int i;
/* We must first set the slot to empty, and mark all columns as null */
ExecClearTuple(slot);
memset(isnull, true, nheapatts * sizeof(bool));
/* Transpose index tuple into heap tuple. */
for (i = 0; i < nindexatts; i++)
{
int indexatt = indexForm->indkey.values[i];
/* Ignore expression columns, as well as system attributes */
if (indexatt <= 0)
continue;
Assert(indexatt <= nheapatts);
values[indexatt - 1] = index_getattr(scandesc->xs_itup, i + 1,
indexDesc,
&isnull[indexatt - 1]);
}
/* And now we can mark the slot as holding a virtual tuple. */
ExecStoreVirtualTuple(slot);
}
/* /*
* IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual * IndexRecheck -- access method routine to recheck a tuple in EvalPlanQual
*/ */
...@@ -493,13 +399,6 @@ ExecEndIndexScan(IndexScanState *node) ...@@ -493,13 +399,6 @@ ExecEndIndexScan(IndexScanState *node)
indexScanDesc = node->iss_ScanDesc; indexScanDesc = node->iss_ScanDesc;
relation = node->ss.ss_currentRelation; relation = node->ss.ss_currentRelation;
/* Release VM buffer pin, if any. */
if (node->iss_VMBuffer != InvalidBuffer)
{
ReleaseBuffer(node->iss_VMBuffer);
node->iss_VMBuffer = InvalidBuffer;
}
/* /*
* Free the exprcontext(s) ... now dead code, see ExecFreeExprContext * Free the exprcontext(s) ... now dead code, see ExecFreeExprContext
*/ */
...@@ -659,7 +558,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ...@@ -659,7 +558,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
*/ */
ExecIndexBuildScanKeys((PlanState *) indexstate, ExecIndexBuildScanKeys((PlanState *) indexstate,
indexstate->iss_RelationDesc, indexstate->iss_RelationDesc,
node->scan.scanrelid,
node->indexqual, node->indexqual,
false, false,
&indexstate->iss_ScanKeys, &indexstate->iss_ScanKeys,
...@@ -674,7 +572,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ...@@ -674,7 +572,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
*/ */
ExecIndexBuildScanKeys((PlanState *) indexstate, ExecIndexBuildScanKeys((PlanState *) indexstate,
indexstate->iss_RelationDesc, indexstate->iss_RelationDesc,
node->scan.scanrelid,
node->indexorderby, node->indexorderby,
true, true,
&indexstate->iss_OrderByKeys, &indexstate->iss_OrderByKeys,
...@@ -712,10 +609,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ...@@ -712,10 +609,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
indexstate->iss_NumScanKeys, indexstate->iss_NumScanKeys,
indexstate->iss_NumOrderByKeys); indexstate->iss_NumOrderByKeys);
/* Prepare for possible index-only scan */
indexstate->iss_ScanDesc->xs_want_itup = node->indexonly;
indexstate->iss_VMBuffer = InvalidBuffer;
/* /*
* If no run-time keys to calculate, go ahead and pass the scankeys to the * If no run-time keys to calculate, go ahead and pass the scankeys to the
* index AM. * index AM.
...@@ -772,7 +665,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ...@@ -772,7 +665,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* *
* planstate: executor state node we are working for * planstate: executor state node we are working for
* index: the index we are building scan keys for * index: the index we are building scan keys for
* scanrelid: varno of the index's relation within current query
* quals: indexquals (or indexorderbys) expressions * quals: indexquals (or indexorderbys) expressions
* isorderby: true if processing ORDER BY exprs, false if processing quals * isorderby: true if processing ORDER BY exprs, false if processing quals
* *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none * *runtimeKeys: ptr to pre-existing IndexRuntimeKeyInfos, or NULL if none
...@@ -791,7 +683,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) ...@@ -791,7 +683,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* ScalarArrayOpExpr quals are not supported. * ScalarArrayOpExpr quals are not supported.
*/ */
void void
ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
List *quals, bool isorderby, List *quals, bool isorderby,
ScanKey *scanKeys, int *numScanKeys, ScanKey *scanKeys, int *numScanKeys,
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
...@@ -865,7 +757,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ...@@ -865,7 +757,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
Assert(leftop != NULL); Assert(leftop != NULL);
if (!(IsA(leftop, Var) && if (!(IsA(leftop, Var) &&
((Var *) leftop)->varno == scanrelid)) ((Var *) leftop)->varno == INDEX_VAR))
elog(ERROR, "indexqual doesn't have key on left side"); elog(ERROR, "indexqual doesn't have key on left side");
varattno = ((Var *) leftop)->varattno; varattno = ((Var *) leftop)->varattno;
...@@ -979,7 +871,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ...@@ -979,7 +871,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
Assert(leftop != NULL); Assert(leftop != NULL);
if (!(IsA(leftop, Var) && if (!(IsA(leftop, Var) &&
((Var *) leftop)->varno == scanrelid)) ((Var *) leftop)->varno == INDEX_VAR))
elog(ERROR, "indexqual doesn't have key on left side"); elog(ERROR, "indexqual doesn't have key on left side");
varattno = ((Var *) leftop)->varattno; varattno = ((Var *) leftop)->varattno;
...@@ -1107,7 +999,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ...@@ -1107,7 +999,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
Assert(leftop != NULL); Assert(leftop != NULL);
if (!(IsA(leftop, Var) && if (!(IsA(leftop, Var) &&
((Var *) leftop)->varno == scanrelid)) ((Var *) leftop)->varno == INDEX_VAR))
elog(ERROR, "indexqual doesn't have key on left side"); elog(ERROR, "indexqual doesn't have key on left side");
varattno = ((Var *) leftop)->varattno; varattno = ((Var *) leftop)->varattno;
...@@ -1172,7 +1064,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid, ...@@ -1172,7 +1064,7 @@ ExecIndexBuildScanKeys(PlanState *planstate, Relation index, Index scanrelid,
Assert(leftop != NULL); Assert(leftop != NULL);
if (!(IsA(leftop, Var) && if (!(IsA(leftop, Var) &&
((Var *) leftop)->varno == scanrelid)) ((Var *) leftop)->varno == INDEX_VAR))
elog(ERROR, "NullTest indexqual has wrong key"); elog(ERROR, "NullTest indexqual has wrong key");
varattno = ((Var *) leftop)->varattno; varattno = ((Var *) leftop)->varattno;
......
...@@ -147,8 +147,8 @@ ExecNestLoop(NestLoopState *node) ...@@ -147,8 +147,8 @@ ExecNestLoop(NestLoopState *node)
ParamExecData *prm; ParamExecData *prm;
prm = &(econtext->ecxt_param_exec_vals[paramno]); prm = &(econtext->ecxt_param_exec_vals[paramno]);
/* Param value should be an OUTER var */ /* Param value should be an OUTER_VAR var */
Assert(nlp->paramval->varno == OUTER); Assert(nlp->paramval->varno == OUTER_VAR);
Assert(nlp->paramval->varattno > 0); Assert(nlp->paramval->varattno > 0);
prm->value = slot_getattr(outerTupleSlot, prm->value = slot_getattr(outerTupleSlot,
nlp->paramval->varattno, nlp->paramval->varattno,
......
...@@ -370,7 +370,31 @@ _copyIndexScan(IndexScan *from) ...@@ -370,7 +370,31 @@ _copyIndexScan(IndexScan *from)
COPY_NODE_FIELD(indexorderby); COPY_NODE_FIELD(indexorderby);
COPY_NODE_FIELD(indexorderbyorig); COPY_NODE_FIELD(indexorderbyorig);
COPY_SCALAR_FIELD(indexorderdir); COPY_SCALAR_FIELD(indexorderdir);
COPY_SCALAR_FIELD(indexonly);
return newnode;
}
/*
* _copyIndexOnlyScan
*/
static IndexOnlyScan *
_copyIndexOnlyScan(IndexOnlyScan *from)
{
IndexOnlyScan *newnode = makeNode(IndexOnlyScan);
/*
* copy node superclass fields
*/
CopyScanFields((Scan *) from, (Scan *) newnode);
/*
* copy remainder of node
*/
COPY_SCALAR_FIELD(indexid);
COPY_NODE_FIELD(indexqual);
COPY_NODE_FIELD(indexorderby);
COPY_NODE_FIELD(indextlist);
COPY_SCALAR_FIELD(indexorderdir);
return newnode; return newnode;
} }
...@@ -3871,6 +3895,9 @@ copyObject(void *from) ...@@ -3871,6 +3895,9 @@ copyObject(void *from)
case T_IndexScan: case T_IndexScan:
retval = _copyIndexScan(from); retval = _copyIndexScan(from);
break; break;
case T_IndexOnlyScan:
retval = _copyIndexOnlyScan(from);
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
retval = _copyBitmapIndexScan(from); retval = _copyBitmapIndexScan(from);
break; break;
......
...@@ -447,7 +447,20 @@ _outIndexScan(StringInfo str, IndexScan *node) ...@@ -447,7 +447,20 @@ _outIndexScan(StringInfo str, IndexScan *node)
WRITE_NODE_FIELD(indexorderby); WRITE_NODE_FIELD(indexorderby);
WRITE_NODE_FIELD(indexorderbyorig); WRITE_NODE_FIELD(indexorderbyorig);
WRITE_ENUM_FIELD(indexorderdir, ScanDirection); WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
WRITE_BOOL_FIELD(indexonly); }
static void
_outIndexOnlyScan(StringInfo str, IndexOnlyScan *node)
{
WRITE_NODE_TYPE("INDEXONLYSCAN");
_outScanInfo(str, (Scan *) node);
WRITE_OID_FIELD(indexid);
WRITE_NODE_FIELD(indexqual);
WRITE_NODE_FIELD(indexorderby);
WRITE_NODE_FIELD(indextlist);
WRITE_ENUM_FIELD(indexorderdir, ScanDirection);
} }
static void static void
...@@ -1501,7 +1514,6 @@ _outIndexPath(StringInfo str, IndexPath *node) ...@@ -1501,7 +1514,6 @@ _outIndexPath(StringInfo str, IndexPath *node)
WRITE_NODE_FIELD(indexorderbys); WRITE_NODE_FIELD(indexorderbys);
WRITE_BOOL_FIELD(isjoininner); WRITE_BOOL_FIELD(isjoininner);
WRITE_ENUM_FIELD(indexscandir, ScanDirection); WRITE_ENUM_FIELD(indexscandir, ScanDirection);
WRITE_BOOL_FIELD(indexonly);
WRITE_FLOAT_FIELD(indextotalcost, "%.2f"); WRITE_FLOAT_FIELD(indextotalcost, "%.2f");
WRITE_FLOAT_FIELD(indexselectivity, "%.4f"); WRITE_FLOAT_FIELD(indexselectivity, "%.4f");
WRITE_FLOAT_FIELD(rows, "%.0f"); WRITE_FLOAT_FIELD(rows, "%.0f");
...@@ -1752,8 +1764,9 @@ _outIndexOptInfo(StringInfo str, IndexOptInfo *node) ...@@ -1752,8 +1764,9 @@ _outIndexOptInfo(StringInfo str, IndexOptInfo *node)
WRITE_FLOAT_FIELD(tuples, "%.0f"); WRITE_FLOAT_FIELD(tuples, "%.0f");
WRITE_INT_FIELD(ncolumns); WRITE_INT_FIELD(ncolumns);
WRITE_OID_FIELD(relam); WRITE_OID_FIELD(relam);
WRITE_NODE_FIELD(indexprs); /* indexprs is redundant since we print indextlist */
WRITE_NODE_FIELD(indpred); WRITE_NODE_FIELD(indpred);
WRITE_NODE_FIELD(indextlist);
WRITE_BOOL_FIELD(predOK); WRITE_BOOL_FIELD(predOK);
WRITE_BOOL_FIELD(unique); WRITE_BOOL_FIELD(unique);
WRITE_BOOL_FIELD(hypothetical); WRITE_BOOL_FIELD(hypothetical);
...@@ -2707,6 +2720,9 @@ _outNode(StringInfo str, void *obj) ...@@ -2707,6 +2720,9 @@ _outNode(StringInfo str, void *obj)
case T_IndexScan: case T_IndexScan:
_outIndexScan(str, obj); _outIndexScan(str, obj);
break; break;
case T_IndexOnlyScan:
_outIndexOnlyScan(str, obj);
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
_outBitmapIndexScan(str, obj); _outBitmapIndexScan(str, obj);
break; break;
......
...@@ -320,14 +320,18 @@ print_expr(Node *expr, List *rtable) ...@@ -320,14 +320,18 @@ print_expr(Node *expr, List *rtable)
switch (var->varno) switch (var->varno)
{ {
case INNER: case INNER_VAR:
relname = "INNER"; relname = "INNER";
attname = "?"; attname = "?";
break; break;
case OUTER: case OUTER_VAR:
relname = "OUTER"; relname = "OUTER";
attname = "?"; attname = "?";
break; break;
case INDEX_VAR:
relname = "INDEX";
attname = "?";
break;
default: default:
{ {
RangeTblEntry *rte; RangeTblEntry *rte;
......
...@@ -199,14 +199,15 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel) ...@@ -199,14 +199,15 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
true, NULL, SAOP_FORBID, ST_ANYSCAN); true, NULL, SAOP_FORBID, ST_ANYSCAN);
/* /*
* Submit all the ones that can form plain IndexScan plans to add_path. (A * Submit all the ones that can form plain IndexScan plans to add_path.
* plain IndexPath always represents a plain IndexScan plan; however some * (A plain IndexPath might represent either a plain IndexScan or an
* of the indexes might support only bitmap scans, and those we mustn't * IndexOnlyScan, but for our purposes here the distinction does not
* submit to add_path here.) Also, pick out the ones that might be useful * matter. However, some of the indexes might support only bitmap scans,
* as bitmap scans. For that, we must discard indexes that don't support * and those we mustn't submit to add_path here.) Also, pick out the ones
* bitmap scans, and we also are only interested in paths that have some * that might be useful as bitmap scans. For that, we must discard
* selectivity; we should discard anything that was generated solely for * indexes that don't support bitmap scans, and we also are only
* ordering purposes. * interested in paths that have some selectivity; we should discard
* anything that was generated solely for ordering purposes.
*/ */
bitindexpaths = NIL; bitindexpaths = NIL;
foreach(l, indexpaths) foreach(l, indexpaths)
...@@ -1107,11 +1108,9 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index) ...@@ -1107,11 +1108,9 @@ check_index_only(RelOptInfo *rel, IndexOptInfo *index)
/* /*
* For the moment, we just ignore index expressions. It might be nice * For the moment, we just ignore index expressions. It might be nice
* to do something with them, later. We also ignore index columns * to do something with them, later.
* that are system columns (such as OID), because the virtual-tuple
* coding used by IndexStoreHeapTuple() can't deal with them.
*/ */
if (attno <= 0) if (attno == 0)
continue; continue;
index_attrs = index_attrs =
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
...@@ -35,8 +34,6 @@ static PathKey *make_canonical_pathkey(PlannerInfo *root, ...@@ -35,8 +34,6 @@ static PathKey *make_canonical_pathkey(PlannerInfo *root,
EquivalenceClass *eclass, Oid opfamily, EquivalenceClass *eclass, Oid opfamily,
int strategy, bool nulls_first); int strategy, bool nulls_first);
static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys); static bool pathkey_is_redundant(PathKey *new_pathkey, List *pathkeys);
static Var *find_indexkey_var(PlannerInfo *root, RelOptInfo *rel,
AttrNumber varattno);
static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey); static bool right_merge_direction(PlannerInfo *root, PathKey *pathkey);
...@@ -504,21 +501,24 @@ build_index_pathkeys(PlannerInfo *root, ...@@ -504,21 +501,24 @@ build_index_pathkeys(PlannerInfo *root,
ScanDirection scandir) ScanDirection scandir)
{ {
List *retval = NIL; List *retval = NIL;
ListCell *indexprs_item; ListCell *lc;
int i; int i;
if (index->sortopfamily == NULL) if (index->sortopfamily == NULL)
return NIL; /* non-orderable index */ return NIL; /* non-orderable index */
indexprs_item = list_head(index->indexprs); i = 0;
for (i = 0; i < index->ncolumns; i++) foreach(lc, index->indextlist)
{ {
TargetEntry *indextle = (TargetEntry *) lfirst(lc);
Expr *indexkey;
bool reverse_sort; bool reverse_sort;
bool nulls_first; bool nulls_first;
int ikey;
Expr *indexkey;
PathKey *cpathkey; PathKey *cpathkey;
/* We assume we don't need to make a copy of the tlist item */
indexkey = indextle->expr;
if (ScanDirectionIsBackward(scandir)) if (ScanDirectionIsBackward(scandir))
{ {
reverse_sort = !index->reverse_sort[i]; reverse_sort = !index->reverse_sort[i];
...@@ -530,21 +530,6 @@ build_index_pathkeys(PlannerInfo *root, ...@@ -530,21 +530,6 @@ build_index_pathkeys(PlannerInfo *root,
nulls_first = index->nulls_first[i]; nulls_first = index->nulls_first[i];
} }
ikey = index->indexkeys[i];
if (ikey != 0)
{
/* simple index column */
indexkey = (Expr *) find_indexkey_var(root, index->rel, ikey);
}
else
{
/* expression --- assume we need not copy it */
if (indexprs_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexkey = (Expr *) lfirst(indexprs_item);
indexprs_item = lnext(indexprs_item);
}
/* OK, try to make a canonical pathkey for this sort key */ /* OK, try to make a canonical pathkey for this sort key */
cpathkey = make_pathkey_from_sortinfo(root, cpathkey = make_pathkey_from_sortinfo(root,
indexkey, indexkey,
...@@ -568,44 +553,11 @@ build_index_pathkeys(PlannerInfo *root, ...@@ -568,44 +553,11 @@ build_index_pathkeys(PlannerInfo *root,
/* Add to list unless redundant */ /* Add to list unless redundant */
if (!pathkey_is_redundant(cpathkey, retval)) if (!pathkey_is_redundant(cpathkey, retval))
retval = lappend(retval, cpathkey); retval = lappend(retval, cpathkey);
}
return retval; i++;
}
/*
* Find or make a Var node for the specified attribute of the rel.
*
* We first look for the var in the rel's target list, because that's
* easy and fast. But the var might not be there (this should normally
* only happen for vars that are used in WHERE restriction clauses,
* but not in join clauses or in the SELECT target list). In that case,
* gin up a Var node the hard way.
*/
static Var *
find_indexkey_var(PlannerInfo *root, RelOptInfo *rel, AttrNumber varattno)
{
ListCell *temp;
Index relid;
Oid reloid,
vartypeid,
varcollid;
int32 type_mod;
foreach(temp, rel->reltargetlist)
{
Var *var = (Var *) lfirst(temp);
if (IsA(var, Var) &&
var->varattno == varattno)
return var;
} }
relid = rel->relid; return retval;
reloid = getrelid(relid, root->parse->rtable);
get_atttypetypmodcoll(reloid, varattno, &vartypeid, &type_mod, &varcollid);
return makeVar(relid, varattno, vartypeid, type_mod, varcollid, 0);
} }
/* /*
......
...@@ -53,8 +53,8 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path ...@@ -53,8 +53,8 @@ static Material *create_material_plan(PlannerInfo *root, MaterialPath *best_path
static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path); static Plan *create_unique_plan(PlannerInfo *root, UniquePath *best_path);
static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path, static SeqScan *create_seqscan_plan(PlannerInfo *root, Path *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
static IndexScan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path, static Scan *create_indexscan_plan(PlannerInfo *root, IndexPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses, bool indexonly);
static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root, static BitmapHeapScan *create_bitmap_scan_plan(PlannerInfo *root,
BitmapHeapPath *best_path, BitmapHeapPath *best_path,
List *tlist, List *scan_clauses); List *tlist, List *scan_clauses);
...@@ -95,7 +95,12 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid); ...@@ -95,7 +95,12 @@ static SeqScan *make_seqscan(List *qptlist, List *qpqual, Index scanrelid);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
Oid indexid, List *indexqual, List *indexqualorig, Oid indexid, List *indexqual, List *indexqualorig,
List *indexorderby, List *indexorderbyorig, List *indexorderby, List *indexorderbyorig,
ScanDirection indexscandir, bool indexonly); ScanDirection indexscandir);
static IndexOnlyScan *make_indexonlyscan(List *qptlist, List *qpqual,
Index scanrelid, Oid indexid,
List *indexqual, List *indexorderby,
List *indextlist,
ScanDirection indexscandir);
static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid, static BitmapIndexScan *make_bitmap_indexscan(Index scanrelid, Oid indexid,
List *indexqual, List *indexqual,
List *indexqualorig); List *indexqualorig);
...@@ -206,6 +211,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path) ...@@ -206,6 +211,7 @@ create_plan_recurse(PlannerInfo *root, Path *best_path)
{ {
case T_SeqScan: case T_SeqScan:
case T_IndexScan: case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan: case T_BitmapHeapScan:
case T_TidScan: case T_TidScan:
case T_SubqueryScan: case T_SubqueryScan:
...@@ -274,10 +280,18 @@ create_scan_plan(PlannerInfo *root, Path *best_path) ...@@ -274,10 +280,18 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
*/ */
if (use_physical_tlist(root, rel)) if (use_physical_tlist(root, rel))
{ {
tlist = build_physical_tlist(root, rel); if (best_path->pathtype == T_IndexOnlyScan)
/* if fail because of dropped cols, use regular method */ {
if (tlist == NIL) /* For index-only scan, the preferred tlist is the index's */
tlist = build_relation_tlist(rel); tlist = copyObject(((IndexPath *) best_path)->indexinfo->indextlist);
}
else
{
tlist = build_physical_tlist(root, rel);
/* if fail because of dropped cols, use regular method */
if (tlist == NIL)
tlist = build_relation_tlist(rel);
}
} }
else else
tlist = build_relation_tlist(rel); tlist = build_relation_tlist(rel);
...@@ -302,7 +316,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path) ...@@ -302,7 +316,16 @@ create_scan_plan(PlannerInfo *root, Path *best_path)
plan = (Plan *) create_indexscan_plan(root, plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path, (IndexPath *) best_path,
tlist, tlist,
scan_clauses); scan_clauses,
false);
break;
case T_IndexOnlyScan:
plan = (Plan *) create_indexscan_plan(root,
(IndexPath *) best_path,
tlist,
scan_clauses,
true);
break; break;
case T_BitmapHeapScan: case T_BitmapHeapScan:
...@@ -476,6 +499,7 @@ disuse_physical_tlist(Plan *plan, Path *path) ...@@ -476,6 +499,7 @@ disuse_physical_tlist(Plan *plan, Path *path)
{ {
case T_SeqScan: case T_SeqScan:
case T_IndexScan: case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan: case T_BitmapHeapScan:
case T_TidScan: case T_TidScan:
case T_SubqueryScan: case T_SubqueryScan:
...@@ -1044,16 +1068,23 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path, ...@@ -1044,16 +1068,23 @@ create_seqscan_plan(PlannerInfo *root, Path *best_path,
* Returns an indexscan plan for the base relation scanned by 'best_path' * Returns an indexscan plan for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'. * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
* *
* We use this for both plain IndexScans and IndexOnlyScans, because the
* qual preprocessing work is the same for both. Note that the caller tells
* us which to build --- we don't look at best_path->path.pathtype, because
* create_bitmap_subplan needs to be able to override the prior decision.
*
* The indexquals list of the path contains implicitly-ANDed qual conditions. * The indexquals list of the path contains implicitly-ANDed qual conditions.
* The list can be empty --- then no index restrictions will be applied during * The list can be empty --- then no index restrictions will be applied during
* the scan. * the scan.
*/ */
static IndexScan * static Scan *
create_indexscan_plan(PlannerInfo *root, create_indexscan_plan(PlannerInfo *root,
IndexPath *best_path, IndexPath *best_path,
List *tlist, List *tlist,
List *scan_clauses) List *scan_clauses,
bool indexonly)
{ {
Scan *scan_plan;
List *indexquals = best_path->indexquals; List *indexquals = best_path->indexquals;
List *indexorderbys = best_path->indexorderbys; List *indexorderbys = best_path->indexorderbys;
Index baserelid = best_path->path.parent->relid; Index baserelid = best_path->path.parent->relid;
...@@ -1063,7 +1094,6 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -1063,7 +1094,6 @@ create_indexscan_plan(PlannerInfo *root,
List *fixed_indexquals; List *fixed_indexquals;
List *fixed_indexorderbys; List *fixed_indexorderbys;
ListCell *l; ListCell *l;
IndexScan *scan_plan;
/* it should be a base rel... */ /* it should be a base rel... */
Assert(baserelid > 0); Assert(baserelid > 0);
...@@ -1077,7 +1107,7 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -1077,7 +1107,7 @@ create_indexscan_plan(PlannerInfo *root,
/* /*
* The executor needs a copy with the indexkey on the left of each clause * The executor needs a copy with the indexkey on the left of each clause
* and with index attr numbers substituted for table ones. * and with index Vars substituted for table ones.
*/ */
fixed_indexquals = fix_indexqual_references(root, best_path, indexquals); fixed_indexquals = fix_indexqual_references(root, best_path, indexquals);
...@@ -1175,20 +1205,29 @@ create_indexscan_plan(PlannerInfo *root, ...@@ -1175,20 +1205,29 @@ create_indexscan_plan(PlannerInfo *root,
} }
/* Finally ready to build the plan node */ /* Finally ready to build the plan node */
scan_plan = make_indexscan(tlist, if (indexonly)
qpqual, scan_plan = (Scan *) make_indexonlyscan(tlist,
baserelid, qpqual,
indexoid, baserelid,
fixed_indexquals, indexoid,
stripped_indexquals, fixed_indexquals,
fixed_indexorderbys, fixed_indexorderbys,
indexorderbys, best_path->indexinfo->indextlist,
best_path->indexscandir, best_path->indexscandir);
best_path->indexonly); else
scan_plan = (Scan *) make_indexscan(tlist,
copy_path_costsize(&scan_plan->scan.plan, &best_path->path); qpqual,
baserelid,
indexoid,
fixed_indexquals,
stripped_indexquals,
fixed_indexorderbys,
indexorderbys,
best_path->indexscandir);
copy_path_costsize(&scan_plan->plan, &best_path->path);
/* use the indexscan-specific rows estimate, not the parent rel's */ /* use the indexscan-specific rows estimate, not the parent rel's */
scan_plan->scan.plan.plan_rows = best_path->rows; scan_plan->plan.plan_rows = best_path->rows;
return scan_plan; return scan_plan;
} }
...@@ -1440,7 +1479,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual, ...@@ -1440,7 +1479,9 @@ create_bitmap_subplan(PlannerInfo *root, Path *bitmapqual,
ListCell *l; ListCell *l;
/* Use the regular indexscan plan build machinery... */ /* Use the regular indexscan plan build machinery... */
iscan = create_indexscan_plan(root, ipath, NIL, NIL); iscan = (IndexScan *) create_indexscan_plan(root, ipath,
NIL, NIL, false);
Assert(IsA(iscan, IndexScan));
/* then convert to a bitmap indexscan */ /* then convert to a bitmap indexscan */
plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid, plan = (Plan *) make_bitmap_indexscan(iscan->scan.scanrelid,
iscan->indexid, iscan->indexid,
...@@ -2549,17 +2590,13 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path, ...@@ -2549,17 +2590,13 @@ fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path,
/* /*
* fix_indexqual_operand * fix_indexqual_operand
* Convert an indexqual expression to a Var referencing the index column. * Convert an indexqual expression to a Var referencing the index column.
*
* We represent index keys by Var nodes having varno == INDEX_VAR and varattno
* equal to the index's attribute number (index column position).
*/ */
static Node * static Node *
fix_indexqual_operand(Node *node, IndexOptInfo *index) fix_indexqual_operand(Node *node, IndexOptInfo *index)
{ {
/*
* We represent index keys by Var nodes having the varno of the base table
* but varattno equal to the index's attribute number (index column
* position). This is a bit hokey ... would be cleaner to use a
* special-purpose node type that could not be mistaken for a regular Var.
* But it will do for now.
*/
Var *result; Var *result;
int pos; int pos;
ListCell *indexpr_item; ListCell *indexpr_item;
...@@ -2583,6 +2620,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index) ...@@ -2583,6 +2620,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
if (index->indexkeys[pos] == varatt) if (index->indexkeys[pos] == varatt)
{ {
result = (Var *) copyObject(node); result = (Var *) copyObject(node);
result->varno = INDEX_VAR;
result->varattno = pos + 1; result->varattno = pos + 1;
return (Node *) result; return (Node *) result;
} }
...@@ -2606,7 +2644,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index) ...@@ -2606,7 +2644,7 @@ fix_indexqual_operand(Node *node, IndexOptInfo *index)
if (equal(node, indexkey)) if (equal(node, indexkey))
{ {
/* Found a match */ /* Found a match */
result = makeVar(index->rel->relid, pos + 1, result = makeVar(INDEX_VAR, pos + 1,
exprType(lfirst(indexpr_item)), -1, exprType(lfirst(indexpr_item)), -1,
exprCollation(lfirst(indexpr_item)), exprCollation(lfirst(indexpr_item)),
0); 0);
...@@ -2842,8 +2880,7 @@ make_indexscan(List *qptlist, ...@@ -2842,8 +2880,7 @@ make_indexscan(List *qptlist,
List *indexqualorig, List *indexqualorig,
List *indexorderby, List *indexorderby,
List *indexorderbyorig, List *indexorderbyorig,
ScanDirection indexscandir, ScanDirection indexscandir)
bool indexonly)
{ {
IndexScan *node = makeNode(IndexScan); IndexScan *node = makeNode(IndexScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
...@@ -2860,7 +2897,34 @@ make_indexscan(List *qptlist, ...@@ -2860,7 +2897,34 @@ make_indexscan(List *qptlist,
node->indexorderby = indexorderby; node->indexorderby = indexorderby;
node->indexorderbyorig = indexorderbyorig; node->indexorderbyorig = indexorderbyorig;
node->indexorderdir = indexscandir; node->indexorderdir = indexscandir;
node->indexonly = indexonly;
return node;
}
static IndexOnlyScan *
make_indexonlyscan(List *qptlist,
List *qpqual,
Index scanrelid,
Oid indexid,
List *indexqual,
List *indexorderby,
List *indextlist,
ScanDirection indexscandir)
{
IndexOnlyScan *node = makeNode(IndexOnlyScan);
Plan *plan = &node->scan.plan;
/* cost should be inserted by caller */
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->indexid = indexid;
node->indexqual = indexqual;
node->indexorderby = indexorderby;
node->indextlist = indextlist;
node->indexorderdir = indexscandir;
return node; return node;
} }
......
This diff is collapsed.
...@@ -1974,6 +1974,18 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, ...@@ -1974,6 +1974,18 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
context.paramids = bms_add_members(context.paramids, scan_params); context.paramids = bms_add_members(context.paramids, scan_params);
break; break;
case T_IndexOnlyScan:
finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexqual,
&context);
finalize_primnode((Node *) ((IndexOnlyScan *) plan)->indexorderby,
&context);
/*
* we need not look at indextlist, since it cannot contain Params.
*/
context.paramids = bms_add_members(context.paramids, scan_params);
break;
case T_BitmapIndexScan: case T_BitmapIndexScan:
finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual, finalize_primnode((Node *) ((BitmapIndexScan *) plan)->indexqual,
&context); &context);
......
...@@ -452,7 +452,7 @@ create_index_path(PlannerInfo *root, ...@@ -452,7 +452,7 @@ create_index_path(PlannerInfo *root,
indexscandir = NoMovementScanDirection; indexscandir = NoMovementScanDirection;
} }
pathnode->path.pathtype = T_IndexScan; pathnode->path.pathtype = indexonly ? T_IndexOnlyScan : T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->path.pathkeys = pathkeys; pathnode->path.pathkeys = pathkeys;
...@@ -470,7 +470,6 @@ create_index_path(PlannerInfo *root, ...@@ -470,7 +470,6 @@ create_index_path(PlannerInfo *root,
pathnode->isjoininner = (outer_rel != NULL); pathnode->isjoininner = (outer_rel != NULL);
pathnode->indexscandir = indexscandir; pathnode->indexscandir = indexscandir;
pathnode->indexonly = indexonly;
if (outer_rel != NULL) if (outer_rel != NULL)
{ {
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "access/sysattr.h" #include "access/sysattr.h"
#include "access/transam.h" #include "access/transam.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/heap.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
...@@ -49,6 +50,8 @@ static int32 get_rel_data_width(Relation rel, int32 *attr_widths); ...@@ -49,6 +50,8 @@ static int32 get_rel_data_width(Relation rel, int32 *attr_widths);
static List *get_relation_constraints(PlannerInfo *root, static List *get_relation_constraints(PlannerInfo *root,
Oid relationObjectId, RelOptInfo *rel, Oid relationObjectId, RelOptInfo *rel,
bool include_notnull); bool include_notnull);
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
Relation heapRelation);
/* /*
...@@ -314,6 +317,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, ...@@ -314,6 +317,10 @@ get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent,
ChangeVarNodes((Node *) info->indexprs, 1, varno, 0); ChangeVarNodes((Node *) info->indexprs, 1, varno, 0);
if (info->indpred && varno != 1) if (info->indpred && varno != 1)
ChangeVarNodes((Node *) info->indpred, 1, varno, 0); ChangeVarNodes((Node *) info->indpred, 1, varno, 0);
/* Build targetlist using the completed indexprs data */
info->indextlist = build_index_tlist(root, info, relation);
info->predOK = false; /* set later in indxpath.c */ info->predOK = false; /* set later in indxpath.c */
info->unique = index->indisunique; info->unique = index->indisunique;
info->hypothetical = false; info->hypothetical = false;
...@@ -900,6 +907,70 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel) ...@@ -900,6 +907,70 @@ build_physical_tlist(PlannerInfo *root, RelOptInfo *rel)
return tlist; return tlist;
} }
/*
* build_index_tlist
*
* Build a targetlist representing the columns of the specified index.
* Each column is represented by a Var for the corresponding base-relation
* column, or an expression in base-relation Vars, as appropriate.
*
* There are never any dropped columns in indexes, so unlike
* build_physical_tlist, we need no failure case.
*/
static List *
build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
Relation heapRelation)
{
List *tlist = NIL;
Index varno = index->rel->relid;
ListCell *indexpr_item;
int i;
indexpr_item = list_head(index->indexprs);
for (i = 0; i < index->ncolumns; i++)
{
int indexkey = index->indexkeys[i];
Expr *indexvar;
if (indexkey != 0)
{
/* simple column */
Form_pg_attribute att_tup;
if (indexkey < 0)
att_tup = SystemAttributeDefinition(indexkey,
heapRelation->rd_rel->relhasoids);
else
att_tup = heapRelation->rd_att->attrs[indexkey - 1];
indexvar = (Expr *) makeVar(varno,
indexkey,
att_tup->atttypid,
att_tup->atttypmod,
att_tup->attcollation,
0);
}
else
{
/* expression column */
if (indexpr_item == NULL)
elog(ERROR, "wrong number of index expressions");
indexvar = (Expr *) lfirst(indexpr_item);
indexpr_item = lnext(indexpr_item);
}
tlist = lappend(tlist,
makeTargetEntry(indexvar,
i + 1,
NULL,
false));
}
if (indexpr_item != NULL)
elog(ERROR, "wrong number of index expressions");
return tlist;
}
/* /*
* restriction_selectivity * restriction_selectivity
* *
......
This diff is collapsed.
...@@ -306,7 +306,7 @@ currtid_for_view(Relation viewrel, ItemPointer tid) ...@@ -306,7 +306,7 @@ currtid_for_view(Relation viewrel, ItemPointer tid)
Var *var = (Var *) tle->expr; Var *var = (Var *) tle->expr;
RangeTblEntry *rte; RangeTblEntry *rte;
if (var->varno > 0 && var->varno < INNER && if (!IS_SPECIAL_VARNO(var->varno) &&
var->varattno == SelfItemPointerAttributeNumber) var->varattno == SelfItemPointerAttributeNumber)
{ {
rte = rt_fetch(var->varno, query->rtable); rte = rt_fetch(var->varno, query->rtable);
......
/*-------------------------------------------------------------------------
*
* nodeIndexonlyscan.h
*
*
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/executor/nodeIndexonlyscan.h
*
*-------------------------------------------------------------------------
*/
#ifndef NODEINDEXONLYSCAN_H
#define NODEINDEXONLYSCAN_H
#include "nodes/execnodes.h"
extern IndexOnlyScanState *ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags);
extern TupleTableSlot *ExecIndexOnlyScan(IndexOnlyScanState *node);
extern void ExecEndIndexOnlyScan(IndexOnlyScanState *node);
extern void ExecIndexOnlyMarkPos(IndexOnlyScanState *node);
extern void ExecIndexOnlyRestrPos(IndexOnlyScanState *node);
extern void ExecReScanIndexOnlyScan(IndexOnlyScanState *node);
#endif /* NODEINDEXONLYSCAN_H */
...@@ -23,9 +23,12 @@ extern void ExecIndexMarkPos(IndexScanState *node); ...@@ -23,9 +23,12 @@ extern void ExecIndexMarkPos(IndexScanState *node);
extern void ExecIndexRestrPos(IndexScanState *node); extern void ExecIndexRestrPos(IndexScanState *node);
extern void ExecReScanIndexScan(IndexScanState *node); extern void ExecReScanIndexScan(IndexScanState *node);
/* routines exported to share code with nodeBitmapIndexscan.c */ /*
* These routines are exported to share code with nodeIndexonlyscan.c and
* nodeBitmapIndexscan.c
*/
extern void ExecIndexBuildScanKeys(PlanState *planstate, Relation index, extern void ExecIndexBuildScanKeys(PlanState *planstate, Relation index,
Index scanrelid, List *quals, bool isorderby, List *quals, bool isorderby,
ScanKey *scanKeys, int *numScanKeys, ScanKey *scanKeys, int *numScanKeys,
IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys, IndexRuntimeKeyInfo **runtimeKeys, int *numRuntimeKeys,
IndexArrayKeyInfo **arrayKeys, int *numArrayKeys); IndexArrayKeyInfo **arrayKeys, int *numArrayKeys);
......
...@@ -1226,7 +1226,6 @@ typedef struct ...@@ -1226,7 +1226,6 @@ typedef struct
* RuntimeContext expr context for evaling runtime Skeys * RuntimeContext expr context for evaling runtime Skeys
* RelationDesc index relation descriptor * RelationDesc index relation descriptor
* ScanDesc index scan descriptor * ScanDesc index scan descriptor
* VMBuffer buffer in use for visibility map testing, if any
* ---------------- * ----------------
*/ */
typedef struct IndexScanState typedef struct IndexScanState
...@@ -1243,9 +1242,42 @@ typedef struct IndexScanState ...@@ -1243,9 +1242,42 @@ typedef struct IndexScanState
ExprContext *iss_RuntimeContext; ExprContext *iss_RuntimeContext;
Relation iss_RelationDesc; Relation iss_RelationDesc;
IndexScanDesc iss_ScanDesc; IndexScanDesc iss_ScanDesc;
Buffer iss_VMBuffer;
} IndexScanState; } IndexScanState;
/* ----------------
* IndexOnlyScanState information
*
* indexqual execution state for indexqual expressions
* ScanKeys Skey structures for index quals
* NumScanKeys number of ScanKeys
* OrderByKeys Skey structures for index ordering operators
* NumOrderByKeys number of OrderByKeys
* RuntimeKeys info about Skeys that must be evaluated at runtime
* NumRuntimeKeys number of RuntimeKeys
* RuntimeKeysReady true if runtime Skeys have been computed
* RuntimeContext expr context for evaling runtime Skeys
* RelationDesc index relation descriptor
* ScanDesc index scan descriptor
* VMBuffer buffer in use for visibility map testing, if any
* ----------------
*/
typedef struct IndexOnlyScanState
{
ScanState ss; /* its first field is NodeTag */
List *indexqual;
ScanKey ioss_ScanKeys;
int ioss_NumScanKeys;
ScanKey ioss_OrderByKeys;
int ioss_NumOrderByKeys;
IndexRuntimeKeyInfo *ioss_RuntimeKeys;
int ioss_NumRuntimeKeys;
bool ioss_RuntimeKeysReady;
ExprContext *ioss_RuntimeContext;
Relation ioss_RelationDesc;
IndexScanDesc ioss_ScanDesc;
Buffer ioss_VMBuffer;
} IndexOnlyScanState;
/* ---------------- /* ----------------
* BitmapIndexScanState information * BitmapIndexScanState information
* *
......
...@@ -52,6 +52,7 @@ typedef enum NodeTag ...@@ -52,6 +52,7 @@ typedef enum NodeTag
T_Scan, T_Scan,
T_SeqScan, T_SeqScan,
T_IndexScan, T_IndexScan,
T_IndexOnlyScan,
T_BitmapIndexScan, T_BitmapIndexScan,
T_BitmapHeapScan, T_BitmapHeapScan,
T_TidScan, T_TidScan,
...@@ -97,6 +98,7 @@ typedef enum NodeTag ...@@ -97,6 +98,7 @@ typedef enum NodeTag
T_ScanState, T_ScanState,
T_SeqScanState, T_SeqScanState,
T_IndexScanState, T_IndexScanState,
T_IndexOnlyScanState,
T_BitmapIndexScanState, T_BitmapIndexScanState,
T_BitmapHeapScanState, T_BitmapHeapScanState,
T_TidScanState, T_TidScanState,
......
...@@ -285,11 +285,8 @@ typedef Scan SeqScan; ...@@ -285,11 +285,8 @@ typedef Scan SeqScan;
* *
* indexqual has the same form, but the expressions have been commuted if * indexqual has the same form, but the expressions have been commuted if
* necessary to put the indexkeys on the left, and the indexkeys are replaced * necessary to put the indexkeys on the left, and the indexkeys are replaced
* by Var nodes identifying the index columns (varattno is the index column * by Var nodes identifying the index columns (their varno is INDEX_VAR and
* position, not the base table's column, even though varno is for the base * their varattno is the index column number).
* table). This is a bit hokey ... would be cleaner to use a special-purpose
* node type that could not be mistaken for a regular Var. But it will do
* for now.
* *
* indexorderbyorig is similarly the original form of any ORDER BY expressions * indexorderbyorig is similarly the original form of any ORDER BY expressions
* that are being implemented by the index, while indexorderby is modified to * that are being implemented by the index, while indexorderby is modified to
...@@ -302,8 +299,7 @@ typedef Scan SeqScan; ...@@ -302,8 +299,7 @@ typedef Scan SeqScan;
* (Note these fields are used for amcanorderbyop cases, not amcanorder cases.) * (Note these fields are used for amcanorderbyop cases, not amcanorder cases.)
* *
* indexorderdir specifies the scan ordering, for indexscans on amcanorder * indexorderdir specifies the scan ordering, for indexscans on amcanorder
* indexes (for other indexes it should be "don't care"). indexonly specifies * indexes (for other indexes it should be "don't care").
* an index-only scan, for indexscans on amcanreturn indexes.
* ---------------- * ----------------
*/ */
typedef struct IndexScan typedef struct IndexScan
...@@ -315,9 +311,35 @@ typedef struct IndexScan ...@@ -315,9 +311,35 @@ typedef struct IndexScan
List *indexorderby; /* list of index ORDER BY exprs */ List *indexorderby; /* list of index ORDER BY exprs */
List *indexorderbyorig; /* the same in original form */ List *indexorderbyorig; /* the same in original form */
ScanDirection indexorderdir; /* forward or backward or don't care */ ScanDirection indexorderdir; /* forward or backward or don't care */
bool indexonly; /* attempt to skip heap fetches? */
} IndexScan; } IndexScan;
/* ----------------
* index-only scan node
*
* IndexOnlyScan is very similar to IndexScan, but it specifies an
* index-only scan, in which the data comes from the index not the heap.
* Because of this, *all* Vars in the plan node's targetlist, qual, and
* index expressions reference index columns and have varno = INDEX_VAR.
* Hence we do not need separate indexqualorig and indexorderbyorig lists,
* since their contents would be equivalent to indexqual and indexorderby.
*
* To help EXPLAIN interpret the index Vars for display, we provide
* indextlist, which represents the contents of the index as a targetlist
* with one TLE per index column. Vars appearing in this list reference
* the base table, and this is the only field in the plan node that may
* contain such Vars.
* ----------------
*/
typedef struct IndexOnlyScan
{
Scan scan;
Oid indexid; /* OID of index to scan */
List *indexqual; /* list of index quals (usually OpExprs) */
List *indexorderby; /* list of index ORDER BY exprs */
List *indextlist; /* TargetEntry list describing index's cols */
ScanDirection indexorderdir; /* forward or backward or don't care */
} IndexOnlyScan;
/* ---------------- /* ----------------
* bitmap index scan node * bitmap index scan node
* *
......
...@@ -118,15 +118,19 @@ typedef struct Expr ...@@ -118,15 +118,19 @@ typedef struct Expr
* Note: during parsing/planning, varnoold/varoattno are always just copies * Note: during parsing/planning, varnoold/varoattno are always just copies
* of varno/varattno. At the tail end of planning, Var nodes appearing in * of varno/varattno. At the tail end of planning, Var nodes appearing in
* upper-level plan nodes are reassigned to point to the outputs of their * upper-level plan nodes are reassigned to point to the outputs of their
* subplans; for example, in a join node varno becomes INNER or OUTER and * subplans; for example, in a join node varno becomes INNER_VAR or OUTER_VAR
* varattno becomes the index of the proper element of that subplan's target * and varattno becomes the index of the proper element of that subplan's
* list. But varnoold/varoattno continue to hold the original values. * target list. But varnoold/varoattno continue to hold the original values.
* The code doesn't really need varnoold/varoattno, but they are very useful * The code doesn't really need varnoold/varoattno, but they are very useful
* for debugging and interpreting completed plans, so we keep them around. * for debugging and interpreting completed plans, so we keep them around.
*/ */
#define INNER 65000 #define INNER_VAR 65000 /* reference to inner subplan */
#define OUTER 65001 #define OUTER_VAR 65001 /* reference to outer subplan */
#define INDEX_VAR 65002 /* reference to index column */
#define IS_SPECIAL_VARNO(varno) ((varno) >= INNER_VAR)
/* Symbols for the indexes of the special RTE entries in rules */
#define PRS2_OLD_VARNO 1 #define PRS2_OLD_VARNO 1
#define PRS2_NEW_VARNO 2 #define PRS2_NEW_VARNO 2
...@@ -134,7 +138,7 @@ typedef struct Var ...@@ -134,7 +138,7 @@ typedef struct Var
{ {
Expr xpr; Expr xpr;
Index varno; /* index of this var's relation in the range Index varno; /* index of this var's relation in the range
* table (could also be INNER or OUTER) */ * table, or INNER_VAR/OUTER_VAR/INDEX_VAR */
AttrNumber varattno; /* attribute number of this var, or zero for AttrNumber varattno; /* attribute number of this var, or zero for
* all */ * all */
Oid vartype; /* pg_type OID for the type of this var */ Oid vartype; /* pg_type OID for the type of this var */
......
...@@ -449,6 +449,10 @@ typedef struct RelOptInfo ...@@ -449,6 +449,10 @@ typedef struct RelOptInfo
* The indexprs and indpred expressions have been run through * The indexprs and indpred expressions have been run through
* prepqual.c and eval_const_expressions() for ease of matching to * prepqual.c and eval_const_expressions() for ease of matching to
* WHERE clauses. indpred is in implicit-AND form. * WHERE clauses. indpred is in implicit-AND form.
*
* indextlist is a TargetEntry list representing the index columns.
* It provides an equivalent base-relation Var for each simple column,
* and links to the matching indexprs element for each expression column.
*/ */
typedef struct IndexOptInfo typedef struct IndexOptInfo
{ {
...@@ -478,6 +482,8 @@ typedef struct IndexOptInfo ...@@ -478,6 +482,8 @@ typedef struct IndexOptInfo
List *indexprs; /* expressions for non-simple index columns */ List *indexprs; /* expressions for non-simple index columns */
List *indpred; /* predicate if a partial index, else NIL */ List *indpred; /* predicate if a partial index, else NIL */
List *indextlist; /* targetlist representing index columns */
bool predOK; /* true if predicate matches query */ bool predOK; /* true if predicate matches query */
bool unique; /* true if a unique index */ bool unique; /* true if a unique index */
bool hypothetical; /* true if index doesn't really exist */ bool hypothetical; /* true if index doesn't really exist */
...@@ -640,6 +646,9 @@ typedef struct Path ...@@ -640,6 +646,9 @@ typedef struct Path
/*---------- /*----------
* IndexPath represents an index scan over a single index. * IndexPath represents an index scan over a single index.
* *
* This struct is used for both regular indexscans and index-only scans;
* path.pathtype is T_IndexScan or T_IndexOnlyScan to show which is meant.
*
* 'indexinfo' is the index to be scanned. * 'indexinfo' is the index to be scanned.
* *
* 'indexclauses' is a list of index qualification clauses, with implicit * 'indexclauses' is a list of index qualification clauses, with implicit
...@@ -673,14 +682,10 @@ typedef struct Path ...@@ -673,14 +682,10 @@ typedef struct Path
* NoMovementScanDirection for an indexscan, but the planner wants to * NoMovementScanDirection for an indexscan, but the planner wants to
* distinguish ordered from unordered indexes for building pathkeys.) * distinguish ordered from unordered indexes for building pathkeys.)
* *
* 'indexonly' is TRUE for an index-only scan, that is, the index's access
* method has amcanreturn = TRUE and we only need columns available from the
* index.
*
* 'indextotalcost' and 'indexselectivity' are saved in the IndexPath so that * 'indextotalcost' and 'indexselectivity' are saved in the IndexPath so that
* we need not recompute them when considering using the same index in a * we need not recompute them when considering using the same index in a
* bitmap index/heap scan (see BitmapHeapPath). The costs of the IndexPath * bitmap index/heap scan (see BitmapHeapPath). The costs of the IndexPath
* itself represent the costs of an IndexScan plan type. * itself represent the costs of an IndexScan or IndexOnlyScan plan type.
* *
* 'rows' is the estimated result tuple count for the indexscan. This * 'rows' is the estimated result tuple count for the indexscan. This
* is the same as path.parent->rows for a simple indexscan, but it is * is the same as path.parent->rows for a simple indexscan, but it is
...@@ -698,7 +703,6 @@ typedef struct IndexPath ...@@ -698,7 +703,6 @@ typedef struct IndexPath
List *indexorderbys; List *indexorderbys;
bool isjoininner; bool isjoininner;
ScanDirection indexscandir; ScanDirection indexscandir;
bool indexonly;
Cost indextotalcost; Cost indextotalcost;
Selectivity indexselectivity; Selectivity indexselectivity;
double rows; /* estimated number of result tuples */ double rows; /* estimated number of result tuples */
...@@ -714,11 +718,12 @@ typedef struct IndexPath ...@@ -714,11 +718,12 @@ typedef struct IndexPath
* The individual indexscans are represented by IndexPath nodes, and any * The individual indexscans are represented by IndexPath nodes, and any
* logic on top of them is represented by a tree of BitmapAndPath and * logic on top of them is represented by a tree of BitmapAndPath and
* BitmapOrPath nodes. Notice that we can use the same IndexPath node both * BitmapOrPath nodes. Notice that we can use the same IndexPath node both
* to represent a regular IndexScan plan, and as the child of a BitmapHeapPath * to represent a regular (or index-only) index scan plan, and as the child
* that represents scanning the same index using a BitmapIndexScan. The * of a BitmapHeapPath that represents scanning the same index using a
* startup_cost and total_cost figures of an IndexPath always represent the * BitmapIndexScan. The startup_cost and total_cost figures of an IndexPath
* costs to use it as a regular IndexScan. The costs of a BitmapIndexScan * always represent the costs to use it as a regular (or index-only)
* can be computed using the IndexPath's indextotalcost and indexselectivity. * IndexScan. The costs of a BitmapIndexScan can be computed using the
* IndexPath's indextotalcost and indexselectivity.
* *
* BitmapHeapPaths can be nestloop inner indexscans. The isjoininner and * BitmapHeapPaths can be nestloop inner indexscans. The isjoininner and
* rows fields serve the same purpose as for plain IndexPaths. * rows fields serve the same purpose as for plain IndexPaths.
......
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