1
0
mirror of https://github.com/postgres/postgres.git synced 2025-05-03 22:24:49 +03:00

1259 lines
32 KiB
C

/*-------------------------------------------------------------------------
*
* createplan.c
* Routines to create the desired plan for processing a query
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.62 1999/07/17 20:17:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include "postgres.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/internal.h"
#include "optimizer/planmain.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#define NONAME_SORT 1
#define NONAME_MATERIAL 2
static List *switch_outer(List *clauses);
static Oid *generate_merge_input_sortorder(List *pathkeys,
MergeOrder *mergeorder);
static Scan *create_scan_node(Path *best_path, List *tlist);
static Join *create_join_node(JoinPath *best_path, List *tlist);
static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
List *scan_clauses);
static IndexScan *create_indexscan_node(IndexPath *best_path, List *tlist,
List *scan_clauses);
static NestLoop *create_nestloop_node(NestPath *best_path, List *tlist,
List *clauses, Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
static MergeJoin *create_mergejoin_node(MergePath *best_path, List *tlist,
List *clauses, Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
static HashJoin *create_hashjoin_node(HashPath *best_path, List *tlist,
List *clauses, Plan *outer_node, List *outer_tlist,
Plan *inner_node, List *inner_tlist);
static Node *fix_indxqual_references(Node *clause, Path *index_path);
static Noname *make_noname(List *tlist, List *pathkeys, Oid *operators,
Plan *plan_node, int nonametype);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual, List *indxqualorig);
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
Plan *righttree);
static HashJoin *make_hashjoin(List *tlist, List *qpqual,
List *hashclauses, Plan *lefttree, Plan *righttree);
static Hash *make_hash(List *tlist, Var *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist, List *qpqual,
List *mergeclauses, Plan *righttree, Plan *lefttree);
static Material *make_material(List *tlist, Oid nonameid, Plan *lefttree,
int keycount);
static void copy_costsize(Plan *dest, Plan *src);
/*
* create_plan
* Creates the access plan for a query by tracing backwards through the
* desired chain of pathnodes, starting at the node 'best_path'. For
* every pathnode found:
* (1) Create a corresponding plan node containing appropriate id,
* target list, and qualification information.
* (2) Modify ALL clauses so that attributes are referenced using
* relative values.
* (3) Target lists are not modified, but will be in another routine.
*
* best_path is the best access path
*
* Returns the optimal(?) access plan.
*/
Plan *
create_plan(Path *best_path)
{
List *tlist;
Plan *plan_node = (Plan *) NULL;
RelOptInfo *parent_rel;
int size;
int width;
int pages;
int tuples;
parent_rel = best_path->parent;
tlist = get_actual_tlist(parent_rel->targetlist);
size = parent_rel->size;
width = parent_rel->width;
pages = parent_rel->pages;
tuples = parent_rel->tuples;
switch (best_path->pathtype)
{
case T_IndexScan:
case T_SeqScan:
plan_node = (Plan *) create_scan_node(best_path, tlist);
break;
case T_HashJoin:
case T_MergeJoin:
case T_NestLoop:
plan_node = (Plan *) create_join_node((JoinPath *) best_path, tlist);
break;
default:
/* do nothing */
break;
}
plan_node->plan_size = size;
plan_node->plan_width = width;
if (pages == 0)
pages = 1;
plan_node->plan_tupperpage = tuples / pages;
#ifdef NOT_USED /* fix xfunc */
/* sort clauses by cost/(1-selectivity) -- JMH 2/26/92 */
if (XfuncMode != XFUNC_OFF)
{
set_qpqual((Plan) plan_node,
lisp_qsort(get_qpqual((Plan) plan_node),
xfunc_clause_compare));
if (XfuncMode != XFUNC_NOR)
/* sort the disjuncts within each clause by cost -- JMH 3/4/92 */
xfunc_disjunct_sort(plan_node->qpqual);
}
#endif
return plan_node;
}
/*
* create_scan_node
* Create a scan path for the parent relation of 'best_path'.
*
* tlist is the targetlist for the base relation scanned by 'best_path'
*
* Returns the scan node.
*/
static Scan *
create_scan_node(Path *best_path, List *tlist)
{
Scan *node = NULL;
List *scan_clauses;
/*
* Extract the relevant clauses from the parent relation and replace
* the operator OIDs with the corresponding regproc ids.
*
* now that local predicate clauses are copied into paths in
* find_rel_paths() and then (possibly) pulled up in
* xfunc_trypullup(), we get the relevant clauses from the path
* itself, not its parent relation. --- JMH, 6/15/92
*/
scan_clauses = fix_opids(get_actual_clauses(best_path->loc_restrictinfo));
switch (best_path->pathtype)
{
case T_SeqScan:
node = (Scan *) create_seqscan_node(best_path, tlist, scan_clauses);
break;
case T_IndexScan:
node = (Scan *) create_indexscan_node((IndexPath *) best_path,
tlist,
scan_clauses);
break;
default:
elog(ERROR, "create_scan_node: unknown node type",
best_path->pathtype);
break;
}
return node;
}
/*
* create_join_node
* Create a join path for 'best_path' and(recursively) paths for its
* inner and outer paths.
*
* 'tlist' is the targetlist for the join relation corresponding to
* 'best_path'
*
* Returns the join node.
*/
static Join *
create_join_node(JoinPath *best_path, List *tlist)
{
Plan *outer_node;
List *outer_tlist;
Plan *inner_node;
List *inner_tlist;
List *clauses;
Join *retval = NULL;
outer_node = create_plan((Path *) best_path->outerjoinpath);
outer_tlist = outer_node->targetlist;
inner_node = create_plan((Path *) best_path->innerjoinpath);
inner_tlist = inner_node->targetlist;
clauses = get_actual_clauses(best_path->pathinfo);
switch (best_path->path.pathtype)
{
case T_MergeJoin:
retval = (Join *) create_mergejoin_node((MergePath *) best_path,
tlist,
clauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
break;
case T_HashJoin:
retval = (Join *) create_hashjoin_node((HashPath *) best_path,
tlist,
clauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
break;
case T_NestLoop:
retval = (Join *) create_nestloop_node((NestPath *) best_path,
tlist,
clauses,
outer_node,
outer_tlist,
inner_node,
inner_tlist);
break;
default:
/* do nothing */
elog(ERROR, "create_join_node: unknown node type",
best_path->path.pathtype);
}
#if 0
/*
* * Expensive function pullups may have pulled local predicates *
* into this path node. Put them in the qpqual of the plan node. *
* JMH, 6/15/92
*/
if (get_loc_restrictinfo(best_path) != NIL)
set_qpqual((Plan) retval,
nconc(get_qpqual((Plan) retval),
fix_opids(get_actual_clauses
(get_loc_restrictinfo(best_path)))));
#endif
return retval;
}
/*****************************************************************************
*
* BASE-RELATION SCAN METHODS
*
*****************************************************************************/
/*
* create_seqscan_node
* Returns a seqscan node for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static SeqScan *
create_seqscan_node(Path *best_path, List *tlist, List *scan_clauses)
{
SeqScan *scan_node = (SeqScan *) NULL;
Index scan_relid = -1;
List *temp;
temp = best_path->parent->relids;
if (temp == NULL)
elog(ERROR, "scanrelid is empty");
else
scan_relid = (Index) lfirsti(temp); /* ??? who takes care of
* lnext? - ay */
scan_node = make_seqscan(tlist,
scan_clauses,
scan_relid,
(Plan *) NULL);
scan_node->plan.cost = best_path->path_cost;
return scan_node;
}
/*
* create_indexscan_node
* Returns a indexscan node for the base relation scanned by 'best_path'
* with restriction clauses 'scan_clauses' and targetlist 'tlist'.
*/
static IndexScan *
create_indexscan_node(IndexPath *best_path,
List *tlist,
List *scan_clauses)
{
/*
* Extract the(first if conjunct, only if disjunct) clause from the
* restrictinfo list.
*/
Expr *index_clause = (Expr *) NULL;
List *indxqual = NIL;
List *qpqual = NIL;
List *fixed_indxqual = NIL;
List *ixid;
IndexScan *scan_node = (IndexScan *) NULL;
bool lossy = FALSE;
HeapTuple indexTuple;
Form_pg_index index;
/*
* If an 'or' clause is to be used with this index, the indxqual field
* will contain a list of the 'or' clause arguments, e.g., the
* clause(OR a b c) will generate: ((a) (b) (c)). Otherwise, the
* indxqual will simply contain one conjunctive qualification: ((a)).
*/
if (best_path->indexqual != NULL)
/* added call to fix_opids, JMH 6/23/92 */
index_clause = (Expr *)
lfirst(fix_opids(get_actual_clauses(best_path->indexqual)));
if (or_clause((Node *) index_clause))
{
List *temp = NIL;
foreach(temp, index_clause->args)
indxqual = lappend(indxqual, lcons(lfirst(temp), NIL));
}
else
{
indxqual = lcons(get_actual_clauses(best_path->indexqual),
NIL);
}
/* check and see if any indices are lossy */
foreach(ixid, best_path->indexid)
{
indexTuple = SearchSysCacheTuple(INDEXRELID,
ObjectIdGetDatum(lfirsti(ixid)),
0, 0, 0);
if (!HeapTupleIsValid(indexTuple))
elog(ERROR, "create_plan: index %u not found", lfirsti(ixid));
index = (Form_pg_index) GETSTRUCT(indexTuple);
if (index->indislossy)
lossy = TRUE;
}
/*
* The qpqual field contains all restrictions not automatically
* handled by the index. Note that for non-lossy indices, the
* predicates in the indxqual are handled by the index, while for
* lossy indices the indxqual predicates need to be double-checked
* after the index fetches the best-guess tuples.
*/
if (or_clause((Node *) index_clause))
{
qpqual = set_difference(scan_clauses,
lcons(index_clause, NIL));
if (lossy)
qpqual = lappend(qpqual, (List *) copyObject(index_clause));
}
else
{
qpqual = set_difference(scan_clauses, lfirst(indxqual));
if (lossy)
qpqual = nconc(qpqual,
(List *) copyObject(lfirst(indxqual)));
}
fixed_indxqual = (List *) fix_indxqual_references((Node *) indxqual, (Path *) best_path);
scan_node = make_indexscan(tlist,
qpqual,
lfirsti(best_path->path.parent->relids),
best_path->indexid,
fixed_indxqual,
indxqual);
scan_node->scan.plan.cost = best_path->path.path_cost;
return scan_node;
}
/*****************************************************************************
*
* JOIN METHODS
*
*****************************************************************************/
static NestLoop *
create_nestloop_node(NestPath *best_path,
List *tlist,
List *clauses,
Plan *outer_node,
List *outer_tlist,
Plan *inner_node,
List *inner_tlist)
{
NestLoop *join_node = (NestLoop *) NULL;
if (IsA(inner_node, IndexScan))
{
/*
* An index is being used to reduce the number of tuples scanned
* in the inner relation. There will never be more than one index
* used in the inner scan path, so we need only consider the first
* set of qualifications in indxqual.
*
* But there may be more than one clause in this "first set" in the
* case of multi-column indices. - vadim 03/18/97
*/
List *inner_indxqual = lfirst(((IndexScan *) inner_node)->indxqual);
List *inner_qual;
bool found = false;
foreach(inner_qual, inner_indxqual)
{
if (!qual_clause_p((Node *) lfirst(inner_qual)))
{
found = true;
break;
}
}
/*
* If we have in fact found a join index qualification, remove
* these index clauses from the nestloop's join clauses and reset
* the inner(index) scan's qualification so that the var nodes
* refer to the proper outer join relation attributes.
*
* XXX Re-moving index clauses doesn't work properly: 1.
* fix_indxqual_references may change varattno-s in
* inner_indxqual; 2. clauses may be commuted I havn't time to fix
* it at the moment. - vadim 04/24/97
*/
if (found)
{
List *new_inner_qual = NIL;
clauses = set_difference(clauses, inner_indxqual); /* XXX */
new_inner_qual = index_outerjoin_references(inner_indxqual,
outer_node->targetlist,
((Scan *) inner_node)->scanrelid);
((IndexScan *) inner_node)->indxqual = lcons(new_inner_qual, NIL);
}
}
else if (IsA_Join(inner_node))
{
inner_node = (Plan *) make_noname(inner_tlist,
NIL,
NULL,
inner_node,
NONAME_MATERIAL);
}
join_node = make_nestloop(tlist,
join_references(clauses,
outer_tlist,
inner_tlist),
outer_node,
inner_node);
join_node->join.cost = best_path->path.path_cost;
return join_node;
}
static MergeJoin *
create_mergejoin_node(MergePath *best_path,
List *tlist,
List *clauses,
Plan *outer_node,
List *outer_tlist,
Plan *inner_node,
List *inner_tlist)
{
List *qpqual,
*mergeclauses;
MergeJoin *join_node;
/*
* Separate the mergeclauses from the other join qualification clauses
* and set those clauses to contain references to lower attributes.
*/
qpqual = join_references(set_difference(clauses,
best_path->path_mergeclauses),
outer_tlist,
inner_tlist);
/*
* Now set the references in the mergeclauses and rearrange them so
* that the outer variable is always on the left.
*/
mergeclauses = switch_outer(join_references(best_path->path_mergeclauses,
outer_tlist,
inner_tlist));
/*
* Create explicit sort paths for the outer and inner join paths if
* necessary. The sort cost was already accounted for in the path.
*/
if (best_path->outersortkeys)
{
Oid *outer_order = generate_merge_input_sortorder(
best_path->outersortkeys,
best_path->jpath.path.pathorder->ord.merge);
outer_node = (Plan *) make_noname(outer_tlist,
best_path->outersortkeys,
outer_order,
outer_node,
NONAME_SORT);
}
if (best_path->innersortkeys)
{
Oid *inner_order = generate_merge_input_sortorder(
best_path->innersortkeys,
best_path->jpath.path.pathorder->ord.merge);
inner_node = (Plan *) make_noname(inner_tlist,
best_path->innersortkeys,
inner_order,
inner_node,
NONAME_SORT);
}
join_node = make_mergejoin(tlist,
qpqual,
mergeclauses,
inner_node,
outer_node);
join_node->join.cost = best_path->jpath.path.path_cost;
return join_node;
}
/*
* create_hashjoin_node-- XXX HASH
*
* Returns a new hashjoin node.
*
* XXX hash join ops are totally bogus -- how the hell do we choose
* these?? at runtime? what about a hash index?
*/
static HashJoin *
create_hashjoin_node(HashPath *best_path,
List *tlist,
List *clauses,
Plan *outer_node,
List *outer_tlist,
Plan *inner_node,
List *inner_tlist)
{
List *qpqual;
List *hashclauses;
HashJoin *join_node;
Hash *hash_node;
Var *innerhashkey;
/*
* Separate the hashclauses from the other join qualification clauses
* and set those clauses to contain references to lower attributes.
*/
qpqual = join_references(set_difference(clauses,
best_path->path_hashclauses),
outer_tlist,
inner_tlist);
/*
* Now set the references in the hashclauses and rearrange them so
* that the outer variable is always on the left.
*/
hashclauses = switch_outer(join_references(best_path->path_hashclauses,
outer_tlist,
inner_tlist));
innerhashkey = get_rightop(lfirst(hashclauses));
hash_node = make_hash(inner_tlist, innerhashkey, inner_node);
join_node = make_hashjoin(tlist,
qpqual,
hashclauses,
outer_node,
(Plan *) hash_node);
join_node->join.cost = best_path->jpath.path.path_cost;
return join_node;
}
/*****************************************************************************
*
* SUPPORTING ROUTINES
*
*****************************************************************************/
/*
* fix_indxqual_references
* Adjust a qual clause to refer to an index instead of the original relation.
*
* Returns a modified copy of the given clause --- the original is not changed.
*/
static Node *
fix_indxqual_references(Node *clause, Path *index_path)
{
if (clause == NULL)
return NULL;
else if (IsA(clause, Var))
{
if (lfirsti(index_path->parent->relids) == ((Var *) clause)->varno)
{
Node *newclause;
int pos = 0;
int varatt = ((Var *) clause)->varattno;
int *indexkeys = ((IndexPath *) index_path)->indexkeys;
if (indexkeys)
{
while (indexkeys[pos] != 0)
{
if (varatt == indexkeys[pos])
break;
pos++;
}
}
newclause = copyObject(clause);
((Var *) newclause)->varattno = pos + 1;
return newclause;
}
/* The Var is not part of the indexed relation, leave it alone */
return copyObject(clause);
}
else if (single_node(clause))
return copyObject(clause);
else if (is_opclause(clause) &&
is_funcclause((Node *) get_leftop((Expr *) clause)) &&
((Func *) ((Expr *) get_leftop((Expr *) clause))->oper)->funcisindex)
{
/*
* This looks pretty seriously wrong to me, but I'm not sure what
* it's supposed to be doing ... tgl 5/99
*/
Var *newvar = makeVar((Index) lfirsti(index_path->parent->relids),
1, /* func indices have one key */
((Func *) ((Expr *) clause)->oper)->functype,
-1,
0,
(Index) lfirsti(index_path->parent->relids),
0);
return ((Node *) make_opclause((Oper *) ((Expr *) clause)->oper,
newvar,
get_rightop((Expr *) clause)));
}
else if (IsA(clause, Expr))
{
Expr *expr = (Expr *) clause;
List *new_subclauses = NIL;
List *i;
foreach(i, expr->args)
{
Node *subclause = lfirst(i);
new_subclauses = lappend(new_subclauses,
fix_indxqual_references(subclause,
index_path));
}
return (Node *) make_clause(expr->opType, expr->oper, new_subclauses);
}
else if (IsA(clause, List))
{
List *new_subclauses = NIL;
List *i;
foreach(i, (List *) clause)
{
Node *subclause = lfirst(i);
new_subclauses = lappend(new_subclauses,
fix_indxqual_references(subclause,
index_path));
}
return (Node *) new_subclauses;
}
else if (IsA(clause, ArrayRef))
{
ArrayRef *oldnode = (ArrayRef *) clause;
ArrayRef *newnode = makeNode(ArrayRef);
newnode->refattrlength = oldnode->refattrlength;
newnode->refelemlength = oldnode->refelemlength;
newnode->refelemtype = oldnode->refelemtype;
newnode->refelembyval = oldnode->refelembyval;
newnode->refupperindexpr = (List *)
fix_indxqual_references((Node *) oldnode->refupperindexpr,
index_path);
newnode->reflowerindexpr = (List *)
fix_indxqual_references((Node *) oldnode->reflowerindexpr,
index_path);
newnode->refexpr =
fix_indxqual_references(oldnode->refexpr, index_path);
newnode->refassgnexpr =
fix_indxqual_references(oldnode->refassgnexpr, index_path);
return (Node *) newnode;
}
else
{
elog(ERROR, "fix_indxqual_references: Cannot handle node type %d",
nodeTag(clause));
return NULL;
}
}
/*
* switch_outer
* Given a list of merge clauses, rearranges the elements within the
* clauses so the outer join variable is on the left and the inner is on
* the right. The original list is not touched; a modified list
* is returned.
*/
static List *
switch_outer(List *clauses)
{
List *t_list = NIL;
Expr *temp;
List *i;
Expr *clause;
Node *op;
foreach(i, clauses)
{
clause = lfirst(i);
Assert(is_opclause((Node *) clause));
op = (Node *) get_rightop(clause);
Assert(op != (Node *) NULL);
if (IsA(op, ArrayRef))
op = ((ArrayRef *) op)->refexpr;
Assert(IsA(op, Var));
if (var_is_outer((Var *) op))
{
/*
* Duplicate just enough of the structure to allow commuting
* the clause without changing the original list. Could use
* copyObject, but a complete deep copy is overkill.
*/
temp = make_clause(clause->opType, clause->oper,
lcons(get_leftop(clause),
lcons(get_rightop(clause),
NIL)));
/* Commute it --- note this modifies the temp node in-place. */
CommuteClause((Node *) temp);
t_list = lappend(t_list, temp);
}
else
t_list = lappend(t_list, clause);
}
return t_list;
}
/*
* generate_merge_input_sortorder
*
* Generate the list of sort ops needed to sort one of the input paths for
* a merge. We may have to use either left or right sortop for each item,
* since the original mergejoin clause may or may not have been commuted
* (compare switch_outer above).
*
* XXX This is largely a crock. It works only because group_clauses_by_order
* only groups together mergejoin clauses that have identical MergeOrder info,
* which means we can safely use a single MergeOrder input to deal with all
* the data. There should be a more general data structure that allows coping
* with groups of mergejoin clauses that have different join operators.
*/
static Oid *
generate_merge_input_sortorder(List *pathkeys, MergeOrder *mergeorder)
{
int listlength = length(pathkeys);
Oid *result = (Oid *) palloc(sizeof(Oid) * (listlength + 1));
Oid *nextsortop = result;
List *p;
foreach(p, pathkeys)
{
Var *pkey = (Var *) lfirst((List *) lfirst(p));
Assert(IsA(pkey, Var));
if (pkey->vartype == mergeorder->left_type)
*nextsortop++ = mergeorder->left_operator;
else if (pkey->vartype == mergeorder->right_type)
*nextsortop++ = mergeorder->right_operator;
else
elog(ERROR,
"generate_merge_input_sortorder: can't handle data type %d",
pkey->vartype);
}
*nextsortop++ = InvalidOid;
return result;
}
/*
* set_noname_tlist_operators
* Sets the key and keyop fields of resdom nodes in a target list.
*
* 'tlist' is the target list
* 'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)),
* corresponding to vars in the target list that are to
* be sorted or hashed
* 'operators' is the corresponding list of N sort or hash operators
*
* Returns the modified-in-place target list.
*/
static List *
set_noname_tlist_operators(List *tlist, List *pathkeys, Oid *operators)
{
int keyno = 1;
Node *pathkey;
Resdom *resdom;
List *i;
foreach(i, pathkeys)
{
pathkey = lfirst((List *) lfirst(i));
resdom = tlist_member((Var *) pathkey, tlist);
if (resdom)
{
/*
* Order the resdom pathkey and replace the operator OID for
* each key with the regproc OID.
*/
resdom->reskey = keyno;
resdom->reskeyop = get_opcode(operators[keyno - 1]);
}
keyno += 1;
}
return tlist;
}
/*
* Copy cost and size info from a lower plan node to an inserted node.
* This is not critical, since the decisions have already been made,
* but it helps produce more reasonable-looking EXPLAIN output.
*/
static void
copy_costsize(Plan *dest, Plan *src)
{
if (src)
{
dest->cost = src->cost;
dest->plan_size = src->plan_size;
dest->plan_width = src->plan_width;
}
else
{
dest->cost = 0;
dest->plan_size = 0;
dest->plan_width = 0;
}
}
/*****************************************************************************
*
*
*****************************************************************************/
/*
* make_noname
* Create plan nodes to sort or materialize relations into noname. The
* result returned for a sort will look like (SEQSCAN(SORT(plan_node)))
* or (SEQSCAN(MATERIAL(plan_node)))
*
* 'tlist' is the target list of the scan to be sorted or hashed
* 'pathkeys' is the list of keys which the sort or hash will be done on
* 'operators' is the operators with which the sort or hash is to be done
* (a list of operator OIDs)
* 'plan_node' is the node which yields tuples for the sort
* 'nonametype' indicates which operation(sort or hash) to perform
*/
static Noname *
make_noname(List *tlist,
List *pathkeys,
Oid *operators,
Plan *plan_node,
int nonametype)
{
List *noname_tlist;
Noname *retval = NULL;
/* Create a new target list for the noname, with keys set. */
noname_tlist = set_noname_tlist_operators(new_unsorted_tlist(tlist),
pathkeys,
operators);
switch (nonametype)
{
case NONAME_SORT:
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
(Plan *) make_sort(noname_tlist,
_NONAME_RELATION_ID_,
plan_node,
length(pathkeys)));
break;
case NONAME_MATERIAL:
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
(Plan *) make_material(noname_tlist,
_NONAME_RELATION_ID_,
plan_node,
length(pathkeys)));
break;
default:
elog(ERROR, "make_noname: unknown noname type %d", nonametype);
}
return retval;
}
SeqScan *
make_seqscan(List *qptlist,
List *qpqual,
Index scanrelid,
Plan *lefttree)
{
SeqScan *node = makeNode(SeqScan);
Plan *plan = &node->plan;
copy_costsize(plan, lefttree);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->scanrelid = scanrelid;
node->scanstate = (CommonScanState *) NULL;
return node;
}
static IndexScan *
make_indexscan(List *qptlist,
List *qpqual,
Index scanrelid,
List *indxid,
List *indxqual,
List *indxqualorig)
{
IndexScan *node = makeNode(IndexScan);
Plan *plan = &node->scan.plan;
copy_costsize(plan, NULL);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = NULL;
plan->righttree = NULL;
node->scan.scanrelid = scanrelid;
node->indxid = indxid;
node->indxqual = indxqual;
node->indxqualorig = indxqualorig;
node->scan.scanstate = (CommonScanState *) NULL;
return node;
}
static NestLoop *
make_nestloop(List *qptlist,
List *qpqual,
Plan *lefttree,
Plan *righttree)
{
NestLoop *node = makeNode(NestLoop);
Plan *plan = &node->join;
/*
* this cost estimate is entirely bogus... hopefully it will be
* overwritten by caller.
*/
plan->cost = (lefttree ? lefttree->cost : 0) +
(righttree ? righttree->cost : 0);
plan->state = (EState *) NULL;
plan->targetlist = qptlist;
plan->qual = qpqual;
plan->lefttree = lefttree;
plan->righttree = righttree;
node->nlstate = (NestLoopState *) NULL;
return node;
}
static HashJoin *
make_hashjoin(List *tlist,
List *qpqual,
List *hashclauses,
Plan *lefttree,
Plan *righttree)
{
HashJoin *node = makeNode(HashJoin);
Plan *plan = &node->join;
/*
* this cost estimate is entirely bogus... hopefully it will be
* overwritten by caller.
*/
plan->cost = (lefttree ? lefttree->cost : 0) +
(righttree ? righttree->cost : 0);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = qpqual;
plan->lefttree = lefttree;
plan->righttree = righttree;
node->hashclauses = hashclauses;
node->hashdone = false;
return node;
}
static Hash *
make_hash(List *tlist, Var *hashkey, Plan *lefttree)
{
Hash *node = makeNode(Hash);
Plan *plan = &node->plan;
copy_costsize(plan, lefttree);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NULL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->hashkey = hashkey;
return node;
}
static MergeJoin *
make_mergejoin(List *tlist,
List *qpqual,
List *mergeclauses,
Plan *righttree,
Plan *lefttree)
{
MergeJoin *node = makeNode(MergeJoin);
Plan *plan = &node->join;
/*
* this cost estimate is entirely bogus... hopefully it will be
* overwritten by caller.
*/
plan->cost = (lefttree ? lefttree->cost : 0) +
(righttree ? righttree->cost : 0);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = qpqual;
plan->lefttree = lefttree;
plan->righttree = righttree;
node->mergeclauses = mergeclauses;
return node;
}
Sort *
make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount)
{
Sort *node = makeNode(Sort);
Plan *plan = &node->plan;
copy_costsize(plan, lefttree);
plan->cost += cost_sort(NULL, plan->plan_size, plan->plan_width);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->nonameid = nonameid;
node->keycount = keycount;
return node;
}
static Material *
make_material(List *tlist,
Oid nonameid,
Plan *lefttree,
int keycount)
{
Material *node = makeNode(Material);
Plan *plan = &node->plan;
copy_costsize(plan, lefttree);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->nonameid = nonameid;
node->keycount = keycount;
return node;
}
Agg *
make_agg(List *tlist, Plan *lefttree)
{
Agg *node = makeNode(Agg);
copy_costsize(&node->plan, lefttree);
node->plan.state = (EState *) NULL;
node->plan.qual = NULL;
node->plan.targetlist = tlist;
node->plan.lefttree = lefttree;
node->plan.righttree = (Plan *) NULL;
node->aggs = NIL;
return node;
}
Group *
make_group(List *tlist,
bool tuplePerGroup,
int ngrp,
AttrNumber *grpColIdx,
Sort *lefttree)
{
Group *node = makeNode(Group);
copy_costsize(&node->plan, (Plan *) lefttree);
node->plan.state = (EState *) NULL;
node->plan.qual = NULL;
node->plan.targetlist = tlist;
node->plan.lefttree = (Plan *) lefttree;
node->plan.righttree = (Plan *) NULL;
node->tuplePerGroup = tuplePerGroup;
node->numCols = ngrp;
node->grpColIdx = grpColIdx;
return node;
}
/*
* A unique node always has a SORT node in the lefttree.
*
* the uniqueAttr argument must be a null-terminated string,
* either the name of the attribute to select unique on
* or "*"
*/
Unique *
make_unique(List *tlist, Plan *lefttree, char *uniqueAttr)
{
Unique *node = makeNode(Unique);
Plan *plan = &node->plan;
copy_costsize(plan, lefttree);
plan->state = (EState *) NULL;
plan->targetlist = tlist;
plan->qual = NIL;
plan->lefttree = lefttree;
plan->righttree = NULL;
node->nonameid = _NONAME_RELATION_ID_;
node->keycount = 0;
if (strcmp(uniqueAttr, "*") == 0)
node->uniqueAttr = NULL;
else
node->uniqueAttr = pstrdup(uniqueAttr);
return node;
}
#ifdef NOT_USED
List *
generate_fjoin(List *tlist)
{
List tlistP;
List newTlist = NIL;
List fjoinList = NIL;
int nIters = 0;
/*
* Break the target list into elements with Iter nodes, and those
* without them.
*/
foreach(tlistP, tlist)
{
List tlistElem;
tlistElem = lfirst(tlistP);
if (IsA(lsecond(tlistElem), Iter))
{
nIters++;
fjoinList = lappend(fjoinList, tlistElem);
}
else
newTlist = lappend(newTlist, tlistElem);
}
/*
* if we have an Iter node then we need to flatten.
*/
if (nIters > 0)
{
List *inner;
List *tempList;
Fjoin *fjoinNode;
DatumPtr results = (DatumPtr) palloc(nIters * sizeof(Datum));
BoolPtr alwaysDone = (BoolPtr) palloc(nIters * sizeof(bool));
inner = lfirst(fjoinList);
fjoinList = lnext(fjoinList);
fjoinNode = (Fjoin) MakeFjoin(false,
nIters,
inner,
results,
alwaysDone);
tempList = lcons(fjoinNode, fjoinList);
newTlist = lappend(newTlist, tempList);
}
return newTlist;
return tlist; /* do nothing for now - ay 10/94 */
}
#endif