aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r--src/backend/optimizer/path/allpaths.c28
-rw-r--r--src/backend/optimizer/path/clausesel.c4
-rw-r--r--src/backend/optimizer/path/costsize.c91
-rw-r--r--src/backend/optimizer/plan/createplan.c67
-rw-r--r--src/backend/optimizer/plan/setrefs.c6
-rw-r--r--src/backend/optimizer/plan/subselect.c9
-rw-r--r--src/backend/optimizer/util/clauses.c4
-rw-r--r--src/backend/optimizer/util/pathnode.c21
-rw-r--r--src/backend/optimizer/util/relnode.c10
9 files changed, 215 insertions, 25 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 9368723188f..1e9074186c2 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.83 2001/12/10 22:54:12 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -42,6 +42,8 @@ static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
List *inheritlist);
static void set_subquery_pathlist(Query *root, RelOptInfo *rel,
Index rti, RangeTblEntry *rte);
+static void set_function_pathlist(Query *root, RelOptInfo *rel,
+ RangeTblEntry *rte);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
@@ -98,11 +100,16 @@ set_base_rel_pathlists(Query *root)
rti = lfirsti(rel->relids);
rte = rt_fetch(rti, root->rtable);
- if (rel->issubquery)
+ if (rel->rtekind == RTE_SUBQUERY)
{
/* Subquery --- generate a separate plan for it */
set_subquery_pathlist(root, rel, rti, rte);
}
+ else if (rel->rtekind == RTE_FUNCTION)
+ {
+ /* RangeFunction --- generate a separate plan for it */
+ set_function_pathlist(root, rel, rte);
+ }
else if ((inheritlist = expand_inherted_rtentry(root, rti, true))
!= NIL)
{
@@ -386,6 +393,23 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
}
/*
+ * set_function_pathlist
+ * Build the (single) access path for a function RTE
+ */
+static void
+set_function_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
+{
+ /* Mark rel with estimated output rows, width, etc */
+ set_function_size_estimates(root, rel);
+
+ /* Generate appropriate path */
+ add_path(rel, create_functionscan_path(root, rel));
+
+ /* Select cheapest path (pretty easy in this case...) */
+ set_cheapest(rel);
+}
+
+/*
* make_fromexpr_rel
* Build access paths for a FromExpr jointree node.
*/
diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c
index 85584bfdcfc..35a98031004 100644
--- a/src/backend/optimizer/path/clausesel.c
+++ b/src/backend/optimizer/path/clausesel.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.49 2002/03/06 06:09:50 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.50 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -381,7 +381,7 @@ clause_selectivity(Query *root,
{
RangeTblEntry *rte = rt_fetch(var->varno, root->rtable);
- if (rte->subquery)
+ if (rte->rtekind == RTE_SUBQUERY)
{
/*
* XXX not smart about subquery references... any way to
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
index 2bc0bb8a137..b14322f45b5 100644
--- a/src/backend/optimizer/path/costsize.c
+++ b/src/backend/optimizer/path/costsize.c
@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.83 2002/03/12 00:51:42 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.84 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -111,7 +111,7 @@ cost_seqscan(Path *path, Query *root,
/* Should only be applied to base relations */
Assert(length(baserel->relids) == 1);
- Assert(!baserel->issubquery);
+ Assert(baserel->rtekind == RTE_RELATION);
if (!enable_seqscan)
startup_cost += disable_cost;
@@ -224,9 +224,10 @@ cost_index(Path *path, Query *root,
b;
/* Should only be applied to base relations */
- Assert(IsA(baserel, RelOptInfo) &&IsA(index, IndexOptInfo));
+ Assert(IsA(baserel, RelOptInfo) &&
+ IsA(index, IndexOptInfo));
Assert(length(baserel->relids) == 1);
- Assert(!baserel->issubquery);
+ Assert(baserel->rtekind == RTE_RELATION);
if (!enable_indexscan && !is_injoin)
startup_cost += disable_cost;
@@ -372,6 +373,10 @@ cost_tidscan(Path *path, Query *root,
Cost cpu_per_tuple;
int ntuples = length(tideval);
+ /* Should only be applied to base relations */
+ Assert(length(baserel->relids) == 1);
+ Assert(baserel->rtekind == RTE_RELATION);
+
if (!enable_tidscan)
startup_cost += disable_cost;
@@ -387,6 +392,36 @@ cost_tidscan(Path *path, Query *root,
}
/*
+ * cost_functionscan
+ * Determines and returns the cost of scanning a function RTE.
+ */
+void
+cost_functionscan(Path *path, Query *root, RelOptInfo *baserel)
+{
+ Cost startup_cost = 0;
+ Cost run_cost = 0;
+ Cost cpu_per_tuple;
+
+ /* Should only be applied to base relations that are functions */
+ Assert(length(baserel->relids) == 1);
+ Assert(baserel->rtekind == RTE_FUNCTION);
+
+ /*
+ * For now, estimate function's cost at one operator eval per function
+ * call. Someday we should revive the function cost estimate columns in
+ * pg_proc...
+ */
+ cpu_per_tuple = cpu_operator_cost;
+
+ /* Add scanning CPU costs */
+ cpu_per_tuple += cpu_tuple_cost + baserel->baserestrictcost;
+ run_cost += cpu_per_tuple * baserel->tuples;
+
+ path->startup_cost = startup_cost;
+ path->total_cost = startup_cost + run_cost;
+}
+
+/*
* cost_sort
* Determines and returns the cost of sorting a relation.
*
@@ -1299,6 +1334,54 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
}
/*
+ * set_function_size_estimates
+ * Set the size estimates for a base relation that is a function call.
+ *
+ * The rel's targetlist and restrictinfo list must have been constructed
+ * already.
+ *
+ * We set the following fields of the rel node:
+ * rows: the estimated number of output tuples (after applying
+ * restriction clauses).
+ * width: the estimated average output tuple width in bytes.
+ * baserestrictcost: estimated cost of evaluating baserestrictinfo clauses.
+ */
+void
+set_function_size_estimates(Query *root, RelOptInfo *rel)
+{
+ /* Should only be applied to base relations that are functions */
+ Assert(length(rel->relids) == 1);
+ Assert(rel->rtekind == RTE_FUNCTION);
+
+ /*
+ * Estimate number of rows the function itself will return.
+ *
+ * XXX no idea how to do this yet; but should at least check whether
+ * function returns set or not...
+ */
+ rel->tuples = 1000;
+
+ /* Now estimate number of output rows */
+ rel->rows = rel->tuples *
+ restrictlist_selectivity(root,
+ rel->baserestrictinfo,
+ lfirsti(rel->relids));
+
+ /*
+ * Force estimate to be at least one row, to make explain output look
+ * better and to avoid possible divide-by-zero when interpolating
+ * cost.
+ */
+ if (rel->rows < 1.0)
+ rel->rows = 1.0;
+
+ rel->baserestrictcost = cost_qual_eval(rel->baserestrictinfo);
+
+ set_rel_width(root, rel);
+}
+
+
+/*
* set_rel_width
* Set the estimated output width of the relation.
*
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index c20abacca39..cd8b0e6612f 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.113 2002/04/28 19:54:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.114 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -43,6 +43,8 @@ static TidScan *create_tidscan_plan(TidPath *best_path, List *tlist,
List *scan_clauses);
static SubqueryScan *create_subqueryscan_plan(Path *best_path,
List *tlist, List *scan_clauses);
+static FunctionScan *create_functionscan_plan(Path *best_path,
+ List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(Query *root,
NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
@@ -77,6 +79,8 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval);
+static FunctionScan *make_functionscan(List *qptlist, List *qpqual,
+ Index scanrelid);
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
@@ -119,6 +123,7 @@ create_plan(Query *root, Path *best_path)
case T_SeqScan:
case T_TidScan:
case T_SubqueryScan:
+ case T_FunctionScan:
plan = (Plan *) create_scan_plan(root, best_path);
break;
case T_HashJoin:
@@ -200,6 +205,12 @@ create_scan_plan(Query *root, Path *best_path)
scan_clauses);
break;
+ case T_FunctionScan:
+ plan = (Scan *) create_functionscan_plan(best_path,
+ tlist,
+ scan_clauses);
+ break;
+
default:
elog(ERROR, "create_scan_plan: unknown node type: %d",
best_path->pathtype);
@@ -353,7 +364,7 @@ create_seqscan_plan(Path *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->parent->relids) == 1);
- Assert(!best_path->parent->issubquery);
+ Assert(best_path->parent->rtekind == RTE_RELATION);
scan_relid = (Index) lfirsti(best_path->parent->relids);
@@ -397,7 +408,7 @@ create_indexscan_plan(Query *root,
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
- Assert(!best_path->path.parent->issubquery);
+ Assert(best_path->path.parent->rtekind == RTE_RELATION);
baserelid = lfirsti(best_path->path.parent->relids);
@@ -515,7 +526,7 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->path.parent->relids) == 1);
- Assert(!best_path->path.parent->issubquery);
+ Assert(best_path->path.parent->rtekind == RTE_RELATION);
scan_relid = (Index) lfirsti(best_path->path.parent->relids);
@@ -546,7 +557,7 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
/* there should be exactly one base rel involved... */
Assert(length(best_path->parent->relids) == 1);
/* and it must be a subquery */
- Assert(best_path->parent->issubquery);
+ Assert(best_path->parent->rtekind == RTE_SUBQUERY);
scan_relid = (Index) lfirsti(best_path->parent->relids);
@@ -558,6 +569,31 @@ create_subqueryscan_plan(Path *best_path, List *tlist, List *scan_clauses)
return scan_plan;
}
+/*
+ * create_functionscan_plan
+ * Returns a functionscan plan for the base relation scanned by 'best_path'
+ * with restriction clauses 'scan_clauses' and targetlist 'tlist'.
+ */
+static FunctionScan *
+create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses)
+{
+ FunctionScan *scan_plan;
+ Index scan_relid;
+
+ /* there should be exactly one base rel involved... */
+ Assert(length(best_path->parent->relids) == 1);
+ /* and it must be a function */
+ Assert(best_path->parent->rtekind == RTE_FUNCTION);
+
+ scan_relid = (Index) lfirsti(best_path->parent->relids);
+
+ scan_plan = make_functionscan(tlist, scan_clauses, scan_relid);
+
+ copy_path_costsize(&scan_plan->scan.plan, best_path);
+
+ return scan_plan;
+}
+
/*****************************************************************************
*
* JOIN METHODS
@@ -791,6 +827,7 @@ create_mergejoin_plan(Query *root,
{
case T_SeqScan:
case T_IndexScan:
+ case T_FunctionScan:
case T_Material:
case T_Sort:
/* OK, these inner plans support mark/restore */
@@ -1305,6 +1342,26 @@ make_subqueryscan(List *qptlist,
return node;
}
+static FunctionScan *
+make_functionscan(List *qptlist,
+ List *qpqual,
+ Index scanrelid)
+{
+ FunctionScan *node = makeNode(FunctionScan);
+ Plan *plan = &node->scan.plan;
+
+ /* cost should be inserted by caller */
+ plan->state = (EState *) NULL;
+ plan->targetlist = qptlist;
+ plan->qual = qpqual;
+ plan->lefttree = NULL;
+ plan->righttree = NULL;
+ node->scan.scanrelid = scanrelid;
+ node->scan.scanstate = (CommonScanState *) NULL;
+
+ return node;
+}
+
Append *
make_append(List *appendplans, bool isTarget, List *tlist)
{
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 807876e4a72..3e07b2425e5 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.75 2002/04/28 19:54:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.76 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -120,6 +120,10 @@ set_plan_references(Query *root, Plan *plan)
/* Recurse into subplan too */
set_plan_references(root, ((SubqueryScan *) plan)->subplan);
break;
+ case T_FunctionScan:
+ fix_expr_references(plan, (Node *) plan->targetlist);
+ fix_expr_references(plan, (Node *) plan->qual);
+ break;
case T_NestLoop:
set_join_references(root, (Join *) plan);
fix_expr_references(plan, (Node *) plan->targetlist);
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index a67eb5d8e54..57930e9a502 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.51 2002/04/16 23:08:11 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.52 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -352,13 +352,13 @@ make_subplan(SubLink *slink)
}
break;
case T_Material:
+ case T_FunctionScan:
case T_Sort:
/*
* Don't add another Material node if there's one
- * already, nor if the top node is a Sort, since Sort
- * materializes its output anyway. (I doubt either
- * case can happen in practice for a subplan, but...)
+ * already, nor if the top node is any other type that
+ * materializes its output anyway.
*/
use_material = false;
break;
@@ -686,6 +686,7 @@ SS_finalize_plan(Plan *plan)
case T_SetOp:
case T_Limit:
case T_Group:
+ case T_FunctionScan:
break;
default:
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 1b7cc14e2e7..1a278e9ca2e 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.97 2002/04/28 19:54:28 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.98 2002/05/12 20:10:03 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -1922,6 +1922,7 @@ query_tree_walker(Query *query,
{
case RTE_RELATION:
case RTE_SPECIAL:
+ case RTE_FUNCTION:
/* nothing to do */
break;
case RTE_SUBQUERY:
@@ -2309,6 +2310,7 @@ query_tree_mutator(Query *query,
{
case RTE_RELATION:
case RTE_SPECIAL:
+ case RTE_FUNCTION:
/* nothing to do, don't bother to make a copy */
break;
case RTE_SUBQUERY:
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index c86ee450082..07d05b38a87 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.76 2001/10/25 05:49:34 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.77 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -455,6 +455,25 @@ create_subqueryscan_path(RelOptInfo *rel)
}
/*
+ * create_functionscan_path
+ * Creates a path corresponding to a sequential scan of a function,
+ * returning the pathnode.
+ */
+Path *
+create_functionscan_path(Query *root, RelOptInfo *rel)
+{
+ Path *pathnode = makeNode(Path);
+
+ pathnode->pathtype = T_FunctionScan;
+ pathnode->parent = rel;
+ pathnode->pathkeys = NIL; /* for now, assume unordered result */
+
+ cost_functionscan(pathnode, root, rel);
+
+ return pathnode;
+}
+
+/*
* create_nestloop_path
* Creates a pathnode corresponding to a nestloop join between two
* relations.
diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c
index 0fad16fbdd0..38e988b26fc 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.36 2002/03/12 00:51:51 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.37 2002/05/12 20:10:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -140,7 +140,7 @@ make_base_rel(Query *root, int relid)
rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
rel->pruneable = true;
- rel->issubquery = false;
+ rel->rtekind = rte->rtekind;
rel->indexlist = NIL;
rel->pages = 0;
rel->tuples = 0;
@@ -168,8 +168,8 @@ make_base_rel(Query *root, int relid)
break;
}
case RTE_SUBQUERY:
- /* Subquery --- mark it as such for later processing */
- rel->issubquery = true;
+ case RTE_FUNCTION:
+ /* Subquery or function --- nothing to do here */
break;
case RTE_JOIN:
/* Join --- must be an otherrel */
@@ -351,7 +351,7 @@ build_join_rel(Query *root,
joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
joinrel->pruneable = true;
- joinrel->issubquery = false;
+ joinrel->rtekind = RTE_JOIN;
joinrel->indexlist = NIL;
joinrel->pages = 0;
joinrel->tuples = 0;