aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/executor/execMain.c23
-rw-r--r--src/backend/executor/execScan.c108
-rw-r--r--src/backend/executor/nodeIndexscan.c14
-rw-r--r--src/backend/executor/nodeSeqscan.c4
-rw-r--r--src/backend/executor/nodeTidscan.c14
-rw-r--r--src/backend/optimizer/plan/createplan.c78
-rw-r--r--src/backend/optimizer/plan/planner.c158
-rw-r--r--src/backend/optimizer/util/plancat.c248
-rw-r--r--src/backend/optimizer/util/relnode.c17
-rw-r--r--src/include/executor/executor.h3
-rw-r--r--src/include/nodes/relation.h4
-rw-r--r--src/include/optimizer/plancat.h7
12 files changed, 462 insertions, 216 deletions
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index e1178cd6a69..9c4b6e74819 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.199 2003/01/23 05:10:37 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.200 2003/02/03 15:07:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -612,9 +612,11 @@ InitPlan(QueryDesc *queryDesc)
tupType = ExecGetTupType(planstate);
/*
- * Initialize the junk filter if needed. SELECT and INSERT queries
- * need a filter if there are any junk attrs in the tlist. UPDATE and
- * DELETE always need one, since there's always a junk 'ctid'
+ * Initialize the junk filter if needed. SELECT and INSERT queries need a
+ * filter if there are any junk attrs in the tlist. INSERT and SELECT
+ * INTO also need a filter if the top plan node is a scan node that's not
+ * doing projection (else we'll be scribbling on the scan tuple!) UPDATE
+ * and DELETE always need a filter, since there's always a junk 'ctid'
* attribute present --- no need to look first.
*/
{
@@ -635,6 +637,19 @@ InitPlan(QueryDesc *queryDesc)
break;
}
}
+ if (!junk_filter_needed &&
+ (operation == CMD_INSERT || do_select_into))
+ {
+ if (IsA(planstate, SeqScanState) ||
+ IsA(planstate, IndexScanState) ||
+ IsA(planstate, TidScanState) ||
+ IsA(planstate, SubqueryScanState) ||
+ IsA(planstate, FunctionScanState))
+ {
+ if (planstate->ps_ProjInfo == NULL)
+ junk_filter_needed = true;
+ }
+ }
break;
case CMD_UPDATE:
case CMD_DELETE:
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 6944e03e9bc..9352c79d81e 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -12,19 +12,20 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.22 2002/12/05 15:50:32 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.23 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include <sys/file.h>
-
#include "executor/executor.h"
#include "miscadmin.h"
#include "utils/memutils.h"
+static bool tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc);
+
+
/* ----------------------------------------------------------------
* ExecScan
*
@@ -50,6 +51,7 @@ ExecScan(ScanState *node,
EState *estate;
ExprContext *econtext;
List *qual;
+ ProjectionInfo *projInfo;
ExprDoneCond isDone;
TupleTableSlot *resultSlot;
@@ -59,6 +61,7 @@ ExecScan(ScanState *node,
estate = node->ps.state;
econtext = node->ps.ps_ExprContext;
qual = node->ps.qual;
+ projInfo = node->ps.ps_ProjInfo;
/*
* Check to see if we're still projecting out tuples from a previous
@@ -67,7 +70,8 @@ ExecScan(ScanState *node,
*/
if (node->ps.ps_TupFromTlist)
{
- resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
+ Assert(projInfo); /* can't get here if not projecting */
+ resultSlot = ExecProject(projInfo, &isDone);
if (isDone == ExprMultipleResult)
return resultSlot;
/* Done with that source tuple... */
@@ -101,10 +105,13 @@ ExecScan(ScanState *node,
*/
if (TupIsNull(slot))
{
- return ExecStoreTuple(NULL,
- node->ps.ps_ProjInfo->pi_slot,
- InvalidBuffer,
- true);
+ if (projInfo)
+ return ExecStoreTuple(NULL,
+ projInfo->pi_slot,
+ InvalidBuffer,
+ true);
+ else
+ return slot;
}
/*
@@ -123,16 +130,27 @@ ExecScan(ScanState *node,
{
/*
* Found a satisfactory scan tuple.
- *
- * Form a projection tuple, store it in the result tuple slot and
- * return it --- unless we find we can project no tuples from
- * this scan tuple, in which case continue scan.
*/
- resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
- if (isDone != ExprEndResult)
+ if (projInfo)
{
- node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
- return resultSlot;
+ /*
+ * Form a projection tuple, store it in the result tuple slot
+ * and return it --- unless we find we can project no tuples
+ * from this scan tuple, in which case continue scan.
+ */
+ resultSlot = ExecProject(projInfo, &isDone);
+ if (isDone != ExprEndResult)
+ {
+ node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
+ return resultSlot;
+ }
+ }
+ else
+ {
+ /*
+ * Here, we aren't projecting, so just return scan tuple.
+ */
+ return slot;
}
}
@@ -142,3 +160,61 @@ ExecScan(ScanState *node,
ResetExprContext(econtext);
}
}
+
+/*
+ * ExecAssignScanProjectionInfo
+ * Set up projection info for a scan node, if necessary.
+ *
+ * We can avoid a projection step if the requested tlist exactly matches
+ * the underlying tuple type. If so, we just set ps_ProjInfo to NULL.
+ * Note that this case occurs not only for simple "SELECT * FROM ...", but
+ * also in most cases where there are joins or other processing nodes above
+ * the scan node, because the planner will preferentially generate a matching
+ * tlist.
+ *
+ * ExecAssignScanType must have been called already.
+ */
+void
+ExecAssignScanProjectionInfo(ScanState *node)
+{
+ Scan *scan = (Scan *) node->ps.plan;
+
+ if (tlist_matches_tupdesc(scan->plan.targetlist,
+ scan->scanrelid,
+ node->ss_ScanTupleSlot->ttc_tupleDescriptor))
+ node->ps.ps_ProjInfo = NULL;
+ else
+ ExecAssignProjectionInfo(&node->ps);
+}
+
+static bool
+tlist_matches_tupdesc(List *tlist, Index varno, TupleDesc tupdesc)
+{
+ int numattrs = tupdesc->natts;
+ int attrno;
+
+ for (attrno = 1; attrno <= numattrs; attrno++)
+ {
+ Form_pg_attribute att_tup = tupdesc->attrs[attrno - 1];
+ Var *var;
+
+ if (tlist == NIL)
+ return false; /* tlist too short */
+ var = (Var *) ((TargetEntry *) lfirst(tlist))->expr;
+ if (!var || !IsA(var, Var))
+ return false; /* tlist item not a Var */
+ Assert(var->varno == varno);
+ if (var->varattno != attrno)
+ return false; /* out of order */
+ Assert(var->vartype == att_tup->atttypid);
+ Assert(var->vartypmod == att_tup->atttypmod);
+ Assert(var->varlevelsup == 0);
+
+ tlist = lnext(tlist);
+ }
+
+ if (tlist)
+ return false; /* tlist too long */
+
+ return true;
+}
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index ad8680d0ff4..6d95b111c01 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.77 2003/01/12 22:01:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.78 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -583,12 +583,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
ExecInitScanTupleSlot(estate, &indexstate->ss);
/*
- * Initialize result tuple type and projection info.
- */
- ExecAssignResultTypeFromTL(&indexstate->ss.ps);
- ExecAssignProjectionInfo(&indexstate->ss.ps);
-
- /*
* Initialize index-specific scan state
*/
indexstate->iss_NumIndices = 0;
@@ -918,6 +912,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
indexstate->iss_ScanDescs = scanDescs;
/*
+ * Initialize result tuple type and projection info.
+ */
+ ExecAssignResultTypeFromTL(&indexstate->ss.ps);
+ ExecAssignScanProjectionInfo(&indexstate->ss);
+
+ /*
* all done.
*/
return indexstate;
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 713dd76234d..47d2e4eb497 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.42 2003/01/12 22:01:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.43 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -232,7 +232,7 @@ ExecInitSeqScan(SeqScan *node, EState *estate)
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&scanstate->ps);
- ExecAssignProjectionInfo(&scanstate->ps);
+ ExecAssignScanProjectionInfo(scanstate);
return scanstate;
}
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 3a51057f282..e1a2165709e 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.31 2003/01/12 22:01:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.32 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -384,12 +384,6 @@ ExecInitTidScan(TidScan *node, EState *estate)
ExecInitScanTupleSlot(estate, &tidstate->ss);
/*
- * Initialize result tuple type and projection info.
- */
- ExecAssignResultTypeFromTL(&tidstate->ss.ps);
- ExecAssignProjectionInfo(&tidstate->ss.ps);
-
- /*
* get the tid node information
*/
tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
@@ -439,6 +433,12 @@ ExecInitTidScan(TidScan *node, EState *estate)
tidstate->ss.ps.chgParam = execParam;
/*
+ * Initialize result tuple type and projection info.
+ */
+ ExecAssignResultTypeFromTL(&tidstate->ss.ps);
+ ExecAssignScanProjectionInfo(&tidstate->ss);
+
+ /*
* all done.
*/
return tidstate;
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index eb7e922d9a1..d78dd2c1e83 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.133 2003/01/22 00:07:00 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.134 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,6 +34,7 @@
static Scan *create_scan_plan(Query *root, Path *best_path);
+static bool use_physical_tlist(RelOptInfo *rel);
static Join *create_join_plan(Query *root, JoinPath *best_path);
static Append *create_append_plan(Query *root, AppendPath *best_path);
static Result *create_result_plan(Query *root, ResultPath *best_path);
@@ -185,15 +186,41 @@ create_plan(Query *root, Path *best_path)
static Scan *
create_scan_plan(Query *root, Path *best_path)
{
- Scan *plan;
- List *tlist = best_path->parent->targetlist;
+ RelOptInfo *rel = best_path->parent;
+ List *tlist;
List *scan_clauses;
+ Scan *plan;
+
+ /*
+ * For table scans, rather than using the relation targetlist (which is
+ * only those Vars actually needed by the query), we prefer to generate a
+ * tlist containing all Vars in order. This will allow the executor to
+ * optimize away projection of the table tuples, if possible. (Note that
+ * planner.c may replace the tlist we generate here, forcing projection to
+ * occur.)
+ */
+ if (use_physical_tlist(rel))
+ {
+ int resdomno = 1;
+ List *v;
+
+ tlist = NIL;
+ foreach(v, rel->varlist)
+ {
+ Var *var = (Var *) lfirst(v);
+
+ tlist = lappend(tlist, create_tl_element(var, resdomno));
+ resdomno++;
+ }
+ }
+ else
+ tlist = rel->targetlist;
/*
* Extract the relevant restriction clauses from the parent relation;
* the executor must apply all these restrictions during the scan.
*/
- scan_clauses = get_actual_clauses(best_path->parent->baserestrictinfo);
+ scan_clauses = get_actual_clauses(rel->baserestrictinfo);
/* Sort clauses into best execution order */
scan_clauses = order_qual_clauses(root, scan_clauses);
@@ -242,6 +269,47 @@ create_scan_plan(Query *root, Path *best_path)
}
/*
+ * use_physical_tlist
+ * Decide whether to use a tlist matching relation structure,
+ * rather than only those Vars actually referenced.
+ */
+static bool
+use_physical_tlist(RelOptInfo *rel)
+{
+ List *t;
+
+ /*
+ * Currently, can't do this for subquery or function scans. (This
+ * is mainly because we don't set up the necessary info when creating
+ * their RelOptInfo nodes.)
+ */
+ if (rel->rtekind != RTE_RELATION)
+ return false;
+ /*
+ * Can't do it with inheritance cases either (mainly because Append
+ * doesn't project).
+ */
+ if (rel->reloptkind != RELOPT_BASEREL)
+ return false;
+ /*
+ * Can't do it if any system columns are requested, either. (This could
+ * possibly be fixed but would take some fragile assumptions in setrefs.c,
+ * I think.)
+ */
+ foreach(t, rel->targetlist)
+ {
+ TargetEntry *tle = (TargetEntry *) lfirst(t);
+ Var *var = (Var *) tle->expr;
+
+ if (!var || !IsA(var, Var))
+ return false; /* probably can't happen */
+ if (var->varattno <= 0)
+ return false; /* system column! */
+ }
+ return true;
+}
+
+/*
* create_join_plan
* Create a join plan for 'best_path' and (recursively) plans for its
* inner and outer paths.
@@ -399,7 +467,7 @@ create_material_plan(Query *root, MaterialPath *best_path)
subplan = create_plan(root, best_path->subpath);
- plan = make_material(best_path->path.parent->targetlist, subplan);
+ plan = make_material(subplan->targetlist, subplan);
copy_path_costsize(&plan->plan, (Path *) best_path);
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 1b481ccbd25..f01b9feffe2 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.142 2003/01/25 23:10:27 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.143 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -55,7 +55,11 @@ static Plan *inheritance_planner(Query *parse, List *inheritlist);
static Plan *grouping_planner(Query *parse, double tuple_fraction);
static bool hash_safe_grouping(Query *parse);
static List *make_subplanTargetList(Query *parse, List *tlist,
- AttrNumber **groupColIdx);
+ AttrNumber **groupColIdx, bool *need_tlist_eval);
+static void locate_grouping_columns(Query *parse,
+ List *tlist,
+ List *sub_tlist,
+ AttrNumber *groupColIdx);
static Plan *make_groupsortplan(Query *parse,
List *groupClause,
AttrNumber *grpColIdx,
@@ -530,6 +534,7 @@ grouping_planner(Query *parse, double tuple_fraction)
List *sub_tlist;
List *group_pathkeys;
AttrNumber *groupColIdx = NULL;
+ bool need_tlist_eval = true;
QualCost tlist_cost;
double sub_tuple_fraction;
Path *cheapest_path;
@@ -602,7 +607,8 @@ grouping_planner(Query *parse, double tuple_fraction)
* Generate appropriate target list for subplan; may be different
* from tlist if grouping or aggregation is needed.
*/
- sub_tlist = make_subplanTargetList(parse, tlist, &groupColIdx);
+ sub_tlist = make_subplanTargetList(parse, tlist,
+ &groupColIdx, &need_tlist_eval);
/*
* Calculate pathkeys that represent grouping/ordering
@@ -1003,45 +1009,65 @@ grouping_planner(Query *parse, double tuple_fraction)
/*
* create_plan() returns a plan with just a "flat" tlist of required
- * Vars. We want to insert the sub_tlist as the tlist of the top
- * plan node. If the top-level plan node is one that cannot do
- * expression evaluation, we must insert a Result node to project the
- * desired tlist.
- * Currently, the only plan node we might see here that falls into
- * that category is Append.
+ * Vars. Usually we need to insert the sub_tlist as the tlist of the
+ * top plan node. However, we can skip that if we determined that
+ * whatever query_planner chose to return will be good enough.
*/
- if (IsA(result_plan, Append))
+ if (need_tlist_eval)
{
- result_plan = (Plan *) make_result(sub_tlist, NULL, result_plan);
+ /*
+ * If the top-level plan node is one that cannot do expression
+ * evaluation, we must insert a Result node to project the desired
+ * tlist.
+ * Currently, the only plan node we might see here that falls into
+ * that category is Append.
+ */
+ if (IsA(result_plan, Append))
+ {
+ result_plan = (Plan *) make_result(sub_tlist, NULL,
+ result_plan);
+ }
+ else
+ {
+ /*
+ * Otherwise, just replace the subplan's flat tlist with
+ * the desired tlist.
+ */
+ result_plan->targetlist = sub_tlist;
+ }
+ /*
+ * Also, account for the cost of evaluation of the sub_tlist.
+ *
+ * Up to now, we have only been dealing with "flat" tlists,
+ * containing just Vars. So their evaluation cost is zero
+ * according to the model used by cost_qual_eval() (or if you
+ * prefer, the cost is factored into cpu_tuple_cost). Thus we can
+ * avoid accounting for tlist cost throughout query_planner() and
+ * subroutines. But now we've inserted a tlist that might contain
+ * actual operators, sub-selects, etc --- so we'd better account
+ * for its cost.
+ *
+ * Below this point, any tlist eval cost for added-on nodes should
+ * be accounted for as we create those nodes. Presently, of the
+ * node types we can add on, only Agg and Group project new tlists
+ * (the rest just copy their input tuples) --- so make_agg() and
+ * make_group() are responsible for computing the added cost.
+ */
+ cost_qual_eval(&tlist_cost, sub_tlist);
+ result_plan->startup_cost += tlist_cost.startup;
+ result_plan->total_cost += tlist_cost.startup +
+ tlist_cost.per_tuple * result_plan->plan_rows;
}
else
{
/*
- * Otherwise, just replace the flat tlist with the desired tlist.
+ * Since we're using query_planner's tlist and not the one
+ * make_subplanTargetList calculated, we have to refigure
+ * any grouping-column indexes make_subplanTargetList computed.
*/
- result_plan->targetlist = sub_tlist;
+ locate_grouping_columns(parse, tlist, result_plan->targetlist,
+ groupColIdx);
}
- /*
- * Also, account for the cost of evaluation of the sub_tlist.
- *
- * Up to now, we have only been dealing with "flat" tlists, containing
- * just Vars. So their evaluation cost is zero according to the
- * model used by cost_qual_eval() (or if you prefer, the cost is
- * factored into cpu_tuple_cost). Thus we can avoid accounting for
- * tlist cost throughout query_planner() and subroutines.
- * But now we've inserted a tlist that might contain actual operators,
- * sub-selects, etc --- so we'd better account for its cost.
- *
- * Below this point, any tlist eval cost for added-on nodes should
- * be accounted for as we create those nodes. Presently, of the
- * node types we can add on, only Agg and Group project new tlists
- * (the rest just copy their input tuples) --- so make_agg() and
- * make_group() are responsible for computing the added cost.
- */
- cost_qual_eval(&tlist_cost, sub_tlist);
- result_plan->startup_cost += tlist_cost.startup;
- result_plan->total_cost += tlist_cost.startup +
- tlist_cost.per_tuple * result_plan->plan_rows;
/*
* Insert AGG or GROUP node if needed, plus an explicit sort step
@@ -1245,10 +1271,17 @@ hash_safe_grouping(Query *parse)
* the extra computation to recompute a+b at the outer level; see
* replace_vars_with_subplan_refs() in setrefs.c.)
*
+ * If we are grouping or aggregating, *and* there are no non-Var grouping
+ * expressions, then the returned tlist is effectively dummy; we do not
+ * need to force it to be evaluated, because all the Vars it contains
+ * should be present in the output of query_planner anyway.
+ *
* 'parse' is the query being processed.
* 'tlist' is the query's target list.
* 'groupColIdx' receives an array of column numbers for the GROUP BY
- * expressions (if there are any) in the subplan's target list.
+ * expressions (if there are any) in the subplan's target list.
+ * 'need_tlist_eval' is set true if we really need to evaluate the
+ * result tlist.
*
* The result is the targetlist to be passed to the subplan.
*---------------
@@ -1256,7 +1289,8 @@ hash_safe_grouping(Query *parse)
static List *
make_subplanTargetList(Query *parse,
List *tlist,
- AttrNumber **groupColIdx)
+ AttrNumber **groupColIdx,
+ bool *need_tlist_eval)
{
List *sub_tlist;
List *extravars;
@@ -1269,7 +1303,10 @@ make_subplanTargetList(Query *parse,
* query_planner should receive the unmodified target list.
*/
if (!parse->hasAggs && !parse->groupClause && !parse->havingQual)
+ {
+ *need_tlist_eval = true;
return tlist;
+ }
/*
* Otherwise, start with a "flattened" tlist (having just the vars
@@ -1280,6 +1317,7 @@ make_subplanTargetList(Query *parse,
extravars = pull_var_clause(parse->havingQual, false);
sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
freeList(extravars);
+ *need_tlist_eval = false; /* only eval if not flat tlist */
/*
* If grouping, create sub_tlist entries for all GROUP BY expressions
@@ -1320,6 +1358,7 @@ make_subplanTargetList(Query *parse,
false),
(Expr *) groupexpr);
sub_tlist = lappend(sub_tlist, te);
+ *need_tlist_eval = true; /* it's not flat anymore */
}
/* and save its resno */
@@ -1331,6 +1370,53 @@ make_subplanTargetList(Query *parse,
}
/*
+ * locate_grouping_columns
+ * Locate grouping columns in the tlist chosen by query_planner.
+ *
+ * This is only needed if we don't use the sub_tlist chosen by
+ * make_subplanTargetList. We have to forget the column indexes found
+ * by that routine and re-locate the grouping vars in the real sub_tlist.
+ */
+static void
+locate_grouping_columns(Query *parse,
+ List *tlist,
+ List *sub_tlist,
+ AttrNumber *groupColIdx)
+{
+ int keyno = 0;
+ List *gl;
+
+ /*
+ * No work unless grouping.
+ */
+ if (!parse->groupClause)
+ {
+ Assert(groupColIdx == NULL);
+ return;
+ }
+ Assert(groupColIdx != NULL);
+
+ foreach(gl, parse->groupClause)
+ {
+ GroupClause *grpcl = (GroupClause *) lfirst(gl);
+ Node *groupexpr = get_sortgroupclause_expr(grpcl, tlist);
+ TargetEntry *te = NULL;
+ List *sl;
+
+ foreach(sl, sub_tlist)
+ {
+ te = (TargetEntry *) lfirst(sl);
+ if (equal(groupexpr, te->expr))
+ break;
+ }
+ if (!sl)
+ elog(ERROR, "locate_grouping_columns: failed");
+
+ groupColIdx[keyno++] = te->resdom->resno;
+ }
+}
+
+/*
* make_groupsortplan
* Add a Sort node to explicitly sort according to the GROUP BY clause.
*
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 4a9f63312c3..3e0bbe69225 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.76 2003/01/28 22:13:35 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.77 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -23,6 +23,7 @@
#include "catalog/pg_amop.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_index.h"
+#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/plancat.h"
#include "parser/parsetree.h"
@@ -38,155 +39,160 @@
/*
* get_relation_info -
* Retrieves catalog information for a given relation.
- * Given the Oid of the relation, return the following info:
- * whether the relation has secondary indices
- * number of pages
- * number of tuples
- */
-void
-get_relation_info(Oid relationObjectId,
- bool *hasindex, long *pages, double *tuples)
-{
- HeapTuple relationTuple;
- Form_pg_class relation;
-
- relationTuple = SearchSysCache(RELOID,
- ObjectIdGetDatum(relationObjectId),
- 0, 0, 0);
- if (!HeapTupleIsValid(relationTuple))
- elog(ERROR, "get_relation_info: Relation %u not found",
- relationObjectId);
- relation = (Form_pg_class) GETSTRUCT(relationTuple);
-
- if (IsIgnoringSystemIndexes() && IsSystemClass(relation))
- *hasindex = false;
- else
- *hasindex = relation->relhasindex;
-
- *pages = relation->relpages;
- *tuples = relation->reltuples;
-
- ReleaseSysCache(relationTuple);
-}
-
-/*
- * find_secondary_indexes
- * Creates a list of IndexOptInfo nodes containing information for each
- * secondary index defined on the specified relation.
*
- * 'relationObjectId' is the OID of the relation for which indices are wanted
+ * Given the Oid of the relation, return the following info into fields
+ * of the RelOptInfo struct:
*
- * Returns a list of new IndexOptInfo nodes.
+ * varlist list of physical columns (expressed as Vars)
+ * indexlist list of IndexOptInfos for relation's indexes
+ * pages number of pages
+ * tuples number of tuples
*/
-List *
-find_secondary_indexes(Oid relationObjectId)
+void
+get_relation_info(Oid relationObjectId, RelOptInfo *rel)
{
- List *indexinfos = NIL;
- List *indexoidlist,
- *indexoidscan;
Relation relation;
+ Index varno = lfirsti(rel->relids);
+ bool hasindex;
+ List *varlist = NIL;
+ List *indexinfos = NIL;
+ int attrno,
+ numattrs;
+
+ relation = heap_open(relationObjectId, AccessShareLock);
/*
- * We used to scan pg_index directly, but now the relcache offers a
- * cached list of OID indexes for each relation. So, get that list
- * and then use the syscache to obtain pg_index entries.
+ * Make list of physical Vars. Note we do NOT ignore dropped columns;
+ * the intent is to model the physical tuples of the relation.
*/
- relation = heap_open(relationObjectId, AccessShareLock);
- indexoidlist = RelationGetIndexList(relation);
+ numattrs = RelationGetNumberOfAttributes(relation);
- foreach(indexoidscan, indexoidlist)
+ for (attrno = 1; attrno <= numattrs; attrno++)
{
- Oid indexoid = lfirsti(indexoidscan);
- Relation indexRelation;
- Form_pg_index index;
- IndexOptInfo *info;
- int i;
- int16 amorderstrategy;
-
- /* Extract info from the relation descriptor for the index */
- indexRelation = index_open(indexoid);
+ Form_pg_attribute att_tup = relation->rd_att->attrs[attrno - 1];
+
+ varlist = lappend(varlist,
+ makeVar(varno,
+ attrno,
+ att_tup->atttypid,
+ att_tup->atttypmod,
+ 0));
+ }
- info = makeNode(IndexOptInfo);
+ rel->varlist = varlist;
- /*
- * Need to make these arrays large enough to be sure there is room
- * for a terminating 0 at the end of each one.
- */
- info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
- info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
- info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
-
- /* Extract info from the pg_index tuple */
- index = indexRelation->rd_index;
- info->indexoid = index->indexrelid;
- info->indproc = index->indproc; /* functional index ?? */
- if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */
- {
- char *predString;
+ /*
+ * Make list of indexes. Ignore indexes on system catalogs if told to.
+ */
+ if (IsIgnoringSystemIndexes() && IsSystemClass(relation->rd_rel))
+ hasindex = false;
+ else
+ hasindex = relation->rd_rel->relhasindex;
- predString = DatumGetCString(DirectFunctionCall1(textout,
- PointerGetDatum(&index->indpred)));
- info->indpred = (List *) stringToNode(predString);
- pfree(predString);
- }
- else
- info->indpred = NIL;
- info->unique = index->indisunique;
+ if (hasindex)
+ {
+ List *indexoidlist,
+ *indexoidscan;
- for (i = 0; i < INDEX_MAX_KEYS; i++)
- {
- if (index->indclass[i] == (Oid) 0)
- break;
- info->classlist[i] = index->indclass[i];
- }
- info->classlist[i] = (Oid) 0;
- info->ncolumns = i;
+ indexoidlist = RelationGetIndexList(relation);
- for (i = 0; i < INDEX_MAX_KEYS; i++)
+ foreach(indexoidscan, indexoidlist)
{
- if (index->indkey[i] == 0)
- break;
- info->indexkeys[i] = index->indkey[i];
- }
- info->indexkeys[i] = 0;
- info->nkeys = i;
+ Oid indexoid = lfirsti(indexoidscan);
+ Relation indexRelation;
+ Form_pg_index index;
+ IndexOptInfo *info;
+ int i;
+ int16 amorderstrategy;
+
+ /* Extract info from the relation descriptor for the index */
+ indexRelation = index_open(indexoid);
+
+ info = makeNode(IndexOptInfo);
+
+ /*
+ * Need to make these arrays large enough to be sure there is room
+ * for a terminating 0 at the end of each one.
+ */
+ info->classlist = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
+ info->indexkeys = (int *) palloc(sizeof(int) * (INDEX_MAX_KEYS + 1));
+ info->ordering = (Oid *) palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
+
+ /* Extract info from the pg_index tuple */
+ index = indexRelation->rd_index;
+ info->indexoid = index->indexrelid;
+ info->indproc = index->indproc; /* functional index ?? */
+ if (VARSIZE(&index->indpred) > VARHDRSZ) /* partial index ?? */
+ {
+ char *predString;
- info->relam = indexRelation->rd_rel->relam;
- info->pages = indexRelation->rd_rel->relpages;
- info->tuples = indexRelation->rd_rel->reltuples;
- info->amcostestimate = index_cost_estimator(indexRelation);
- amorderstrategy = indexRelation->rd_am->amorderstrategy;
+ predString = DatumGetCString(DirectFunctionCall1(textout,
+ PointerGetDatum(&index->indpred)));
+ info->indpred = (List *) stringToNode(predString);
+ pfree(predString);
+ }
+ else
+ info->indpred = NIL;
+ info->unique = index->indisunique;
- /*
- * Fetch the ordering operators associated with the index, if any.
- */
- MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
- if (amorderstrategy != 0)
- {
- int oprindex = amorderstrategy - 1;
+ for (i = 0; i < INDEX_MAX_KEYS; i++)
+ {
+ if (index->indclass[i] == (Oid) 0)
+ break;
+ info->classlist[i] = index->indclass[i];
+ }
+ info->classlist[i] = (Oid) 0;
+ info->ncolumns = i;
- for (i = 0; i < info->ncolumns; i++)
+ for (i = 0; i < INDEX_MAX_KEYS; i++)
{
- info->ordering[i] = indexRelation->rd_operator[oprindex];
- oprindex += indexRelation->rd_am->amstrategies;
+ if (index->indkey[i] == 0)
+ break;
+ info->indexkeys[i] = index->indkey[i];
}
- }
+ info->indexkeys[i] = 0;
+ info->nkeys = i;
+
+ info->relam = indexRelation->rd_rel->relam;
+ info->pages = indexRelation->rd_rel->relpages;
+ info->tuples = indexRelation->rd_rel->reltuples;
+ info->amcostestimate = index_cost_estimator(indexRelation);
+ amorderstrategy = indexRelation->rd_am->amorderstrategy;
+
+ /*
+ * Fetch the ordering operators associated with the index, if any.
+ */
+ MemSet(info->ordering, 0, sizeof(Oid) * (INDEX_MAX_KEYS + 1));
+ if (amorderstrategy != 0)
+ {
+ int oprindex = amorderstrategy - 1;
- /* initialize cached join info to empty */
- info->outer_relids = NIL;
- info->inner_paths = NIL;
+ for (i = 0; i < info->ncolumns; i++)
+ {
+ info->ordering[i] = indexRelation->rd_operator[oprindex];
+ oprindex += indexRelation->rd_am->amstrategies;
+ }
+ }
- index_close(indexRelation);
+ /* initialize cached join info to empty */
+ info->outer_relids = NIL;
+ info->inner_paths = NIL;
- indexinfos = lcons(info, indexinfos);
+ index_close(indexRelation);
+
+ indexinfos = lcons(info, indexinfos);
+ }
+
+ freeList(indexoidlist);
}
- freeList(indexoidlist);
+ rel->indexlist = indexinfos;
+
+ rel->pages = relation->rd_rel->relpages;
+ rel->tuples = relation->rd_rel->reltuples;
/* XXX keep the lock here? */
heap_close(relation, AccessShareLock);
-
- return indexinfos;
}
/*
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 06a73bf4e9e..ebfaa4924d4 100644
--- a/src/backend/optimizer/util/relnode.c
+++ b/src/backend/optimizer/util/relnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.45 2003/01/24 03:58:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.46 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -143,6 +143,7 @@ make_base_rel(Query *root, int relid)
rel->cheapest_unique_path = NULL;
rel->pruneable = true;
rel->rtekind = rte->rtekind;
+ rel->varlist = NIL;
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
@@ -159,16 +160,9 @@ make_base_rel(Query *root, int relid)
switch (rte->rtekind)
{
case RTE_RELATION:
- {
- /* Table --- retrieve statistics from the system catalogs */
- bool indexed;
-
- get_relation_info(rte->relid,
- &indexed, &rel->pages, &rel->tuples);
- if (indexed)
- rel->indexlist = find_secondary_indexes(rte->relid);
- break;
- }
+ /* Table --- retrieve statistics from the system catalogs */
+ get_relation_info(rte->relid, rel);
+ break;
case RTE_SUBQUERY:
case RTE_FUNCTION:
/* Subquery or function --- nothing to do here */
@@ -304,6 +298,7 @@ build_join_rel(Query *root,
joinrel->cheapest_unique_path = NULL;
joinrel->pruneable = true;
joinrel->rtekind = RTE_JOIN;
+ joinrel->varlist = NIL;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index cd462ac27a0..2f329ca57af 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: executor.h,v 1.87 2003/01/12 04:03:34 tgl Exp $
+ * $Id: executor.h,v 1.88 2003/02/03 15:07:07 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -137,6 +137,7 @@ extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
typedef TupleTableSlot *(*ExecScanAccessMtd) (ScanState *node);
extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd);
+extern void ExecAssignScanProjectionInfo(ScanState *node);
/*
* prototypes from functions in execTuples.c
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index c2d8970234f..807c70073de 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: relation.h,v 1.77 2003/01/20 18:55:04 tgl Exp $
+ * $Id: relation.h,v 1.78 2003/02/03 15:07:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -105,6 +105,7 @@ typedef struct QualCost
* If the relation is a base relation it will have these fields set:
*
* rtekind - distinguishes plain relation, subquery, or function RTE
+ * varlist - list of Vars for physical columns (only if table)
* indexlist - list of IndexOptInfo nodes for relation's indexes
* (always NIL if it's not a table)
* pages - number of disk pages in relation (zero if not a table)
@@ -190,6 +191,7 @@ typedef struct RelOptInfo
/* information about a base rel (not set for join rels!) */
RTEKind rtekind; /* RELATION, SUBQUERY, or FUNCTION */
+ List *varlist;
List *indexlist;
long pages;
double tuples;
diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h
index 255d196d7d7..c6b18ab0da9 100644
--- a/src/include/optimizer/plancat.h
+++ b/src/include/optimizer/plancat.h
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: plancat.h,v 1.28 2003/01/28 22:13:41 tgl Exp $
+ * $Id: plancat.h,v 1.29 2003/02/03 15:07:08 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -17,10 +17,7 @@
#include "nodes/relation.h"
-extern void get_relation_info(Oid relationObjectId,
- bool *hasindex, long *pages, double *tuples);
-
-extern List *find_secondary_indexes(Oid relationObjectId);
+extern void get_relation_info(Oid relationObjectId, RelOptInfo *rel);
extern List *find_inheritance_children(Oid inhparent);