mirror of
https://github.com/postgres/postgres.git
synced 2025-11-07 19:06:32 +03:00
First pass at set-returning-functions in FROM, by Joe Conway with
some kibitzing from Tom Lane. Not everything works yet, and there's no documentation or regression test, but let's commit this so Joe doesn't need to cope with tracking changes in so many files ...
This commit is contained in:
@@ -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)
|
||||
{
|
||||
@@ -385,6 +392,23 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
||||
set_cheapest(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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -386,6 +391,36 @@ cost_tidscan(Path *path, Query *root,
|
||||
path->total_cost = startup_cost + run_cost;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -1298,6 +1333,54 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
|
||||
rel->width = outer_rel->width + inner_rel->width;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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 $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -454,6 +454,25 @@ create_subqueryscan_path(RelOptInfo *rel)
|
||||
return pathnode;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user