1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

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).
This commit is contained in:
Tom Lane
2011-10-11 14:20:06 -04:00
parent fa351d5a0d
commit a0185461dd
34 changed files with 1312 additions and 419 deletions

View File

@ -107,9 +107,11 @@ typedef struct
* deparse_namespace list (since a plan tree never contains Vars with
* varlevelsup > 0). We store the PlanState node that is the immediate
* parent of the expression to be deparsed, as well as a list of that
* PlanState's ancestors. In addition, we store the outer and inner
* subplan nodes, whose targetlists are used to resolve OUTER and INNER Vars.
* (Note: these could be derived on-the-fly from the planstate instead.)
* PlanState's ancestors. In addition, we store its outer and inner subplan
* state nodes, as well as their plan nodes' targetlists, and the indextlist
* if the current PlanState is an IndexOnlyScanState. (These fields could
* be derived on-the-fly from the current PlanState, but it seems notationally
* clearer to set them up as separate fields.)
*/
typedef struct
{
@ -118,10 +120,11 @@ typedef struct
/* Remaining fields are used only when deparsing a Plan tree: */
PlanState *planstate; /* immediate parent of current expression */
List *ancestors; /* ancestors of planstate */
PlanState *outer_planstate; /* OUTER subplan state, or NULL if none */
PlanState *inner_planstate; /* INNER subplan state, or NULL if none */
Plan *outer_plan; /* OUTER subplan, or NULL if none */
Plan *inner_plan; /* INNER subplan, or NULL if none */
PlanState *outer_planstate; /* outer subplan state, or NULL if none */
PlanState *inner_planstate; /* inner subplan state, or NULL if none */
List *outer_tlist; /* referent for OUTER_VAR Vars */
List *inner_tlist; /* referent for INNER_VAR Vars */
List *index_tlist; /* referent for INDEX_VAR Vars */
} deparse_namespace;
@ -2162,9 +2165,14 @@ deparse_context_for(const char *aliasname, Oid relid)
* deparse_context_for_planstate - Build deparse context for a plan
*
* When deparsing an expression in a Plan tree, we might have to resolve
* OUTER or INNER references. To do this, the caller must provide the
* parent PlanState node. Then OUTER and INNER references can be resolved
* by drilling down into the left and right child plans.
* OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
* provide the parent PlanState node. Then OUTER_VAR and INNER_VAR references
* can be resolved by drilling down into the left and right child plans.
* Similarly, INDEX_VAR references can be resolved by reference to the
* indextlist given in the parent IndexOnlyScan node. (Note that we don't
* currently support deparsing of indexquals in regular IndexScan or
* BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
* fields, which won't contain INDEX_VAR Vars.)
*
* Note: planstate really ought to be declared as "PlanState *", but we use
* "Node *" to avoid having to include execnodes.h in builtins.h.
@ -2175,7 +2183,7 @@ deparse_context_for(const char *aliasname, Oid relid)
*
* The plan's rangetable list must also be passed. We actually prefer to use
* the rangetable to resolve simple Vars, but the plan inputs are necessary
* for Vars that reference expressions computed in subplan target lists.
* for Vars with special varnos.
*/
List *
deparse_context_for_planstate(Node *planstate, List *ancestors,
@ -2201,10 +2209,11 @@ deparse_context_for_planstate(Node *planstate, List *ancestors,
* set_deparse_planstate: set up deparse_namespace to parse subexpressions
* of a given PlanState node
*
* This sets the planstate, outer_planstate, inner_planstate, outer_plan, and
* inner_plan fields. Caller is responsible for adjusting the ancestors list
* if necessary. Note that the rtable and ctes fields do not need to change
* when shifting attention to different plan nodes in a single plan tree.
* This sets the planstate, outer_planstate, inner_planstate, outer_tlist,
* inner_tlist, and index_tlist fields. Caller is responsible for adjusting
* the ancestors list if necessary. Note that the rtable and ctes fields do
* not need to change when shifting attention to different plan nodes in a
* single plan tree.
*/
static void
set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
@ -2229,9 +2238,9 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
dpns->outer_planstate = outerPlanState(ps);
if (dpns->outer_planstate)
dpns->outer_plan = dpns->outer_planstate->plan;
dpns->outer_tlist = dpns->outer_planstate->plan->targetlist;
else
dpns->outer_plan = NULL;
dpns->outer_tlist = NIL;
/*
* For a SubqueryScan, pretend the subplan is INNER referent. (We don't
@ -2246,18 +2255,25 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
dpns->inner_planstate = innerPlanState(ps);
if (dpns->inner_planstate)
dpns->inner_plan = dpns->inner_planstate->plan;
dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
else
dpns->inner_plan = NULL;
dpns->inner_tlist = NIL;
/* index_tlist is set only if it's an IndexOnlyScan */
if (IsA(ps->plan, IndexOnlyScan))
dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
else
dpns->index_tlist = NIL;
}
/*
* push_child_plan: temporarily transfer deparsing attention to a child plan
*
* When expanding an OUTER or INNER reference, we must adjust the deparse
* context in case the referenced expression itself uses OUTER/INNER. We
* modify the top stack entry in-place to avoid affecting levelsup issues
* (although in a Plan tree there really shouldn't be any).
* When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
* deparse context in case the referenced expression itself uses
* OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
* affecting levelsup issues (although in a Plan tree there really shouldn't
* be any).
*
* Caller must provide a local deparse_namespace variable to save the
* previous state for pop_child_plan.
@ -2271,10 +2287,11 @@ push_child_plan(deparse_namespace *dpns, PlanState *ps,
/*
* Currently we don't bother to adjust the ancestors list, because an
* OUTER or INNER reference really shouldn't contain any Params that would
* be set by the parent node itself. If we did want to adjust it,
* lcons'ing dpns->planstate onto dpns->ancestors would be the appropriate
* thing --- and pop_child_plan would need to undo the change to the list.
* OUTER_VAR or INNER_VAR reference really shouldn't contain any Params
* that would be set by the parent node itself. If we did want to adjust
* the list, lcons'ing dpns->planstate onto dpns->ancestors would be the
* appropriate thing --- and pop_child_plan would need to undo the change
* to the list.
*/
/* Set attention on selected child */
@ -2298,7 +2315,7 @@ pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
* When expanding a Param reference, we must adjust the deparse context
* to match the plan node that contains the expression being printed;
* otherwise we'd fail if that expression itself contains a Param or
* OUTER/INNER variables.
* OUTER_VAR/INNER_VAR/INDEX_VAR variable.
*
* The target ancestor is conveniently identified by the ListCell holding it
* in dpns->ancestors.
@ -3716,22 +3733,22 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
/*
* Try to find the relevant RTE in this rtable. In a plan tree, it's
* likely that varno is OUTER or INNER, in which case we must dig down
* into the subplans.
* likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
* down into the subplans, or INDEX_VAR, which is resolved similarly.
*/
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
else if (var->varno == OUTER && dpns->outer_plan)
else if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
@ -3749,14 +3766,14 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INNER && dpns->inner_plan)
else if (var->varno == INNER_VAR && dpns->inner_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
@ -3774,6 +3791,28 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
pop_child_plan(dpns, &save_dpns);
return NULL;
}
else if (var->varno == INDEX_VAR && dpns->index_tlist)
{
TargetEntry *tle;
tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
/*
* Force parentheses because our caller probably assumed a Var is a
* simple expression.
*/
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, '(');
get_rule_expr((Node *) tle->expr, context, true);
if (!IsA(tle->expr, Var))
appendStringInfoChar(buf, ')');
return NULL;
}
else
{
elog(ERROR, "bogus varno: %d", var->varno);
@ -3789,16 +3828,16 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
* no alias. So in that case, drill down to the subplan and print the
* contents of the referenced tlist item. This works because in a plan
* tree, such Vars can only occur in a SubqueryScan or CteScan node, and
* we'll have set dpns->inner_plan to reference the child plan node.
* we'll have set dpns->inner_planstate to reference the child plan node.
*/
if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
attnum > list_length(rte->eref->colnames) &&
dpns->inner_plan)
dpns->inner_planstate)
{
TargetEntry *tle;
deparse_namespace save_dpns;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);
@ -3984,23 +4023,23 @@ get_name_for_var_field(Var *var, int fieldno,
/*
* Try to find the relevant RTE in this rtable. In a plan tree, it's
* likely that varno is OUTER or INNER, in which case we must dig down
* into the subplans.
* likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
* down into the subplans, or INDEX_VAR, which is resolved similarly.
*/
if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
{
rte = rt_fetch(var->varno, dpns->rtable);
attnum = var->varattno;
}
else if (var->varno == OUTER && dpns->outer_plan)
else if (var->varno == OUTER_VAR && dpns->outer_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
@ -4011,15 +4050,15 @@ get_name_for_var_field(Var *var, int fieldno,
pop_child_plan(dpns, &save_dpns);
return result;
}
else if (var->varno == INNER && dpns->inner_plan)
else if (var->varno == INNER_VAR && dpns->inner_tlist)
{
TargetEntry *tle;
deparse_namespace save_dpns;
const char *result;
tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
@ -4030,6 +4069,22 @@ get_name_for_var_field(Var *var, int fieldno,
pop_child_plan(dpns, &save_dpns);
return result;
}
else if (var->varno == INDEX_VAR && dpns->index_tlist)
{
TargetEntry *tle;
const char *result;
tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
if (!tle)
elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
Assert(netlevelsup == 0);
result = get_name_for_var_field((Var *) tle->expr, fieldno,
levelsup, context);
return result;
}
else
{
elog(ERROR, "bogus varno: %d", var->varno);
@ -4115,11 +4170,10 @@ get_name_for_var_field(Var *var, int fieldno,
deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
if (!dpns->inner_planstate)
elog(ERROR, "failed to find plan for subquery %s",
rte->eref->aliasname);
tle = get_tle_by_resno(dpns->inner_plan->targetlist,
attnum);
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);
@ -4232,11 +4286,10 @@ get_name_for_var_field(Var *var, int fieldno,
deparse_namespace save_dpns;
const char *result;
if (!dpns->inner_plan)
if (!dpns->inner_planstate)
elog(ERROR, "failed to find plan for CTE %s",
rte->eref->aliasname);
tle = get_tle_by_resno(dpns->inner_plan->targetlist,
attnum);
tle = get_tle_by_resno(dpns->inner_tlist, attnum);
if (!tle)
elog(ERROR, "bogus varattno for subquery var: %d",
attnum);