aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2011-10-11 14:20:06 -0400
committerTom Lane <tgl@sss.pgh.pa.us>2011-10-11 14:21:30 -0400
commita0185461dd94c8d31d8d55a7f2839b0d2f172ab9 (patch)
tree3bd68d4e123336bbdefa8fd92372f0af7fb6d64f /src/backend/utils/adt/ruleutils.c
parentfa351d5a0db0672b6f586315720302e493116f27 (diff)
downloadpostgresql-a0185461dd94c8d31d8d55a7f2839b0d2f172ab9.tar.gz
postgresql-a0185461dd94c8d31d8d55a7f2839b0d2f172ab9.zip
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).
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c159
1 files changed, 106 insertions, 53 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index c112a9cc163..75923a6f2ea 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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);