/*------------------------------------------------------------------------- * * 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.50 1999/03/01 00:10:33 tgl Exp $ * *------------------------------------------------------------------------- */ #include #include #include "postgres.h" #include #include #include "nodes/execnodes.h" #include "nodes/plannodes.h" #include "nodes/relation.h" #include "nodes/primnodes.h" #include "nodes/nodeFuncs.h" #include "nodes/makefuncs.h" #include "utils/lsyscache.h" #include "utils/palloc.h" #include "utils/builtins.h" #include "optimizer/restrictinfo.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" #include "optimizer/tlist.h" #include "optimizer/planner.h" #include "optimizer/xfunc.h" #include "optimizer/internal.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, Cost cost); 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); /* * 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 %d 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, 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); Noname *sorted_outer_node = make_noname(outer_tlist, best_path->outersortkeys, outer_order, outer_node, NONAME_SORT); sorted_outer_node->plan.cost = outer_node->cost; outer_node = (Plan *) sorted_outer_node; } if (best_path->innersortkeys) { Oid *inner_order = generate_merge_input_sortorder( best_path->innersortkeys, best_path->jpath.path.pathorder->ord.merge); Noname *sorted_inner_node = make_noname(inner_tlist, best_path->innersortkeys, inner_order, inner_node, NONAME_SORT); sorted_inner_node->plan.cost = outer_node->cost; /* XXX not inner_node? */ inner_node = (Plan *) sorted_inner_node; } 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 * *****************************************************************************/ static Node * fix_indxqual_references(Node *clause, Path *index_path) { Node *newclause; if (IsA(clause, Var)) { if (lfirsti(index_path->parent->relids) == ((Var *) clause)->varno) { 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; } else return clause; } else if (IsA(clause, Const)) return clause; else if (IsA(clause, Param)) { /* Function parameter used as index scan arg. DZ - 27-8-1996 */ return clause; } else if (is_opclause(clause) && is_funcclause((Node *) get_leftop((Expr *) clause)) && ((Func *) ((Expr *) get_leftop((Expr *) clause))->oper)->funcisindex) { 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; Node *subclause = NULL; List *i = NIL; foreach(i, expr->args) { subclause = lfirst(i); if (subclause) new_subclauses = lappend(new_subclauses, fix_indxqual_references(subclause, index_path)); } /* * XXX new_subclauses should be a list of the form: ( (var var) * (var const) ...) ? */ if (new_subclauses) { return (Node *) make_clause(expr->opType, expr->oper, new_subclauses); } else return clause; } else if (IsA(clause, CaseExpr)) { elog(NOTICE,"optimizer: fix_indxqual_references sees CaseExpr"); return clause; } else { List *oldclauses = (List *) clause; List *new_subclauses = NIL; Node *subclause = NULL; List *i = NIL; foreach(i, oldclauses) { subclause = lfirst(i); if (subclause) new_subclauses = lappend(new_subclauses, fix_indxqual_references(subclause, index_path)); } /* * XXX new_subclauses should be a list of the form: ( (var var) * (var const) ...) ? */ if (new_subclauses) return (Node *) new_subclauses; else return clause; } } /* * 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; } /***************************************************************************** * * *****************************************************************************/ /* * 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; plan->cost = (lefttree ? lefttree->cost : 0); 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, Cost cost) { IndexScan *node = makeNode(IndexScan); Plan *plan = &node->scan.plan; plan->cost = cost; 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; 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; plan->cost = (lefttree ? lefttree->cost : 0) + (righttree ? righttree->cost : 0); plan->cost = 0.0; plan->state = (EState *) NULL; plan->targetlist = tlist; plan->qual = qpqual; plan->lefttree = lefttree; plan->righttree = righttree; node->hashclauses = hashclauses; node->hashjointable = NULL; node->hashjointablekey = 0; node->hashjointablesize = 0; node->hashdone = false; return node; } static Hash * make_hash(List *tlist, Var *hashkey, Plan *lefttree) { Hash *node = makeNode(Hash); Plan *plan = &node->plan; plan->cost = (lefttree ? lefttree->cost : 0); plan->cost = 0.0; plan->state = (EState *) NULL; plan->targetlist = tlist; plan->qual = NULL; plan->lefttree = lefttree; plan->righttree = NULL; node->hashkey = hashkey; node->hashtable = NULL; node->hashtablekey = 0; node->hashtablesize = 0; return node; } static MergeJoin * make_mergejoin(List *tlist, List *qpqual, List *mergeclauses, Plan *righttree, Plan *lefttree) { MergeJoin *node = makeNode(MergeJoin); Plan *plan = &node->join; 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; plan->cost = (lefttree ? lefttree->cost : 0); 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; plan->cost = (lefttree ? lefttree->cost : 0); 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); node->plan.cost = (lefttree ? lefttree->cost : 0); 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); node->plan.cost = (lefttree ? lefttree->plan.cost : 0); 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; plan->cost = (lefttree ? lefttree->cost : 0); 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