1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-14 08:21:07 +03:00

pgindent run. Make it all clean.

This commit is contained in:
Bruce Momjian
2001-03-22 04:01:46 +00:00
parent 6cf8707b82
commit 9e1552607a
555 changed files with 32514 additions and 28110 deletions

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_eval.c,v 1.57 2001/01/24 19:42:57 momjian Exp $
* $Id: geqo_eval.c,v 1.58 2001/03/22 03:59:33 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -49,8 +49,8 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
* allocated inside gimme_tree().
*
* Since geqo_eval() will be called many times, we can't afford to let
* all that memory go unreclaimed until end of statement. Note we make
* the temp context a child of TransactionCommandContext, so that
* all that memory go unreclaimed until end of statement. Note we
* make the temp context a child of TransactionCommandContext, so that
* it will be freed even if we abort via elog(ERROR).
*/
mycontext = AllocSetContextCreate(TransactionCommandContext,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_main.c,v 1.26 2001/01/24 19:42:57 momjian Exp $
* $Id: geqo_main.c,v 1.27 2001/03/22 03:59:33 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -36,11 +36,11 @@
/*
* Configuration options
*/
int Geqo_pool_size;
int Geqo_effort;
int Geqo_generations;
int Geqo_pool_size;
int Geqo_effort;
int Geqo_generations;
double Geqo_selection_bias;
int Geqo_random_seed;
int Geqo_random_seed;
static int gimme_pool_size(int nr_rel);
@ -101,10 +101,10 @@ geqo(Query *root, int number_of_rels, List *initial_rels)
/* seed random number generator */
/* XXX why is this done every time around? */
if (Geqo_random_seed >= 0)
srandom((unsigned int) Geqo_random_seed);
else
srandom((unsigned int) time(NULL));
if (Geqo_random_seed >= 0)
srandom((unsigned int) Geqo_random_seed);
else
srandom((unsigned int) time(NULL));
/* allocate genetic pool memory */
pool = alloc_pool(pool_size, number_of_rels);
@ -160,8 +160,8 @@ geqo(Query *root, int number_of_rels, List *initial_rels)
{
/* SELECTION */
geqo_selection(momma, daddy, pool, Geqo_selection_bias);/* using linear bias
* function */
geqo_selection(momma, daddy, pool, Geqo_selection_bias); /* using linear bias
* function */
@ -293,15 +293,15 @@ gimme_pool_size(int nr_rel)
{
double size;
if (Geqo_pool_size != 0)
{
if (Geqo_pool_size < MIN_GEQO_POOL_SIZE)
return MIN_GEQO_POOL_SIZE;
else if (Geqo_pool_size > MAX_GEQO_POOL_SIZE)
return MAX_GEQO_POOL_SIZE;
else
return Geqo_pool_size;
}
if (Geqo_pool_size != 0)
{
if (Geqo_pool_size < MIN_GEQO_POOL_SIZE)
return MIN_GEQO_POOL_SIZE;
else if (Geqo_pool_size > MAX_GEQO_POOL_SIZE)
return MAX_GEQO_POOL_SIZE;
else
return Geqo_pool_size;
}
size = pow(2.0, nr_rel + 1.0);
@ -323,8 +323,8 @@ gimme_pool_size(int nr_rel)
static int
gimme_number_generations(int pool_size, int effort)
{
if (Geqo_generations <= 0)
return effort * (int) ceil(log((double) pool_size) / log(2.0));
else
return Geqo_generations;
if (Geqo_generations <= 0)
return effort * (int) ceil(log((double) pool_size) / log(2.0));
else
return Geqo_generations;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.8 2001/01/24 19:42:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/_deadcode/Attic/predmig.c,v 1.9 2001/03/22 03:59:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -485,7 +485,7 @@ xfunc_form_groups(Query *queryInfo, Stream root, Stream bottom)
}
/* ------------------- UTILITY FUNCTIONS ------------------------- */
/* ------------------- UTILITY FUNCTIONS ------------------------- */
/*
** xfunc_free_stream

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.71 2001/02/03 21:17:52 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.72 2001/03/22 03:59:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,12 +33,12 @@ int geqo_rels = DEFAULT_GEQO_RELS;
static void set_base_rel_pathlists(Query *root);
static void set_plain_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte);
RangeTblEntry *rte);
static void set_inherited_rel_pathlist(Query *root, RelOptInfo *rel,
RangeTblEntry *rte,
List *inheritlist);
RangeTblEntry *rte,
List *inheritlist);
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
List *initial_rels);
List *initial_rels);
#ifdef OPTIMIZER_DEBUG
static void debug_print_rel(Query *root, RelOptInfo *rel);
@ -94,7 +94,7 @@ set_base_rel_pathlists(Query *root)
RangeTblEntry *rte;
List *inheritlist;
Assert(length(rel->relids) == 1); /* better be base rel */
Assert(length(rel->relids) == 1); /* better be base rel */
rti = lfirsti(rel->relids);
rte = rt_fetch(rti, root->rtable);
@ -103,24 +103,25 @@ set_base_rel_pathlists(Query *root)
/* Subquery --- generate a separate plan for it */
/*
* If there are any restriction clauses that have been attached
* to the subquery relation, consider pushing them down to become
* HAVING quals of the subquery itself. (Not WHERE clauses, since
* they may refer to subquery outputs that are aggregate results.
* But planner.c will transfer them into the subquery's WHERE if
* they do not.) This transformation is useful because it may
* allow us to generate a better plan for the subquery than
* evaluating all the subquery output rows and then filtering
* them.
* If there are any restriction clauses that have been
* attached to the subquery relation, consider pushing them
* down to become HAVING quals of the subquery itself. (Not
* WHERE clauses, since they may refer to subquery outputs
* that are aggregate results. But planner.c will transfer
* them into the subquery's WHERE if they do not.) This
* transformation is useful because it may allow us to
* generate a better plan for the subquery than evaluating all
* the subquery output rows and then filtering them.
*
* Currently, we do not push down clauses that contain subselects,
* mainly because I'm not sure it will work correctly (the
* subplan hasn't yet transformed sublinks to subselects).
* Also, if the subquery contains set ops (UNION/INTERSECT/EXCEPT)
* we do not push down any qual clauses, since the planner doesn't
* support quals at the top level of a setop. (With suitable
* analysis we could try to push the quals down into the component
* queries of the setop, but getting it right is not trivial.)
* Currently, we do not push down clauses that contain
* subselects, mainly because I'm not sure it will work
* correctly (the subplan hasn't yet transformed sublinks to
* subselects). Also, if the subquery contains set ops
* (UNION/INTERSECT/EXCEPT) we do not push down any qual
* clauses, since the planner doesn't support quals at the top
* level of a setop. (With suitable analysis we could try to
* push the quals down into the component queries of the
* setop, but getting it right is not trivial.)
* Non-pushed-down clauses will get evaluated as qpquals of
* the SubqueryScan node.
*
@ -136,8 +137,8 @@ set_base_rel_pathlists(Query *root)
foreach(lst, rel->baserestrictinfo)
{
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst);
Node *clause = (Node *) rinfo->clause;
RestrictInfo *rinfo = (RestrictInfo *) lfirst(lst);
Node *clause = (Node *) rinfo->clause;
if (contain_subplans(clause))
{
@ -146,13 +147,14 @@ set_base_rel_pathlists(Query *root)
}
else
{
/*
* We need to replace Vars in the clause (which must
* refer to outputs of the subquery) with copies of
* the subquery's targetlist expressions. Note that
* at this point, any uplevel Vars in the clause
* should have been replaced with Params, so they
* need no work.
* We need to replace Vars in the clause (which
* must refer to outputs of the subquery) with
* copies of the subquery's targetlist
* expressions. Note that at this point, any
* uplevel Vars in the clause should have been
* replaced with Params, so they need no work.
*/
clause = ResolveNew(clause, rti, 0,
rte->subquery->targetList,
@ -160,11 +162,12 @@ set_base_rel_pathlists(Query *root)
rte->subquery->havingQual =
make_and_qual(rte->subquery->havingQual,
clause);
/*
* We need not change the subquery's hasAggs or
* hasSublinks flags, since we can't be pushing down
* any aggregates that weren't there before, and we
* don't push down subselects at all.
* hasSublinks flags, since we can't be pushing
* down any aggregates that weren't there before,
* and we don't push down subselects at all.
*/
}
}
@ -215,9 +218,9 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
/*
* Generate paths and add them to the rel's pathlist.
*
* Note: add_path() will discard any paths that are dominated by
* another available path, keeping only those paths that are
* superior along at least one dimension of cost or sortedness.
* Note: add_path() will discard any paths that are dominated by another
* available path, keeping only those paths that are superior along at
* least one dimension of cost or sortedness.
*/
/* Consider sequential scan */
@ -230,9 +233,9 @@ set_plain_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte)
create_index_paths(root, rel, indices);
/*
* Note: create_or_index_paths depends on create_index_paths to
* have marked OR restriction clauses with relevant indices; this
* is why it doesn't need to be given the list of indices.
* Note: create_or_index_paths depends on create_index_paths to have
* marked OR restriction clauses with relevant indices; this is why it
* doesn't need to be given the list of indices.
*/
create_or_index_paths(root, rel, rel->baserestrictinfo);
@ -258,8 +261,8 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
List *il;
/*
* XXX for now, can't handle inherited expansion of FOR UPDATE;
* can we do better?
* XXX for now, can't handle inherited expansion of FOR UPDATE; can we
* do better?
*/
if (intMember(parentRTindex, root->rowMarks))
elog(ERROR, "SELECT FOR UPDATE is not supported for inherit queries");
@ -271,14 +274,14 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
rel->width = 0;
/*
* Generate access paths for each table in the tree (parent AND children),
* and pick the cheapest path for each table.
* Generate access paths for each table in the tree (parent AND
* children), and pick the cheapest path for each table.
*/
foreach(il, inheritlist)
{
int childRTindex = lfirsti(il);
int childRTindex = lfirsti(il);
RangeTblEntry *childrte;
Oid childOID;
Oid childOID;
RelOptInfo *childrel;
childrte = rt_fetch(childRTindex, root->rtable);
@ -289,16 +292,18 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
* attach the RelOptInfo to the query's base_rel_list, however.
*
* NOTE: when childRTindex == parentRTindex, we create a second
* RelOptInfo for the same relation. This RelOptInfo will represent
* the parent table alone, whereas the original RelOptInfo represents
* the union of the inheritance tree members.
* RelOptInfo for the same relation. This RelOptInfo will
* represent the parent table alone, whereas the original
* RelOptInfo represents the union of the inheritance tree
* members.
*/
childrel = make_base_rel(root, childRTindex);
/*
* Copy the parent's targetlist and restriction quals to the child,
* with attribute-number adjustment if needed. We don't bother
* to copy the join quals, since we can't do any joining here.
* Copy the parent's targetlist and restriction quals to the
* child, with attribute-number adjustment if needed. We don't
* bother to copy the join quals, since we can't do any joining
* here.
*/
childrel->targetlist = (List *)
adjust_inherited_attrs((Node *) rel->targetlist,
@ -328,8 +333,8 @@ set_inherited_rel_pathlist(Query *root, RelOptInfo *rel, RangeTblEntry *rte,
}
/*
* Finally, build Append path and install it as the only access
* path for the parent rel.
* Finally, build Append path and install it as the only access path
* for the parent rel.
*/
add_path(rel, (Path *) create_append_path(rel, subpaths));
@ -350,9 +355,9 @@ make_fromexpr_rel(Query *root, FromExpr *from)
List *jt;
/*
* Count the number of child jointree nodes. This is the depth
* of the dynamic-programming algorithm we must employ to consider
* all ways of joining the child nodes.
* Count the number of child jointree nodes. This is the depth of the
* dynamic-programming algorithm we must employ to consider all ways
* of joining the child nodes.
*/
levels_needed = length(from->fromlist);
@ -374,6 +379,7 @@ make_fromexpr_rel(Query *root, FromExpr *from)
if (levels_needed == 1)
{
/*
* Single jointree node, so we're done.
*/
@ -381,6 +387,7 @@ make_fromexpr_rel(Query *root, FromExpr *from)
}
else
{
/*
* Consider the different orders in which we could join the rels,
* using either GEQO or regular optimizer.
@ -401,7 +408,7 @@ make_fromexpr_rel(Query *root, FromExpr *from)
* independent jointree items in the query. This is > 1.
*
* 'initial_rels' is a list of RelOptInfo nodes for each independent
* jointree item. These are the components to be joined together.
* jointree item. These are the components to be joined together.
*
* Returns the final level of join relations, i.e., the relation that is
* the result of joining all the original relations together.
@ -423,8 +430,8 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
* joinitems[j] is a list of all the j-item rels. Initially we set
* joinitems[1] to represent all the single-jointree-item relations.
*/
joinitems = (List **) palloc((levels_needed+1) * sizeof(List *));
MemSet(joinitems, 0, (levels_needed+1) * sizeof(List *));
joinitems = (List **) palloc((levels_needed + 1) * sizeof(List *));
MemSet(joinitems, 0, (levels_needed + 1) * sizeof(List *));
joinitems[1] = initial_rels;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.41 2001/01/24 19:42:57 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.42 2001/03/22 03:59:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -128,7 +128,8 @@ clauselist_selectivity(Query *root,
* behave in the simple way we are expecting.)
*
* NB: for consistency of results, this fragment of code had better
* match what clause_selectivity() would do in the cases it handles.
* match what clause_selectivity() would do in the cases it
* handles.
*/
if (varRelid != 0 || NumRelids(clause) == 1)
{
@ -148,7 +149,7 @@ clauselist_selectivity(Query *root,
get_leftop((Expr *) clause);
if (is_pseudo_constant_clause((Node *) other))
{
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno;
RegProcedure oprrest = get_oprrest(opno);
if (!oprrest)
@ -156,15 +157,16 @@ clauselist_selectivity(Query *root,
else
s2 = restriction_selectivity(oprrest, opno,
getrelid(relidx,
root->rtable),
root->rtable),
attno,
constval, flag);
/*
* If we reach here, we have computed the same result that
* clause_selectivity would, so we can just use s2 if it's
* the wrong oprrest. But if it's the right oprrest, add
* the clause to rqlist for later processing.
* If we reach here, we have computed the same result
* that clause_selectivity would, so we can just use
* s2 if it's the wrong oprrest. But if it's the
* right oprrest, add the clause to rqlist for later
* processing.
*/
switch (oprrest)
{
@ -384,18 +386,20 @@ clause_selectivity(Query *root,
if (rte->subquery)
{
/*
* XXX not smart about subquery references...
* any way to do better?
* XXX not smart about subquery references... any way to
* do better?
*/
s1 = 0.5;
}
else
{
/*
* A Var at the top of a clause must be a bool Var.
* This is equivalent to the clause reln.attribute = 't',
* so we compute the selectivity as if that is what we have.
* A Var at the top of a clause must be a bool Var. This
* is equivalent to the clause reln.attribute = 't', so we
* compute the selectivity as if that is what we have.
*/
s1 = restriction_selectivity(F_EQSEL,
BooleanEqualOperator,

View File

@ -41,7 +41,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.68 2001/02/16 00:03:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.69 2001/03/22 03:59:34 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -67,11 +67,11 @@
#define LOG6(x) (log(x) / 1.79175946922805)
double effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
double random_page_cost = DEFAULT_RANDOM_PAGE_COST;
double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
double effective_cache_size = DEFAULT_EFFECTIVE_CACHE_SIZE;
double random_page_cost = DEFAULT_RANDOM_PAGE_COST;
double cpu_tuple_cost = DEFAULT_CPU_TUPLE_COST;
double cpu_index_tuple_cost = DEFAULT_CPU_INDEX_TUPLE_COST;
double cpu_operator_cost = DEFAULT_CPU_OPERATOR_COST;
Cost disable_cost = 100000000.0;
@ -117,14 +117,14 @@ cost_seqscan(Path *path, RelOptInfo *baserel)
/*
* disk costs
*
* The cost of reading a page sequentially is 1.0, by definition.
* Note that the Unix kernel will typically do some amount of
* read-ahead optimization, so that this cost is less than the
* true cost of reading a page from disk. We ignore that issue
* here, but must take it into account when estimating the cost of
* non-sequential accesses!
* The cost of reading a page sequentially is 1.0, by definition. Note
* that the Unix kernel will typically do some amount of read-ahead
* optimization, so that this cost is less than the true cost of
* reading a page from disk. We ignore that issue here, but must take
* it into account when estimating the cost of non-sequential
* accesses!
*/
run_cost += baserel->pages; /* sequential fetches with cost 1.0 */
run_cost += baserel->pages; /* sequential fetches with cost 1.0 */
/* CPU costs */
cpu_per_tuple = cpu_tuple_cost + baserel->baserestrictcost;
@ -600,12 +600,12 @@ cost_hashjoin(Path *path,
/*
* The number of tuple comparisons needed is the number of outer
* tuples times the typical hash bucket size. nodeHash.c tries for
* average bucket loading of NTUP_PER_BUCKET, but that goal will
* be reached only if data values are uniformly distributed among
* the buckets. To be conservative, we scale up the target bucket
* size by the number of inner rows times inner dispersion, giving
* an estimate of the typical number of duplicates of each value.
* We then charge one cpu_operator_cost per tuple comparison.
* average bucket loading of NTUP_PER_BUCKET, but that goal will be
* reached only if data values are uniformly distributed among the
* buckets. To be conservative, we scale up the target bucket size by
* the number of inner rows times inner dispersion, giving an estimate
* of the typical number of duplicates of each value. We then charge
* one cpu_operator_cost per tuple comparison.
*/
run_cost += cpu_operator_cost * outer_path->parent->rows *
NTUP_PER_BUCKET * ceil(inner_path->parent->rows * innerdispersion);
@ -672,7 +672,7 @@ cost_qual_eval(List *quals)
foreach(l, quals)
{
Node *qual = (Node *) lfirst(l);
Node *qual = (Node *) lfirst(l);
/*
* RestrictInfo nodes contain an eval_cost field reserved for this

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.102 2001/02/16 03:16:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.103 2001/03/22 03:59:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -183,8 +183,8 @@ create_index_paths(Query *root,
restrictinfo_list);
/*
* 3. Compute pathkeys describing index's ordering, if any,
* then see how many of them are actually useful for this query.
* 3. Compute pathkeys describing index's ordering, if any, then
* see how many of them are actually useful for this query.
*/
index_pathkeys = build_index_pathkeys(root, rel, index,
ForwardScanDirection);
@ -207,8 +207,9 @@ create_index_paths(Query *root,
NoMovementScanDirection));
/*
* 5. If the index is ordered, a backwards scan might be interesting.
* Currently this is only possible for a DESC query result ordering.
* 5. If the index is ordered, a backwards scan might be
* interesting. Currently this is only possible for a DESC query
* result ordering.
*/
if (index_is_ordered)
{
@ -422,10 +423,11 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
if (and_clause((Node *) orsubclause))
{
/*
* Extract relevant sub-subclauses in indexkey order. This is just
* like group_clauses_by_indexkey() except that the input and output
* are lists of bare clauses, not of RestrictInfo nodes.
* Extract relevant sub-subclauses in indexkey order. This is
* just like group_clauses_by_indexkey() except that the input and
* output are lists of bare clauses, not of RestrictInfo nodes.
*/
int *indexkeys = index->indexkeys;
Oid *classes = index->classlist;
@ -446,8 +448,8 @@ extract_or_indexqual_conditions(RelOptInfo *rel,
}
/*
* If no clauses match this key, we're done; we don't want to look
* at keys to its right.
* If no clauses match this key, we're done; we don't want to
* look at keys to its right.
*/
if (clausegroup == NIL)
break;
@ -748,8 +750,8 @@ match_clause_to_indexkey(RelOptInfo *rel,
/*
* Check for an indexqual that could be handled by a nestloop
* join. We need the index key to be compared against an
* expression that uses none of the indexed relation's vars
* and contains no non-cachable functions.
* expression that uses none of the indexed relation's vars and
* contains no non-cachable functions.
*/
if (match_index_to_operand(indexkey, leftop, rel, index))
{
@ -793,7 +795,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
* recognizing binary-compatible datatypes. For example, if we have
* an expression like "oid = 123", the operator will be oideqint4,
* which we need to replace with oideq in order to recognize it as
* matching an oid_ops index on the oid field. A variant case is where
* matching an oid_ops index on the oid field. A variant case is where
* the expression is like "oid::int4 = 123", where the given operator
* will be int4eq and again we need to intuit that we want to use oideq.
*
@ -832,13 +834,13 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
/*
* Maybe the index uses a binary-compatible operator set.
*
* Get the nominal input types of the given operator and the actual
* type (before binary-compatible relabeling) of the index key.
* Get the nominal input types of the given operator and the actual type
* (before binary-compatible relabeling) of the index key.
*/
oldoptup = SearchSysCache(OPEROID,
ObjectIdGetDatum(expr_op),
0, 0, 0);
if (! HeapTupleIsValid(oldoptup))
if (!HeapTupleIsValid(oldoptup))
return InvalidOid; /* probably can't happen */
oldopform = (Form_pg_operator) GETSTRUCT(oldoptup);
opname = pstrdup(NameStr(oldopform->oprname));
@ -848,7 +850,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
if (indexkey_on_left)
{
Node *leftop = (Node *) get_leftop(clause);
Node *leftop = (Node *) get_leftop(clause);
if (leftop && IsA(leftop, RelabelType))
leftop = ((RelabelType *) leftop)->arg;
@ -856,7 +858,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
}
else
{
Node *rightop = (Node *) get_rightop(clause);
Node *rightop = (Node *) get_rightop(clause);
if (rightop && IsA(rightop, RelabelType))
rightop = ((RelabelType *) rightop)->arg;
@ -874,9 +876,10 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
return InvalidOid;
/*
* OK, look for operator of the same name with the indexkey's data type.
* (In theory this might find a non-semantically-comparable operator,
* but in practice that seems pretty unlikely for binary-compatible types.)
* OK, look for operator of the same name with the indexkey's data
* type. (In theory this might find a non-semantically-comparable
* operator, but in practice that seems pretty unlikely for
* binary-compatible types.)
*/
new_op = compatible_oper_opid(opname, indexkeytype, indexkeytype, true);
@ -886,8 +889,8 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
{
/*
* OK, we found a binary-compatible operator of the same
* name; now does it match the index?
* OK, we found a binary-compatible operator of the same name;
* now does it match the index?
*/
if (indexkey_on_left)
commuted_op = new_op;
@ -1491,8 +1494,9 @@ match_index_to_operand(int indexkey,
RelOptInfo *rel,
IndexOptInfo *index)
{
/*
* Ignore any RelabelType node above the indexkey. This is needed to
* Ignore any RelabelType node above the indexkey. This is needed to
* be able to apply indexscanning in binary-compatible-operator cases.
* Note: we can assume there is at most one RelabelType node;
* eval_const_expressions() will have simplified if more than one.
@ -1670,7 +1674,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
patt = DatumGetCString(DirectFunctionCall1(textout,
constvalue));
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like,
&prefix, &rest) != Pattern_Prefix_None;
&prefix, &rest) != Pattern_Prefix_None;
if (prefix)
pfree(prefix);
pfree(patt);
@ -1687,7 +1691,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
patt = DatumGetCString(DirectFunctionCall1(textout,
constvalue));
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Like_IC,
&prefix, &rest) != Pattern_Prefix_None;
&prefix, &rest) != Pattern_Prefix_None;
if (prefix)
pfree(prefix);
pfree(patt);
@ -1704,7 +1708,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
patt = DatumGetCString(DirectFunctionCall1(textout,
constvalue));
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex,
&prefix, &rest) != Pattern_Prefix_None;
&prefix, &rest) != Pattern_Prefix_None;
if (prefix)
pfree(prefix);
pfree(patt);
@ -1721,7 +1725,7 @@ match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
patt = DatumGetCString(DirectFunctionCall1(textout,
constvalue));
isIndexable = pattern_fixed_prefix(patt, Pattern_Type_Regex_IC,
&prefix, &rest) != Pattern_Prefix_None;
&prefix, &rest) != Pattern_Prefix_None;
if (prefix)
pfree(prefix);
pfree(patt);
@ -1983,8 +1987,8 @@ prefix_quals(Var *leftop, Oid expr_op,
result = makeList1(expr);
/*
* If we can create a string larger than the prefix, we can say
* "x < greaterstr".
* If we can create a string larger than the prefix, we can say "x <
* greaterstr".
*/
greaterstr = make_greater_string(prefix, datatype);
if (greaterstr)
@ -2025,6 +2029,7 @@ find_operator(const char *opname, Oid datatype)
static Datum
string_to_datum(const char *str, Oid datatype)
{
/*
* We cheat a little by assuming that textin() will do for bpchar and
* varchar constants too...

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.61 2001/01/24 19:42:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.62 2001/03/22 03:59:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -25,32 +25,32 @@
#include "utils/lsyscache.h"
static void sort_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
static void match_unsorted_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
#ifdef NOT_USED
static void match_unsorted_inner(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, List *mergeclause_list,
JoinType jointype);
#endif
static void hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, JoinType jointype);
RelOptInfo *outerrel, RelOptInfo *innerrel,
List *restrictlist, JoinType jointype);
static Path *best_innerjoin(List *join_paths, List *outer_relid,
JoinType jointype);
JoinType jointype);
static Selectivity estimate_dispersion(Query *root, Var *var);
static List *select_mergejoin_clauses(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype);
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist,
JoinType jointype);
/*
@ -160,26 +160,27 @@ sort_inner_and_outer(Query *root,
* generate a differently-sorted result path at essentially the same
* cost. We have no basis for choosing one over another at this level
* of joining, but some sort orders may be more useful than others for
* higher-level mergejoins, so it's worth considering multiple orderings.
* higher-level mergejoins, so it's worth considering multiple
* orderings.
*
* Actually, it's not quite true that every mergeclause ordering will
* generate a different path order, because some of the clauses may be
* redundant. Therefore, what we do is convert the mergeclause list to
* a list of canonical pathkeys, and then consider different orderings
* of the pathkeys.
* redundant. Therefore, what we do is convert the mergeclause list
* to a list of canonical pathkeys, and then consider different
* orderings of the pathkeys.
*
* Generating a path for *every* permutation of the pathkeys doesn't
* seem like a winning strategy; the cost in planning time is too high.
* For now, we generate one path for each pathkey, listing that pathkey
* first and the rest in random order. This should allow at
* least a one-clause mergejoin without re-sorting against any other
* possible mergejoin partner path. But if we've not guessed the
* right ordering of secondary keys, we may end up evaluating
* clauses as qpquals when they could have been done as mergeclauses.
* We need to figure out a better way. (Two possible approaches: look
* at all the relevant index relations to suggest plausible sort
* orders, or make just one output path and somehow mark it as having
* a sort-order that can be rearranged freely.)
* Generating a path for *every* permutation of the pathkeys doesn't seem
* like a winning strategy; the cost in planning time is too high. For
* now, we generate one path for each pathkey, listing that pathkey
* first and the rest in random order. This should allow at least a
* one-clause mergejoin without re-sorting against any other possible
* mergejoin partner path. But if we've not guessed the right
* ordering of secondary keys, we may end up evaluating clauses as
* qpquals when they could have been done as mergeclauses. We need to
* figure out a better way. (Two possible approaches: look at all the
* relevant index relations to suggest plausible sort orders, or make
* just one output path and somehow mark it as having a sort-order
* that can be rearranged freely.)
*/
all_pathkeys = make_pathkeys_for_mergeclauses(root,
mergeclause_list,
@ -200,16 +201,17 @@ sort_inner_and_outer(Query *root,
lremove(front_pathkey,
listCopy(all_pathkeys)));
else
cur_pathkeys = all_pathkeys; /* no work at first one... */
cur_pathkeys = all_pathkeys; /* no work at first one... */
/*
* Select mergeclause(s) that match this sort ordering. If we had
* redundant merge clauses then we will get a subset of the original
* clause list. There had better be some match, however...
* redundant merge clauses then we will get a subset of the
* original clause list. There had better be some match,
* however...
*/
cur_mergeclauses = find_mergeclauses_for_pathkeys(root,
cur_pathkeys,
mergeclause_list);
mergeclause_list);
Assert(cur_mergeclauses != NIL);
/*
@ -334,10 +336,12 @@ match_unsorted_outer(Query *root,
if (nestjoinOK)
{
/*
* Always consider a nestloop join with this outer and cheapest-
* total-cost inner. Consider nestloops using the cheapest-
* startup-cost inner as well, and the best innerjoin indexpath.
* Always consider a nestloop join with this outer and
* cheapest- total-cost inner. Consider nestloops using the
* cheapest- startup-cost inner as well, and the best
* innerjoin indexpath.
*/
add_path(joinrel, (Path *)
create_nestloop_path(joinrel,
@ -352,7 +356,7 @@ match_unsorted_outer(Query *root,
create_nestloop_path(joinrel,
jointype,
outerpath,
innerrel->cheapest_startup_path,
innerrel->cheapest_startup_path,
restrictlist,
merge_pathkeys));
if (bestinnerjoin != NULL)
@ -382,8 +386,8 @@ match_unsorted_outer(Query *root,
/*
* Generate a mergejoin on the basis of sorting the cheapest
* inner. Since a sort will be needed, only cheapest total cost
* matters. (But create_mergejoin_path will do the right thing
* if innerrel->cheapest_total_path is already correctly sorted.)
* matters. (But create_mergejoin_path will do the right thing if
* innerrel->cheapest_total_path is already correctly sorted.)
*/
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
@ -400,13 +404,14 @@ match_unsorted_outer(Query *root,
* Look for presorted inner paths that satisfy the innersortkey
* list or any truncation thereof. Here, we consider both cheap
* startup cost and cheap total cost. Ignore
* innerrel->cheapest_total_path, since we already made a path with it.
* innerrel->cheapest_total_path, since we already made a path
* with it.
*/
num_sortkeys = length(innersortkeys);
if (num_sortkeys > 1)
trialsortkeys = listCopy(innersortkeys); /* need modifiable copy */
trialsortkeys = listCopy(innersortkeys); /* need modifiable copy */
else
trialsortkeys = innersortkeys; /* won't really truncate */
trialsortkeys = innersortkeys; /* won't really truncate */
cheapest_startup_inner = NULL;
cheapest_total_inner = NULL;
@ -417,8 +422,8 @@ match_unsorted_outer(Query *root,
/*
* Look for an inner path ordered well enough for the first
* 'sortkeycnt' innersortkeys. NB: trialsortkeys list
* is modified destructively, which is why we made a copy...
* 'sortkeycnt' innersortkeys. NB: trialsortkeys list is
* modified destructively, which is why we made a copy...
*/
trialsortkeys = ltruncate(sortkeycnt, trialsortkeys);
innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,
@ -478,8 +483,8 @@ match_unsorted_outer(Query *root,
{
newclauses =
find_mergeclauses_for_pathkeys(root,
trialsortkeys,
mergeclauses);
trialsortkeys,
mergeclauses);
Assert(newclauses != NIL);
}
else
@ -601,7 +606,7 @@ match_unsorted_inner(Query *root,
if (startupouterpath != NULL && startupouterpath != totalouterpath)
{
merge_pathkeys = build_join_pathkeys(root, joinrel,
startupouterpath->pathkeys);
startupouterpath->pathkeys);
add_path(joinrel, (Path *)
create_mergejoin_path(joinrel,
jointype,
@ -696,8 +701,8 @@ hash_inner_and_outer(Query *root,
* estimate dispersion of inner var for costing purposes.
*
* Since we tend to visit the same clauses over and over when
* planning a large query, we cache the dispersion estimates in the
* RestrictInfo node to avoid repeated lookups of statistics.
* planning a large query, we cache the dispersion estimates in
* the RestrictInfo node to avoid repeated lookups of statistics.
*/
if (intMember(left->varno, outerrelids) &&
intMember(right->varno, innerrelids))
@ -793,13 +798,13 @@ best_innerjoin(List *join_paths, Relids outer_relids, JoinType jointype)
foreach(join_path, join_paths)
{
IndexPath *path = (IndexPath *) lfirst(join_path);
IndexPath *path = (IndexPath *) lfirst(join_path);
Assert(IsA(path, IndexPath));
/*
* If processing an outer join, only use explicit join clauses in the
* inner indexscan. For inner joins we need not be so picky.
* If processing an outer join, only use explicit join clauses in
* the inner indexscan. For inner joins we need not be so picky.
*/
if (isouterjoin && !path->alljoinquals)
continue;
@ -879,15 +884,15 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
*right;
/*
* If processing an outer join, only use its own join clauses in the
* merge. For inner joins we need not be so picky.
* If processing an outer join, only use its own join clauses in
* the merge. For inner joins we need not be so picky.
*
* Furthermore, if it is a right/full join then *all* the explicit
* join clauses must be mergejoinable, else the executor will fail.
* If we are asked for a right join then just return NIL to indicate
* no mergejoin is possible (we can handle it as a left join instead).
* If we are asked for a full join then emit an error, because there
* is no fallback.
* join clauses must be mergejoinable, else the executor will
* fail. If we are asked for a right join then just return NIL to
* indicate no mergejoin is possible (we can handle it as a left
* join instead). If we are asked for a full join then emit an
* error, because there is no fallback.
*/
if (isouterjoin)
{
@ -897,7 +902,7 @@ select_mergejoin_clauses(RelOptInfo *joinrel,
{
case JOIN_RIGHT:
if (restrictinfo->mergejoinoperator == InvalidOid)
return NIL; /* not mergejoinable */
return NIL; /* not mergejoinable */
break;
case JOIN_FULL:
if (restrictinfo->mergejoinoperator == InvalidOid)

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.51 2001/02/16 00:03:07 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.52 2001/03/22 03:59:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -19,7 +19,7 @@
static RelOptInfo *make_join_rel(Query *root, RelOptInfo *rel1,
RelOptInfo *rel2, JoinType jointype);
RelOptInfo *rel2, JoinType jointype);
/*
@ -44,18 +44,19 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
/*
* First, consider left-sided and right-sided plans, in which rels of
* exactly level-1 member relations are joined against initial relations.
* We prefer to join using join clauses, but if we find a rel of level-1
* members that has no join clauses, we will generate Cartesian-product
* joins against all initial rels not already contained in it.
* exactly level-1 member relations are joined against initial
* relations. We prefer to join using join clauses, but if we find a
* rel of level-1 members that has no join clauses, we will generate
* Cartesian-product joins against all initial rels not already
* contained in it.
*
* In the first pass (level == 2), we try to join each initial rel to each
* initial rel that appears later in joinrels[1]. (The mirror-image
* joins are handled automatically by make_join_rel.) In later
* passes, we try to join rels of size level-1 from joinrels[level-1]
* to each initial rel in joinrels[1].
* In the first pass (level == 2), we try to join each initial rel to
* each initial rel that appears later in joinrels[1]. (The
* mirror-image joins are handled automatically by make_join_rel.) In
* later passes, we try to join rels of size level-1 from
* joinrels[level-1] to each initial rel in joinrels[1].
*/
foreach(r, joinrels[level-1])
foreach(r, joinrels[level - 1])
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
List *other_rels;
@ -73,9 +74,9 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
* Note that if all available join clauses for this rel
* require more than one other rel, we will fail to make any
* joins against it here. That's OK; it'll be considered by
* "bushy plan" join code in a higher-level pass where we
* have those other rels collected into a join rel. See also
* the last-ditch case below.
* "bushy plan" join code in a higher-level pass where we have
* those other rels collected into a join rel. See also the
* last-ditch case below.
*/
new_rels = make_rels_by_clause_joins(root,
old_rel,
@ -94,16 +95,16 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
}
/*
* At levels above 2 we will generate the same joined relation
* in multiple ways --- for example (a join b) join c is the same
* At levels above 2 we will generate the same joined relation in
* multiple ways --- for example (a join b) join c is the same
* RelOptInfo as (b join c) join a, though the second case will
* add a different set of Paths to it. To avoid making extra work
* for subsequent passes, do not enter the same RelOptInfo into our
* output list multiple times.
* add a different set of Paths to it. To avoid making extra work
* for subsequent passes, do not enter the same RelOptInfo into
* our output list multiple times.
*/
foreach(nr, new_rels)
{
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
if (!ptrMember(jrel, result_rels))
result_rels = lcons(jrel, result_rels);
@ -111,20 +112,21 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
}
/*
* Now, consider "bushy plans" in which relations of k initial rels are
* joined to relations of level-k initial rels, for 2 <= k <= level-2.
* Now, consider "bushy plans" in which relations of k initial rels
* are joined to relations of level-k initial rels, for 2 <= k <=
* level-2.
*
* We only consider bushy-plan joins for pairs of rels where there is a
* suitable join clause, in order to avoid unreasonable growth of
* planning time.
*/
for (k = 2; ; k++)
for (k = 2;; k++)
{
int other_level = level - k;
/*
* Since make_join_rel(x, y) handles both x,y and y,x cases,
* we only need to go as far as the halfway point.
* Since make_join_rel(x, y) handles both x,y and y,x cases, we
* only need to go as far as the halfway point.
*/
if (k > other_level)
break;
@ -139,7 +141,7 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
continue; /* we ignore clauseless joins here */
if (k == other_level)
other_rels = lnext(r); /* only consider remaining rels */
other_rels = lnext(r); /* only consider remaining rels */
else
other_rels = joinrels[other_level];
@ -153,8 +155,8 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
/*
* OK, we can build a rel of the right level from this
* pair of rels. Do so if there is at least one usable
* join clause.
* pair of rels. Do so if there is at least one
* usable join clause.
*/
foreach(i, old_rel->joininfo)
{
@ -170,7 +172,8 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
/* Avoid making duplicate entries ... */
if (!ptrMember(jrel, result_rels))
result_rels = lcons(jrel, result_rels);
break; /* need not consider more joininfos */
break; /* need not consider more
* joininfos */
}
}
}
@ -180,31 +183,34 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
/*
* Last-ditch effort: if we failed to find any usable joins so far,
* force a set of cartesian-product joins to be generated. This
* force a set of cartesian-product joins to be generated. This
* handles the special case where all the available rels have join
* clauses but we cannot use any of the joins yet. An example is
* clauses but we cannot use any of the joins yet. An example is
*
* SELECT * FROM a,b,c WHERE (a.f1 + b.f2 + c.f3) = 0;
*
* The join clause will be usable at level 3, but at level 2 we have
* no choice but to make cartesian joins. We consider only left-sided
* The join clause will be usable at level 3, but at level 2 we have no
* choice but to make cartesian joins. We consider only left-sided
* and right-sided cartesian joins in this case (no bushy).
*/
if (result_rels == NIL)
{
/* This loop is just like the first one, except we always call
/*
* This loop is just like the first one, except we always call
* make_rels_by_clauseless_joins().
*/
foreach(r, joinrels[level-1])
foreach(r, joinrels[level - 1])
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
List *other_rels;
if (level == 2)
other_rels = lnext(r); /* only consider remaining initial
* rels */
other_rels = lnext(r); /* only consider remaining initial
* rels */
else
other_rels = joinrels[1]; /* consider all initial rels */
other_rels = joinrels[1]; /* consider all initial
* rels */
new_rels = make_rels_by_clauseless_joins(root,
old_rel,
@ -212,7 +218,7 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
foreach(nr, new_rels)
{
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);
if (!ptrMember(jrel, result_rels))
result_rels = lcons(jrel, result_rels);
@ -266,6 +272,7 @@ make_rels_by_clause_joins(Query *root,
RelOptInfo *jrel;
jrel = make_join_rel(root, old_rel, other_rel, JOIN_INNER);
/*
* Avoid entering same joinrel into our output list more
* than once. (make_rels_by_joins doesn't really care,
@ -310,9 +317,10 @@ make_rels_by_clauseless_joins(Query *root,
RelOptInfo *jrel;
jrel = make_join_rel(root, old_rel, other_rel, JOIN_INNER);
/*
* As long as given other_rels are distinct, don't need
* to test to see if jrel is already part of output list.
* As long as given other_rels are distinct, don't need to
* test to see if jrel is already part of output list.
*/
result = lcons(jrel, result);
}
@ -325,7 +333,7 @@ make_rels_by_clauseless_joins(Query *root,
/*
* make_jointree_rel
* Find or build a RelOptInfojoin rel representing a specific
* jointree item. For JoinExprs, we only consider the construction
* jointree item. For JoinExprs, we only consider the construction
* path that corresponds exactly to what the user wrote.
*/
RelOptInfo *

View File

@ -11,7 +11,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.30 2001/01/24 19:42:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.31 2001/03/22 03:59:35 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,7 +31,7 @@
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
static List *make_canonical_pathkey(Query *root, PathKeyItem *item);
static Var *find_indexkey_var(Query *root, RelOptInfo *rel,
AttrNumber varattno);
AttrNumber varattno);
/*
@ -89,10 +89,10 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
* into our new set. When done, we add the new set to the front of
* equi_key_list.
*
* It may well be that the two items we're given are already known to
* be equijoin-equivalent, in which case we don't need to change our
* data structure. If we find both of them in the same equivalence
* set to start with, we can quit immediately.
* It may well be that the two items we're given are already known to be
* equijoin-equivalent, in which case we don't need to change our data
* structure. If we find both of them in the same equivalence set to
* start with, we can quit immediately.
*
* This is a standard UNION-FIND problem, for which there exist better
* data structures than simple lists. If this code ever proves to be
@ -109,7 +109,11 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
if (item1here || item2here)
{
/* If find both in same equivalence set, no need to do any more */
/*
* If find both in same equivalence set, no need to do any
* more
*/
if (item1here && item2here)
{
/* Better not have seen only one in an earlier set... */
@ -126,7 +130,8 @@ add_equijoined_keys(Query *root, RestrictInfo *restrictinfo)
/*
* Remove old set from equi_key_list. NOTE this does not
* change lnext(cursetlink), so the foreach loop doesn't break.
* change lnext(cursetlink), so the foreach loop doesn't
* break.
*/
root->equi_key_list = lremove(curset, root->equi_key_list);
freeList(curset); /* might as well recycle old cons cells */
@ -171,8 +176,8 @@ generate_implied_equalities(Query *root)
continue;
/*
* Match each item in the set with all that appear after it
* (it's sufficient to generate A=B, need not process B=A too).
* Match each item in the set with all that appear after it (it's
* sufficient to generate A=B, need not process B=A too).
*/
foreach(ptr1, curset)
{
@ -246,11 +251,12 @@ canonicalize_pathkeys(Query *root, List *pathkeys)
Assert(pathkey != NIL);
item = (PathKeyItem *) lfirst(pathkey);
cpathkey = make_canonical_pathkey(root, item);
/*
* Eliminate redundant ordering requests --- ORDER BY A,A
* is the same as ORDER BY A. We want to check this only
* after we have canonicalized the keys, so that equivalent-key
* knowledge is used when deciding if an item is redundant.
* Eliminate redundant ordering requests --- ORDER BY A,A is the
* same as ORDER BY A. We want to check this only after we have
* canonicalized the keys, so that equivalent-key knowledge is
* used when deciding if an item is redundant.
*/
if (!ptrMember(cpathkey, new_pathkeys))
new_pathkeys = lappend(new_pathkeys, cpathkey);
@ -285,8 +291,8 @@ compare_pathkeys(List *keys1, List *keys2)
List *subkey2 = lfirst(key2);
/*
* XXX would like to check that we've been given canonicalized input,
* but query root not accessible here...
* XXX would like to check that we've been given canonicalized
* input, but query root not accessible here...
*/
#ifdef NOT_USED
Assert(ptrMember(subkey1, root->equi_key_list));
@ -295,7 +301,7 @@ compare_pathkeys(List *keys1, List *keys2)
/*
* We will never have two subkeys where one is a subset of the
* other, because of the canonicalization process. Either they
* other, because of the canonicalization process. Either they
* are equal or they ain't. Furthermore, we only need pointer
* comparison to detect equality.
*/
@ -555,9 +561,10 @@ build_index_pathkeys(Query *root,
/* OK, make a sublist for this sort key */
item = makePathKeyItem((Node *) relvar, sortop);
cpathkey = make_canonical_pathkey(root, item);
/*
* Eliminate redundant ordering info; could happen if query
* is such that index keys are equijoined...
* Eliminate redundant ordering info; could happen if query is
* such that index keys are equijoined...
*/
if (!ptrMember(cpathkey, retval))
retval = lappend(retval, cpathkey);
@ -693,7 +700,7 @@ make_pathkeys_for_sortclauses(List *sortclauses,
*
* RestrictInfo contains fields in which we may cache the result
* of looking up the canonical pathkeys for the left and right sides
* of the mergeclause. (Note that in normal cases they will be the
* of the mergeclause. (Note that in normal cases they will be the
* same, but not if the mergeclause appears above an OUTER JOIN.)
* This is a worthwhile savings because these routines will be invoked
* many times when dealing with a many-relation query.
@ -756,8 +763,8 @@ find_mergeclauses_for_pathkeys(Query *root,
/*
* We can match a pathkey against either left or right side of any
* mergejoin clause we haven't used yet. For the moment we use a
* dumb "greedy" algorithm with no backtracking. Is it worth being
* any smarter to make a longer list of usable mergeclauses?
* dumb "greedy" algorithm with no backtracking. Is it worth
* being any smarter to make a longer list of usable mergeclauses?
* Probably not.
*/
foreach(j, restrictinfos)
@ -765,9 +772,10 @@ find_mergeclauses_for_pathkeys(Query *root,
RestrictInfo *restrictinfo = lfirst(j);
cache_mergeclause_pathkeys(root, restrictinfo);
/*
* We can compare canonical pathkey sublists by simple
* pointer equality; see compare_pathkeys.
* We can compare canonical pathkey sublists by simple pointer
* equality; see compare_pathkeys.
*/
if ((pathkey == restrictinfo->left_pathkey ||
pathkey == restrictinfo->right_pathkey) &&
@ -830,7 +838,7 @@ make_pathkeys_for_mergeclauses(Query *root,
cache_mergeclause_pathkeys(root, restrictinfo);
key = (Node *) get_leftop(restrictinfo->clause);
if (IsA(key, Var) && intMember(((Var *) key)->varno, rel->relids))
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
{
/* Rel is left side of mergeclause */
pathkey = restrictinfo->left_pathkey;
@ -838,7 +846,7 @@ make_pathkeys_for_mergeclauses(Query *root,
else
{
key = (Node *) get_rightop(restrictinfo->clause);
if (IsA(key, Var) && intMember(((Var *) key)->varno, rel->relids))
if (IsA(key, Var) &&intMember(((Var *) key)->varno, rel->relids))
{
/* Rel is right side of mergeclause */
pathkey = restrictinfo->right_pathkey;
@ -851,13 +859,14 @@ make_pathkeys_for_mergeclauses(Query *root,
}
/*
* When we are given multiple merge clauses, it's possible that some
* clauses refer to the same vars as earlier clauses. There's no
* reason for us to specify sort keys like (A,B,A) when (A,B) will
* do --- and adding redundant sort keys makes add_path think that
* this sort order is different from ones that are really the same,
* so don't do it. Since we now have a canonicalized pathkey,
* a simple ptrMember test is sufficient to detect redundant keys.
* When we are given multiple merge clauses, it's possible that
* some clauses refer to the same vars as earlier clauses.
* There's no reason for us to specify sort keys like (A,B,A) when
* (A,B) will do --- and adding redundant sort keys makes add_path
* think that this sort order is different from ones that are
* really the same, so don't do it. Since we now have a
* canonicalized pathkey, a simple ptrMember test is sufficient to
* detect redundant keys.
*/
if (!ptrMember(pathkey, pathkeys))
pathkeys = lappend(pathkeys, pathkey);
@ -911,6 +920,7 @@ pathkeys_useful_for_merging(Query *root, RelOptInfo *rel, List *pathkeys)
if (restrictinfo->mergejoinoperator == InvalidOid)
continue;
cache_mergeclause_pathkeys(root, restrictinfo);
/*
* We can compare canonical pathkey sublists by simple
* pointer equality; see compare_pathkeys.
@ -984,7 +994,9 @@ truncate_useless_pathkeys(Query *root,
nuseful2 = pathkeys_useful_for_ordering(root, pathkeys);
if (nuseful2 > nuseful)
nuseful = nuseful2;
/* Note: not safe to modify input list destructively, but we can avoid
/*
* Note: not safe to modify input list destructively, but we can avoid
* copying the list if we're not actually going to change it
*/
if (nuseful == length(pathkeys))

View File

@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.103 2001/01/24 19:42:58 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.104 2001/03/22 03:59:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -42,19 +42,19 @@ static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
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);
List *tlist, List *scan_clauses);
static NestLoop *create_nestloop_plan(NestPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static MergeJoin *create_mergejoin_plan(MergePath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static HashJoin *create_hashjoin_plan(HashPath *best_path, List *tlist,
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
List *joinclauses, List *otherclauses,
Plan *outer_plan, List *outer_tlist,
Plan *inner_plan, List *inner_tlist);
static List *fix_indxqual_references(List *indexquals, IndexPath *index_path);
static List *fix_indxqual_sublist(List *indexqual, int baserelid, Oid relam,
Form_pg_index index);
@ -72,20 +72,20 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval);
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static HashJoin *make_hashjoin(List *tlist,
List *joinclauses, List *otherclauses,
List *hashclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
List *joinclauses, List *otherclauses,
List *hashclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static Hash *make_hash(List *tlist, Node *hashkey, Plan *lefttree);
static MergeJoin *make_mergejoin(List *tlist,
List *joinclauses, List *otherclauses,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
List *joinclauses, List *otherclauses,
List *mergeclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
/*
* create_plan
@ -313,8 +313,8 @@ create_append_plan(Query *root, AppendPath *best_path)
foreach(subpaths, best_path->subpaths)
{
Path *subpath = (Path *) lfirst(subpaths);
Path *subpath = (Path *) lfirst(subpaths);
subplans = lappend(subplans, create_plan(root, subpath));
}
@ -344,7 +344,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->issubquery);
scan_relid = (Index) lfirsti(best_path->parent->relids);
@ -386,7 +386,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->issubquery);
baserelid = lfirsti(best_path->path.parent->relids);
@ -496,7 +496,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->issubquery);
scan_relid = (Index) lfirsti(best_path->path.parent->relids);
@ -737,21 +737,22 @@ create_mergejoin_plan(MergePath *best_path,
best_path->innersortkeys);
/*
* The executor requires the inner side of a mergejoin to support "mark"
* and "restore" operations. Not all plan types do, so we must be careful
* not to generate an invalid plan. If necessary, an invalid inner plan
* can be handled by inserting a Materialize node.
* The executor requires the inner side of a mergejoin to support
* "mark" and "restore" operations. Not all plan types do, so we must
* be careful not to generate an invalid plan. If necessary, an
* invalid inner plan can be handled by inserting a Materialize node.
*
* Since the inner side must be ordered, and only Sorts and IndexScans can
* create order to begin with, you might think there's no problem --- but
* you'd be wrong. Nestloop and merge joins can *preserve* the order of
* their inputs, so they can be selected as the input of a mergejoin,
* and that won't work in the present executor.
* Since the inner side must be ordered, and only Sorts and IndexScans
* can create order to begin with, you might think there's no problem
* --- but you'd be wrong. Nestloop and merge joins can *preserve*
* the order of their inputs, so they can be selected as the input of
* a mergejoin, and that won't work in the present executor.
*
* Doing this here is a bit of a kluge since the cost of the Materialize
* wasn't taken into account in our earlier decisions. But Materialize
* is hard to estimate a cost for, and the above consideration shows that
* this is a rare case anyway, so this seems an acceptable way to proceed.
* wasn't taken into account in our earlier decisions. But
* Materialize is hard to estimate a cost for, and the above
* consideration shows that this is a rare case anyway, so this seems
* an acceptable way to proceed.
*
* This check must agree with ExecMarkPos/ExecRestrPos in
* executor/execAmi.c!
@ -1015,6 +1016,7 @@ static Node *
fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
Oid *opclass)
{
/*
* Remove any binary-compatible relabeling of the indexkey
*/
@ -1025,8 +1027,8 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
* We represent index keys by Var nodes having the varno of the base
* table but varattno equal to the index's attribute number (index
* column position). This is a bit hokey ... would be cleaner to use
* a special-purpose node type that could not be mistaken for a regular
* Var. But it will do for now.
* a special-purpose node type that could not be mistaken for a
* regular Var. But it will do for now.
*/
if (IsA(node, Var))
{
@ -1062,7 +1064,7 @@ fix_indxqual_operand(Node *node, int baserelid, Form_pg_index index,
* the returned varattno must be 1.
*/
Assert(is_funcclause(node)); /* not a very thorough check, but easy */
Assert(is_funcclause(node));/* not a very thorough check, but easy */
/* indclass[0] is the only class of a functional index */
*opclass = index->indclass[0];
@ -1493,7 +1495,7 @@ make_sort_from_pathkeys(List *tlist, Plan *lefttree, List *pathkeys)
return make_sort(sort_tlist, lefttree, numsortkeys);
}
Material *
Material *
make_material(List *tlist, Plan *lefttree)
{
Material *node = makeNode(Material);
@ -1734,10 +1736,10 @@ make_limit(List *tlist, Plan *lefttree,
copy_plan_costsize(plan, lefttree);
/*
* If offset/count are constants, adjust the output rows count and costs
* accordingly. This is only a cosmetic issue if we are at top level,
* but if we are building a subquery then it's important to report
* correct info to the outer planner.
* If offset/count are constants, adjust the output rows count and
* costs accordingly. This is only a cosmetic issue if we are at top
* level, but if we are building a subquery then it's important to
* report correct info to the outer planner.
*/
if (limitOffset && IsA(limitOffset, Const))
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.57 2001/02/16 03:16:57 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.58 2001/03/22 03:59:36 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,11 +35,11 @@
static void mark_baserels_for_outer_join(Query *root, Relids rels,
Relids outerrels);
Relids outerrels);
static void distribute_qual_to_rels(Query *root, Node *clause,
bool ispusheddown,
bool isouterjoin,
Relids qualscope);
bool ispusheddown,
bool isouterjoin,
Relids qualscope);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
Relids join_relids);
static void add_vars_to_targetlist(Query *root, List *vars);
@ -57,7 +57,7 @@ static void check_hashjoinable(RestrictInfo *restrictinfo);
* build_base_rel_tlists
* Creates rel nodes for every relation mentioned in the target list
* 'tlist' (if a node hasn't already been created) and adds them to
* root->base_rel_list. Creates targetlist entries for each var seen
* root->base_rel_list. Creates targetlist entries for each var seen
* in 'tlist' and adds them to the tlist of the appropriate rel node.
*/
void
@ -118,6 +118,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
if (IsA(jtnode, RangeTblRef))
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
/* This call to get_base_rel does the primary work... */
RelOptInfo *rel = get_base_rel(root, varno);
@ -160,7 +161,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* distribute_quals_to_rels
* Recursively scan the query's join tree for WHERE and JOIN/ON qual
* clauses, and add these to the appropriate RestrictInfo and JoinInfo
* lists belonging to base RelOptInfos. New base rel entries are created
* lists belonging to base RelOptInfos. New base rel entries are created
* as needed. Also, base RelOptInfos are marked with outerjoinset
* information, to aid in proper positioning of qual clauses that appear
* above outer joins.
@ -169,7 +170,7 @@ add_missing_rels_to_query(Query *root, Node *jtnode)
* be evaluated at the lowest level where all the variables it mentions are
* available. However, we cannot push a qual down into the nullable side(s)
* of an outer join since the qual might eliminate matching rows and cause a
* NULL row to be incorrectly emitted by the join. Therefore, rels appearing
* NULL row to be incorrectly emitted by the join. Therefore, rels appearing
* within the nullable side(s) of an outer join are marked with
* outerjoinset = list of Relids used at the outer join node.
* This list will be added to the list of rels referenced by quals using such
@ -228,14 +229,14 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
List *qual;
/*
* Order of operations here is subtle and critical. First we recurse
* to handle sub-JOINs. Their join quals will be placed without
* regard for whether this level is an outer join, which is correct.
* Then, if we are an outer join, we mark baserels contained within
* the nullable side(s) with our own rel list; this will restrict
* placement of subsequent quals using those rels, including our own
* quals and quals above us in the join tree.
* Finally we place our own join quals.
* Order of operations here is subtle and critical. First we
* recurse to handle sub-JOINs. Their join quals will be placed
* without regard for whether this level is an outer join, which
* is correct. Then, if we are an outer join, we mark baserels
* contained within the nullable side(s) with our own rel list;
* this will restrict placement of subsequent quals using those
* rels, including our own quals and quals above us in the join
* tree. Finally we place our own join quals.
*/
leftids = distribute_quals_to_rels(root, j->larg);
rightids = distribute_quals_to_rels(root, j->rarg);
@ -261,9 +262,10 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
isouterjoin = true;
break;
case JOIN_UNION:
/*
* This is where we fail if upper levels of planner haven't
* rewritten UNION JOIN as an Append ...
* This is where we fail if upper levels of planner
* haven't rewritten UNION JOIN as an Append ...
*/
elog(ERROR, "UNION JOIN is not implemented yet");
break;
@ -338,12 +340,12 @@ distribute_qual_to_rels(Query *root, Node *clause,
bool can_be_equijoin;
restrictinfo->clause = (Expr *) clause;
restrictinfo->eval_cost = -1; /* not computed until needed */
restrictinfo->eval_cost = -1; /* not computed until needed */
restrictinfo->subclauseindices = NIL;
restrictinfo->mergejoinoperator = InvalidOid;
restrictinfo->left_sortop = InvalidOid;
restrictinfo->right_sortop = InvalidOid;
restrictinfo->left_pathkey = NIL; /* not computable yet */
restrictinfo->left_pathkey = NIL; /* not computable yet */
restrictinfo->right_pathkey = NIL;
restrictinfo->hashjoinoperator = InvalidOid;
restrictinfo->left_dispersion = -1; /* not computed until needed */
@ -358,7 +360,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
* Cross-check: clause should contain no relids not within its scope.
* Otherwise the parser messed up.
*/
if (! is_subseti(relids, qualscope))
if (!is_subseti(relids, qualscope))
elog(ERROR, "JOIN qualification may not refer to other relations");
/*
@ -377,14 +379,14 @@ distribute_qual_to_rels(Query *root, Node *clause,
* This ensures that the clause will be evaluated exactly at the level
* of joining corresponding to the outer join.
*
* For a non-outer-join qual, we can evaluate the qual as soon as
* (1) we have all the rels it mentions, and (2) we are at or above any
* outer joins that can null any of these rels and are below the syntactic
* location of the given qual. To enforce the latter, scan the base rels
* listed in relids, and merge their outer-join lists into the clause's
* own reference list. At the time we are called, the outerjoinset list
* of each baserel will show exactly those outer joins that are below the
* qual in the join tree.
* For a non-outer-join qual, we can evaluate the qual as soon as (1) we
* have all the rels it mentions, and (2) we are at or above any outer
* joins that can null any of these rels and are below the syntactic
* location of the given qual. To enforce the latter, scan the base
* rels listed in relids, and merge their outer-join lists into the
* clause's own reference list. At the time we are called, the
* outerjoinset list of each baserel will show exactly those outer
* joins that are below the qual in the join tree.
*/
if (isouterjoin)
{
@ -396,19 +398,24 @@ distribute_qual_to_rels(Query *root, Node *clause,
Relids newrelids = relids;
List *relid;
/* We rely on set_unioni to be nondestructive of its input lists... */
/*
* We rely on set_unioni to be nondestructive of its input
* lists...
*/
can_be_equijoin = true;
foreach(relid, relids)
{
RelOptInfo *rel = get_base_rel(root, lfirsti(relid));
if (rel->outerjoinset &&
! is_subseti(rel->outerjoinset, relids))
!is_subseti(rel->outerjoinset, relids))
{
newrelids = set_unioni(newrelids, rel->outerjoinset);
/*
* Because application of the qual will be delayed by outer
* join, we mustn't assume its vars are equal everywhere.
* Because application of the qual will be delayed by
* outer join, we mustn't assume its vars are equal
* everywhere.
*/
can_be_equijoin = false;
}
@ -419,10 +426,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
}
/*
* Mark the qual as "pushed down" if it can be applied at a level below
* its original syntactic level. This allows us to distinguish original
* JOIN/ON quals from higher-level quals pushed down to the same joinrel.
* A qual originating from WHERE is always considered "pushed down".
* Mark the qual as "pushed down" if it can be applied at a level
* below its original syntactic level. This allows us to distinguish
* original JOIN/ON quals from higher-level quals pushed down to the
* same joinrel. A qual originating from WHERE is always considered
* "pushed down".
*/
restrictinfo->ispusheddown = ispusheddown || !sameseti(relids,
qualscope);
@ -458,10 +466,10 @@ distribute_qual_to_rels(Query *root, Node *clause,
* the relid list. Set additional RestrictInfo fields for
* joining.
*
* We don't bother setting the merge/hashjoin info if we're not
* going to need it. We do want to know about mergejoinable ops
* in any potential equijoin clause (see later in this routine),
* and we ignore enable_mergejoin if isouterjoin is true, because
* We don't bother setting the merge/hashjoin info if we're not going
* to need it. We do want to know about mergejoinable ops in any
* potential equijoin clause (see later in this routine), and we
* ignore enable_mergejoin if isouterjoin is true, because
* mergejoin is the only implementation we have for full and right
* outer joins.
*/
@ -485,6 +493,7 @@ distribute_qual_to_rels(Query *root, Node *clause,
}
else
{
/*
* 'clause' references no rels, and therefore we have no place to
* attach it. Shouldn't get here if callers are working properly.
@ -493,12 +502,12 @@ distribute_qual_to_rels(Query *root, Node *clause,
}
/*
* If the clause has a mergejoinable operator, and is not an outer-join
* qualification nor bubbled up due to an outer join, then the two sides
* represent equivalent PathKeyItems for path keys: any path that is
* sorted by one side will also be sorted by the other (as soon as the
* two rels are joined, that is). Record the key equivalence for future
* use.
* If the clause has a mergejoinable operator, and is not an
* outer-join qualification nor bubbled up due to an outer join, then
* the two sides represent equivalent PathKeyItems for path keys: any
* path that is sorted by one side will also be sorted by the other
* (as soon as the two rels are joined, that is). Record the key
* equivalence for future use.
*/
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
add_equijoined_keys(root, restrictinfo);
@ -569,15 +578,16 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
Expr *clause;
/*
* Currently, since check_mergejoinable only accepts Var = Var clauses,
* we should only see Var nodes here. Would have to work a little
* harder to locate the right rel(s) if more-general mergejoin clauses
* were accepted.
* Currently, since check_mergejoinable only accepts Var = Var
* clauses, we should only see Var nodes here. Would have to work a
* little harder to locate the right rel(s) if more-general mergejoin
* clauses were accepted.
*/
Assert(IsA(item1, Var));
irel1 = ((Var *) item1)->varno;
Assert(IsA(item2, Var));
irel2 = ((Var *) item2)->varno;
/*
* If both vars belong to same rel, we need to look at that rel's
* baserestrictinfo list. If different rels, each will have a
@ -593,6 +603,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
restrictlist = joininfo->jinfo_restrictinfo;
}
/*
* Scan to see if equality is already known.
*/
@ -611,6 +622,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
(equal(item2, left) && equal(item1, right)))
return; /* found a matching clause */
}
/*
* This equality is new information, so construct a clause
* representing it to add to the query data structures.
@ -620,6 +632,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
eq_operator = compatible_oper("=", ltype, rtype, true);
if (!HeapTupleIsValid(eq_operator))
{
/*
* Would it be safe to just not add the equality to the query if
* we have no suitable equality operator for the combination of
@ -629,6 +642,7 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
typeidTypeName(ltype), typeidTypeName(rtype));
}
pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
/*
* Let's just make sure this appears to be a compatible operator.
*/
@ -641,21 +655,21 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
clause = makeNode(Expr);
clause->typeOid = BOOLOID;
clause->opType = OP_EXPR;
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
InvalidOid, /* opid */
BOOLOID); /* operator result type */
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
InvalidOid, /* opid */
BOOLOID); /* operator result type */
clause->args = makeList2(item1, item2);
ReleaseSysCache(eq_operator);
/*
* Note: we mark the qual "pushed down" to ensure that it can never be
* taken for an original JOIN/ON clause. We also claim it is an outer-
* join clause, which it isn't, but that keeps distribute_qual_to_rels
* from examining the outerjoinsets of the relevant rels (which are no
* longer of interest, but could keep the qual from being pushed down
* to where it should be). It'll also save a useless call to
* add_equijoined keys...
* taken for an original JOIN/ON clause. We also claim it is an
* outer- join clause, which it isn't, but that keeps
* distribute_qual_to_rels from examining the outerjoinsets of the
* relevant rels (which are no longer of interest, but could keep the
* qual from being pushed down to where it should be). It'll also
* save a useless call to add_equijoined keys...
*/
distribute_qual_to_rels(root, (Node *) clause,
true, true,

View File

@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.63 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.64 2001/03/22 03:59:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,7 +33,7 @@
static Plan *subplanner(Query *root, List *flat_tlist,
double tuple_fraction);
double tuple_fraction);
/*--------------------
@ -82,7 +82,7 @@ query_planner(Query *root,
/*
* If the query has an empty join tree, then it's something easy like
* "SELECT 2+2;" or "INSERT ... VALUES()". Fall through quickly.
* "SELECT 2+2;" or "INSERT ... VALUES()". Fall through quickly.
*/
if (root->jointree->fromlist == NIL)
{
@ -213,9 +213,9 @@ subplanner(Query *root,
foreach(brel, root->base_rel_list)
{
RelOptInfo *baserel = (RelOptInfo *) lfirst(brel);
int relid = lfirsti(baserel->relids);
int relid = lfirsti(baserel->relids);
if (! ptrMember(baserel, joined_rels))
if (!ptrMember(baserel, joined_rels))
elog(ERROR, "Internal error: no jointree entry for rel %s (%d)",
rt_fetch(relid, root->rtable)->eref->relname, relid);
}
@ -334,9 +334,9 @@ subplanner(Query *root,
/*
* Nothing for it but to sort the cheapest-total-cost path --- but we
* let the caller do that. grouping_planner has to be able to add a
* sort node anyway, so no need for extra code here. (Furthermore, the
* given pathkeys might involve something we can't compute here, such
* as an aggregate function...)
* sort node anyway, so no need for extra code here. (Furthermore,
* the given pathkeys might involve something we can't compute here,
* such as an aggregate function...)
*/
root->query_pathkeys = cheapestpath->pathkeys;
resultplan = create_plan(root, cheapestpath);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.101 2001/01/27 04:42:32 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.102 2001/03/22 03:59:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,9 +33,9 @@
/* Expression kind codes for preprocess_expression */
#define EXPRKIND_TARGET 0
#define EXPRKIND_TARGET 0
#define EXPRKIND_WHERE 1
#define EXPRKIND_HAVING 2
#define EXPRKIND_HAVING 2
static Node *pull_up_subqueries(Query *parse, Node *jtnode);
@ -68,16 +68,16 @@ planner(Query *parse)
/*
* The planner can be called recursively (an example is when
* eval_const_expressions tries to pre-evaluate an SQL function).
* So, these global state variables must be saved and restored.
* eval_const_expressions tries to pre-evaluate an SQL function). So,
* these global state variables must be saved and restored.
*
* These vars cannot be moved into the Query structure since their
* whole purpose is communication across multiple sub-Queries.
* These vars cannot be moved into the Query structure since their whole
* purpose is communication across multiple sub-Queries.
*
* Note we do NOT save and restore PlannerPlanId: it exists to assign
* unique IDs to SubPlan nodes, and we want those IDs to be unique
* for the life of a backend. Also, PlannerInitPlan is saved/restored
* in subquery_planner, not here.
* unique IDs to SubPlan nodes, and we want those IDs to be unique for
* the life of a backend. Also, PlannerInitPlan is saved/restored in
* subquery_planner, not here.
*/
save_PlannerQueryLevel = PlannerQueryLevel;
save_PlannerParamVar = PlannerParamVar;
@ -150,6 +150,7 @@ subquery_planner(Query *parse, double tuple_fraction)
*/
parse->jointree = (FromExpr *)
pull_up_subqueries(parse, (Node *) parse->jointree);
/*
* If so, we may have created opportunities to simplify the jointree.
*/
@ -170,26 +171,26 @@ subquery_planner(Query *parse, double tuple_fraction)
/*
* A HAVING clause without aggregates is equivalent to a WHERE clause
* (except it can only refer to grouped fields). Transfer any agg-free
* clauses of the HAVING qual into WHERE. This may seem like wasting
* cycles to cater to stupidly-written queries, but there are other
* reasons for doing it. Firstly, if the query contains no aggs at all,
* then we aren't going to generate an Agg plan node, and so there'll be
* no place to execute HAVING conditions; without this transfer, we'd
* lose the HAVING condition entirely, which is wrong. Secondly, when
* we push down a qual condition into a sub-query, it's easiest to push
* the qual into HAVING always, in case it contains aggs, and then let
* this code sort it out.
* (except it can only refer to grouped fields). Transfer any
* agg-free clauses of the HAVING qual into WHERE. This may seem like
* wasting cycles to cater to stupidly-written queries, but there are
* other reasons for doing it. Firstly, if the query contains no aggs
* at all, then we aren't going to generate an Agg plan node, and so
* there'll be no place to execute HAVING conditions; without this
* transfer, we'd lose the HAVING condition entirely, which is wrong.
* Secondly, when we push down a qual condition into a sub-query, it's
* easiest to push the qual into HAVING always, in case it contains
* aggs, and then let this code sort it out.
*
* Note that both havingQual and parse->jointree->quals are in
* implicitly-ANDed-list form at this point, even though they are
* declared as Node *. Also note that contain_agg_clause does not
* declared as Node *. Also note that contain_agg_clause does not
* recurse into sub-selects, which is exactly what we need here.
*/
newHaving = NIL;
foreach(lst, (List *) parse->havingQual)
{
Node *havingclause = (Node *) lfirst(lst);
Node *havingclause = (Node *) lfirst(lst);
if (contain_agg_clause(havingclause))
newHaving = lappend(newHaving, havingclause);
@ -201,30 +202,32 @@ subquery_planner(Query *parse, double tuple_fraction)
/*
* Do the main planning. If we have an inherited target relation,
* that needs special processing, else go straight to grouping_planner.
* that needs special processing, else go straight to
* grouping_planner.
*/
if (parse->resultRelation &&
(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
(lst = expand_inherted_rtentry(parse, parse->resultRelation)) != NIL)
plan = inheritance_planner(parse, lst);
else
plan = grouping_planner(parse, tuple_fraction);
/*
* If any subplans were generated, or if we're inside a subplan,
* build subPlan, extParam and locParam lists for plan nodes.
* If any subplans were generated, or if we're inside a subplan, build
* subPlan, extParam and locParam lists for plan nodes.
*/
if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1)
{
(void) SS_finalize_plan(plan);
/*
* At the moment, SS_finalize_plan doesn't handle initPlans
* and so we assign them to the topmost plan node.
* At the moment, SS_finalize_plan doesn't handle initPlans and so
* we assign them to the topmost plan node.
*/
plan->initPlan = PlannerInitPlan;
/* Must add the initPlans' extParams to the topmost node's, too */
foreach(lst, plan->initPlan)
{
SubPlan *subplan = (SubPlan *) lfirst(lst);
SubPlan *subplan = (SubPlan *) lfirst(lst);
plan->extParam = set_unioni(plan->extParam,
subplan->plan->extParam);
@ -266,44 +269,47 @@ pull_up_subqueries(Query *parse, Node *jtnode)
Query *subquery = rte->subquery;
/*
* Is this a subquery RTE, and if so, is the subquery simple enough
* to pull up? (If not, do nothing at this node.)
* Is this a subquery RTE, and if so, is the subquery simple
* enough to pull up? (If not, do nothing at this node.)
*/
if (subquery && is_simple_subquery(subquery))
{
int rtoffset;
Node *subjointree;
List *subtlist;
List *l;
int rtoffset;
Node *subjointree;
List *subtlist;
List *l;
/*
* First, recursively pull up the subquery's subqueries,
* so that this routine's processing is complete for its
* jointree and rangetable. NB: if the same subquery is
* referenced from multiple jointree items (which can't happen
* normally, but might after rule rewriting), then we will invoke
* this processing multiple times on that subquery. OK because
* First, recursively pull up the subquery's subqueries, so
* that this routine's processing is complete for its jointree
* and rangetable. NB: if the same subquery is referenced
* from multiple jointree items (which can't happen normally,
* but might after rule rewriting), then we will invoke this
* processing multiple times on that subquery. OK because
* nothing will happen after the first time. We do have to be
* careful to copy everything we pull up, however, or risk
* having chunks of structure multiply linked.
*/
subquery->jointree = (FromExpr *)
pull_up_subqueries(subquery, (Node *) subquery->jointree);
/*
* Append the subquery's rangetable to mine (currently,
* no adjustments will be needed in the subquery's rtable).
* Append the subquery's rangetable to mine (currently, no
* adjustments will be needed in the subquery's rtable).
*/
rtoffset = length(parse->rtable);
parse->rtable = nconc(parse->rtable,
copyObject(subquery->rtable));
/*
* Make copies of the subquery's jointree and targetlist
* with varnos adjusted to match the merged rangetable.
* Make copies of the subquery's jointree and targetlist with
* varnos adjusted to match the merged rangetable.
*/
subjointree = copyObject(subquery->jointree);
OffsetVarNodes(subjointree, rtoffset, 0);
subtlist = copyObject(subquery->targetList);
OffsetVarNodes((Node *) subtlist, rtoffset, 0);
/*
* Replace all of the top query's references to the subquery's
* outputs with copies of the adjusted subtlist items, being
@ -316,16 +322,18 @@ pull_up_subqueries(Query *parse, Node *jtnode)
parse->havingQual =
ResolveNew(parse->havingQual,
varno, 0, subtlist, CMD_SELECT, 0);
/*
* Pull up any FOR UPDATE markers, too.
*/
foreach(l, subquery->rowMarks)
{
int submark = lfirsti(l);
int submark = lfirsti(l);
parse->rowMarks = lappendi(parse->rowMarks,
submark + rtoffset);
}
/*
* Miscellaneous housekeeping.
*/
@ -345,9 +353,7 @@ pull_up_subqueries(Query *parse, Node *jtnode)
List *l;
foreach(l, f->fromlist)
{
lfirst(l) = pull_up_subqueries(parse, lfirst(l));
}
}
else if (IsA(jtnode, JoinExpr))
{
@ -370,6 +376,7 @@ pull_up_subqueries(Query *parse, Node *jtnode)
static bool
is_simple_subquery(Query *subquery)
{
/*
* Let's just make sure it's a valid subselect ...
*/
@ -379,12 +386,14 @@ is_simple_subquery(Query *subquery)
subquery->into != NULL ||
subquery->isPortal)
elog(ERROR, "is_simple_subquery: subquery is bogus");
/*
* Can't currently pull up a query with setops.
* Maybe after querytree redesign...
* Can't currently pull up a query with setops. Maybe after querytree
* redesign...
*/
if (subquery->setOperations)
return false;
/*
* Can't pull up a subquery involving grouping, aggregation, sorting,
* or limiting.
@ -397,12 +406,13 @@ is_simple_subquery(Query *subquery)
subquery->limitOffset ||
subquery->limitCount)
return false;
/*
* Hack: don't try to pull up a subquery with an empty jointree.
* query_planner() will correctly generate a Result plan for a
* jointree that's totally empty, but I don't think the right things
* happen if an empty FromExpr appears lower down in a jointree.
* Not worth working hard on this, just to collapse SubqueryScan/Result
* happen if an empty FromExpr appears lower down in a jointree. Not
* worth working hard on this, just to collapse SubqueryScan/Result
* into Result...
*/
if (subquery->jointree->fromlist == NIL)
@ -443,7 +453,9 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
resolvenew_in_jointree(j->rarg, varno, subtlist);
j->quals = ResolveNew(j->quals,
varno, 0, subtlist, CMD_SELECT, 0);
/* We don't bother to update the colvars list, since it won't be
/*
* We don't bother to update the colvars list, since it won't be
* used again ...
*/
}
@ -458,13 +470,13 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
*
* If we succeed in pulling up a subquery then we might form a jointree
* in which a FromExpr is a direct child of another FromExpr. In that
* case we can consider collapsing the two FromExprs into one. This is
* case we can consider collapsing the two FromExprs into one. This is
* an optional conversion, since the planner will work correctly either
* way. But we may find a better plan (at the cost of more planning time)
* if we merge the two nodes.
*
* NOTE: don't try to do this in the same jointree scan that does subquery
* pullup! Since we're changing the jointree structure here, that wouldn't
* pullup! Since we're changing the jointree structure here, that wouldn't
* work reliably --- see comments for pull_up_subqueries().
*/
static Node *
@ -484,27 +496,29 @@ preprocess_jointree(Query *parse, Node *jtnode)
foreach(l, f->fromlist)
{
Node *child = (Node *) lfirst(l);
Node *child = (Node *) lfirst(l);
/* Recursively simplify the child... */
child = preprocess_jointree(parse, child);
/* Now, is it a FromExpr? */
if (child && IsA(child, FromExpr))
{
/*
* Yes, so do we want to merge it into parent? Always do so
* if child has just one element (since that doesn't make the
* parent's list any longer). Otherwise we have to be careful
* about the increase in planning time caused by combining the
* two join search spaces into one. Our heuristic is to merge
* if the merge will produce a join list no longer than
* GEQO_RELS/2. (Perhaps need an additional user parameter?)
* Yes, so do we want to merge it into parent? Always do
* so if child has just one element (since that doesn't
* make the parent's list any longer). Otherwise we have
* to be careful about the increase in planning time
* caused by combining the two join search spaces into
* one. Our heuristic is to merge if the merge will
* produce a join list no longer than GEQO_RELS/2.
* (Perhaps need an additional user parameter?)
*/
FromExpr *subf = (FromExpr *) child;
int childlen = length(subf->fromlist);
int myothers = length(newlist) + length(lnext(l));
if (childlen <= 1 || (childlen+myothers) <= geqo_rels/2)
if (childlen <= 1 || (childlen + myothers) <= geqo_rels / 2)
{
newlist = nconc(newlist, subf->fromlist);
f->quals = make_and_qual(f->quals, subf->quals);
@ -540,6 +554,7 @@ preprocess_jointree(Query *parse, Node *jtnode)
static Node *
preprocess_expression(Query *parse, Node *expr, int kind)
{
/*
* Simplify constant expressions.
*
@ -551,8 +566,8 @@ preprocess_expression(Query *parse, Node *expr, int kind)
expr = eval_const_expressions(expr);
/*
* If it's a qual or havingQual, canonicalize it, and convert it
* to implicit-AND format.
* If it's a qual or havingQual, canonicalize it, and convert it to
* implicit-AND format.
*
* XXX Is there any value in re-applying eval_const_expressions after
* canonicalize_qual?
@ -575,10 +590,11 @@ preprocess_expression(Query *parse, Node *expr, int kind)
if (kind != EXPRKIND_WHERE &&
(parse->groupClause != NIL || parse->hasAggs))
{
/*
* Check for ungrouped variables passed to subplans. Note we
* do NOT do this for subplans in WHERE (or JOIN/ON); it's legal
* there because WHERE is evaluated pre-GROUP.
* do NOT do this for subplans in WHERE (or JOIN/ON); it's
* legal there because WHERE is evaluated pre-GROUP.
*/
check_subplans_for_ungrouped_vars(expr, parse);
}
@ -635,12 +651,12 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
* inheritance set.
*
* We have to handle this case differently from cases where a source
* relation is an inheritance set. Source inheritance is expanded at
* relation is an inheritance set. Source inheritance is expanded at
* the bottom of the plan tree (see allpaths.c), but target inheritance
* has to be expanded at the top. The reason is that for UPDATE, each
* target relation needs a different targetlist matching its own column
* set. (This is not so critical for DELETE, but for simplicity we treat
* inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target
* inherited DELETE the same way.) Fortunately, the UPDATE/DELETE target
* can never be the nullable side of an outer join, so it's OK to generate
* the plan this way.
*
@ -661,17 +677,17 @@ inheritance_planner(Query *parse, List *inheritlist)
foreach(l, inheritlist)
{
int childRTindex = lfirsti(l);
Oid childOID = getrelid(childRTindex, parse->rtable);
Query *subquery;
Plan *subplan;
int childRTindex = lfirsti(l);
Oid childOID = getrelid(childRTindex, parse->rtable);
Query *subquery;
Plan *subplan;
/* Generate modified query with this rel as target */
subquery = (Query *) adjust_inherited_attrs((Node *) parse,
parentRTindex, parentOID,
childRTindex, childOID);
parentRTindex, parentOID,
childRTindex, childOID);
/* Generate plan */
subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */);
subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ );
subplans = lappend(subplans, subplan);
/* Save preprocessed tlist from first rel for use in Append */
if (tlist == NIL)
@ -718,6 +734,7 @@ grouping_planner(Query *parse, double tuple_fraction)
if (parse->setOperations)
{
/*
* Construct the plan for set operations. The result will not
* need any work except perhaps a top-level sort and/or LIMIT.
@ -736,17 +753,17 @@ grouping_planner(Query *parse, double tuple_fraction)
tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);
/*
* Can't handle FOR UPDATE here (parser should have checked already,
* but let's make sure).
* Can't handle FOR UPDATE here (parser should have checked
* already, but let's make sure).
*/
if (parse->rowMarks)
elog(ERROR, "SELECT FOR UPDATE is not allowed with UNION/INTERSECT/EXCEPT");
/*
* We set current_pathkeys NIL indicating we do not know sort
* order. This is correct when the top set operation is UNION ALL,
* since the appended-together results are unsorted even if the
* subplans were sorted. For other set operations we could be
* order. This is correct when the top set operation is UNION
* ALL, since the appended-together results are unsorted even if
* the subplans were sorted. For other set operations we could be
* smarter --- room for future improvement!
*/
current_pathkeys = NIL;
@ -772,22 +789,26 @@ grouping_planner(Query *parse, double tuple_fraction)
/*
* Add TID targets for rels selected FOR UPDATE (should this be
* done in preprocess_targetlist?). The executor uses the TID
* to know which rows to lock, much as for UPDATE or DELETE.
* done in preprocess_targetlist?). The executor uses the TID to
* know which rows to lock, much as for UPDATE or DELETE.
*/
if (parse->rowMarks)
{
List *l;
/*
* We've got trouble if the FOR UPDATE appears inside grouping,
* since grouping renders a reference to individual tuple CTIDs
* invalid. This is also checked at parse time, but that's
* insufficient because of rule substitution, query pullup, etc.
* We've got trouble if the FOR UPDATE appears inside
* grouping, since grouping renders a reference to individual
* tuple CTIDs invalid. This is also checked at parse time,
* but that's insufficient because of rule substitution, query
* pullup, etc.
*/
CheckSelectForUpdate(parse);
/* Currently the executor only supports FOR UPDATE at top level */
/*
* Currently the executor only supports FOR UPDATE at top
* level
*/
if (PlannerQueryLevel > 1)
elog(ERROR, "SELECT FOR UPDATE is not allowed in subselects");
@ -873,9 +894,9 @@ grouping_planner(Query *parse, double tuple_fraction)
int32 count = DatumGetInt32(limitc->constvalue);
/*
* A NULL-constant LIMIT represents "LIMIT ALL",
* which we treat the same as no limit (ie,
* expect to retrieve all the tuples).
* A NULL-constant LIMIT represents "LIMIT ALL", which
* we treat the same as no limit (ie, expect to
* retrieve all the tuples).
*/
if (!limitc->constisnull && count > 0)
{
@ -902,17 +923,19 @@ grouping_planner(Query *parse, double tuple_fraction)
}
else
{
/*
* COUNT is an expression ... don't know exactly what the
* limit will be, but for lack of a better idea assume
* 10% of the plan's result is wanted.
* COUNT is an expression ... don't know exactly what
* the limit will be, but for lack of a better idea
* assume 10% of the plan's result is wanted.
*/
tuple_fraction = 0.10;
}
}
/*
* If no LIMIT, check for retrieve-into-portal, ie DECLARE CURSOR.
* If no LIMIT, check for retrieve-into-portal, ie DECLARE
* CURSOR.
*
* We have no real idea how many tuples the user will ultimately
* FETCH from a cursor, but it seems a good bet that he

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.70 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.71 2001/03/22 03:59:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -97,16 +97,17 @@ set_plan_references(Plan *plan)
fix_expr_references(plan,
(Node *) ((IndexScan *) plan)->indxqual);
fix_expr_references(plan,
(Node *) ((IndexScan *) plan)->indxqualorig);
(Node *) ((IndexScan *) plan)->indxqualorig);
break;
case T_TidScan:
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
break;
case T_SubqueryScan:
/*
* We do not do set_uppernode_references() here, because
* a SubqueryScan will always have been created with correct
* We do not do set_uppernode_references() here, because a
* SubqueryScan will always have been created with correct
* references to its subplan's outputs to begin with.
*/
fix_expr_references(plan, (Node *) plan->targetlist);
@ -126,7 +127,7 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
fix_expr_references(plan,
(Node *) ((MergeJoin *) plan)->mergeclauses);
(Node *) ((MergeJoin *) plan)->mergeclauses);
break;
case T_HashJoin:
set_join_references((Join *) plan);
@ -134,7 +135,7 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->qual);
fix_expr_references(plan, (Node *) ((Join *) plan)->joinqual);
fix_expr_references(plan,
(Node *) ((HashJoin *) plan)->hashclauses);
(Node *) ((HashJoin *) plan)->hashclauses);
break;
case T_Material:
case T_Sort:
@ -148,10 +149,10 @@ set_plan_references(Plan *plan)
* targetlists or quals (because they just return their
* unmodified input tuples). The optimizer is lazy about
* creating really valid targetlists for them. Best to just
* leave the targetlist alone. In particular, we do not want
* leave the targetlist alone. In particular, we do not want
* to pull a subplan list for them, since we will likely end
* up with duplicate list entries for subplans that also appear
* in lower levels of the plan tree!
* up with duplicate list entries for subplans that also
* appear in lower levels of the plan tree!
*/
break;
case T_Agg:
@ -175,11 +176,12 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, ((Result *) plan)->resconstantqual);
break;
case T_Append:
/*
* Append, like Sort et al, doesn't actually evaluate its
* targetlist or quals, and we haven't bothered to give it
* its own tlist copy. So, don't fix targetlist/qual.
* But do recurse into subplans.
* targetlist or quals, and we haven't bothered to give it its
* own tlist copy. So, don't fix targetlist/qual. But do
* recurse into subplans.
*/
foreach(pl, ((Append *) plan)->appendplans)
set_plan_references((Plan *) lfirst(pl));
@ -296,7 +298,7 @@ set_uppernode_references(Plan *plan, Index subvarno)
subplanTargetList = NIL;
outputTargetList = NIL;
foreach (l, plan->targetlist)
foreach(l, plan->targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
TargetEntry *subplantle;
@ -306,8 +308,8 @@ set_uppernode_references(Plan *plan, Index subvarno)
if (subplantle)
{
/* Found a matching subplan output expression */
Resdom *resdom = subplantle->resdom;
Var *newvar;
Resdom *resdom = subplantle->resdom;
Var *newvar;
newvar = makeVar(subvarno,
resdom->resno,
@ -317,7 +319,7 @@ set_uppernode_references(Plan *plan, Index subvarno)
/* If we're just copying a simple Var, copy up original info */
if (subplantle->expr && IsA(subplantle->expr, Var))
{
Var *subvar = (Var *) subplantle->expr;
Var *subvar = (Var *) subplantle->expr;
newvar->varnoold = subvar->varnoold;
newvar->varoattno = subvar->varoattno;

View File

@ -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.48 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.49 2001/03/22 03:59:37 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -82,12 +82,12 @@ replace_var(Var *var)
/*
* If there's already a PlannerParamVar entry for this same Var, just
* use it. NOTE: in sufficiently complex querytrees, it is
* possible for the same varno/varlevel to refer to different RTEs in
* different parts of the parsetree, so that different fields might
* end up sharing the same Param number. As long as we check the
* vartype as well, I believe that this sort of aliasing will cause no
* trouble. The correct field should get stored into the Param slot at
* use it. NOTE: in sufficiently complex querytrees, it is possible
* for the same varno/varlevel to refer to different RTEs in different
* parts of the parsetree, so that different fields might end up
* sharing the same Param number. As long as we check the vartype as
* well, I believe that this sort of aliasing will cause no trouble.
* The correct field should get stored into the Param slot at
* execution in each part of the tree.
*/
i = 0;
@ -142,10 +142,10 @@ make_subplan(SubLink *slink)
elog(ERROR, "make_subplan: invalid expression structure (subquery already processed?)");
/*
* Copy the source Query node. This is a quick and dirty kluge to resolve
* the fact that the parser can generate trees with multiple links to the
* same sub-Query node, but the planner wants to scribble on the Query.
* Try to clean this up when we do querytree redesign...
* Copy the source Query node. This is a quick and dirty kluge to
* resolve the fact that the parser can generate trees with multiple
* links to the same sub-Query node, but the planner wants to scribble
* on the Query. Try to clean this up when we do querytree redesign...
*/
subquery = (Query *) copyObject(subquery);
@ -183,7 +183,8 @@ make_subplan(SubLink *slink)
*/
node->plan = plan = subquery_planner(subquery, tuple_fraction);
node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
node->plan_id = PlannerPlanId++; /* Assign unique ID to this
* SubPlan */
node->rtable = subquery->rtable;
node->sublink = slink;
@ -191,8 +192,8 @@ make_subplan(SubLink *slink)
slink->subselect = NULL; /* cool ?! see error check above! */
/*
* Make parParam list of params that current query level will pass
* to this child plan.
* Make parParam list of params that current query level will pass to
* this child plan.
*/
foreach(lst, plan->extParam)
{
@ -275,7 +276,7 @@ make_subplan(SubLink *slink)
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oper->opno),
0, 0, 0);
if (! HeapTupleIsValid(tup))
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u", oper->opno);
opform = (Form_pg_operator) GETSTRUCT(tup);
@ -413,7 +414,7 @@ make_subplan(SubLink *slink)
tup = SearchSysCache(OPEROID,
ObjectIdGetDatum(oper->opno),
0, 0, 0);
if (! HeapTupleIsValid(tup))
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for operator %u", oper->opno);
opform = (Form_pg_operator) GETSTRUCT(tup);
@ -614,15 +615,16 @@ SS_finalize_plan(Plan *plan)
break;
case T_SubqueryScan:
/*
* In a SubqueryScan, SS_finalize_plan has already been run
* on the subplan by the inner invocation of subquery_planner,
* so there's no need to do it again. Instead, just pull out
* the subplan's extParams list, which represents the params
* it needs from my level and higher levels.
* In a SubqueryScan, SS_finalize_plan has already been run on
* the subplan by the inner invocation of subquery_planner, so
* there's no need to do it again. Instead, just pull out the
* subplan's extParams list, which represents the params it
* needs from my level and higher levels.
*/
results.paramids = set_unioni(results.paramids,
((SubqueryScan *) plan)->subplan->extParam);
((SubqueryScan *) plan)->subplan->extParam);
break;
case T_IndexScan:

View File

@ -104,7 +104,8 @@ transformKeySetQuery(Query *origNode)
Node_Copy(origNode, unionNode, distinctClause);
Node_Copy(origNode, unionNode, sortClause);
Node_Copy(origNode, unionNode, rtable);
origNode->jointree->quals = NULL; /* avoid unnecessary copying */
origNode->jointree->quals = NULL; /* avoid unnecessary
* copying */
Node_Copy(origNode, unionNode, jointree);
Node_Copy(origNode, unionNode, targetList);
@ -216,4 +217,4 @@ inspectOpNode(Expr *expr)
return (firstExpr && secondExpr && nodeTag(firstExpr) == T_Var && nodeTag(secondExpr) == T_Const);
}
#endif /* ENABLE_KEY_SET_QUERY */
#endif /* ENABLE_KEY_SET_QUERY */

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.28 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.29 2001/03/22 03:59:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -260,6 +260,7 @@ dnfify(Expr *qual)
return newqual;
}
#endif
/*--------------------
@ -663,11 +664,11 @@ or_normalize(List *orlist)
* We are going to insert the orlist into multiple places in the
* result expression. For most expression types, it'd be OK to
* just have multiple links to the same subtree, but this fails
* badly for SubLinks (and perhaps other cases?). For safety,
* we make a distinct copy for each place the orlist is inserted.
* badly for SubLinks (and perhaps other cases?). For safety, we
* make a distinct copy for each place the orlist is inserted.
*/
if (lnext(temp) == NIL)
neworlist = orlist; /* can use original tree at the end */
neworlist = orlist; /* can use original tree at the end */
else
neworlist = copyObject(orlist);
@ -791,11 +792,12 @@ and_normalize(List *andlist)
* We are going to insert the andlist into multiple places in the
* result expression. For most expression types, it'd be OK to
* just have multiple links to the same subtree, but this fails
* badly for SubLinks (and perhaps other cases?). For safety,
* we make a distinct copy for each place the andlist is inserted.
* badly for SubLinks (and perhaps other cases?). For safety, we
* make a distinct copy for each place the andlist is inserted.
*/
if (lnext(temp) == NIL)
newandlist = andlist; /* can use original tree at the end */
newandlist = andlist; /* can use original tree at the
* end */
else
newandlist = copyObject(andlist);
@ -957,8 +959,10 @@ count_bool_nodes(Expr *qual,
}
else if (contain_subplans((Node *) qual))
{
/* charge extra for subexpressions containing sub-SELECTs,
* to discourage us from rearranging them in a way that might
/*
* charge extra for subexpressions containing sub-SELECTs, to
* discourage us from rearranging them in a way that might
* generate N copies of a subselect rather than one. The magic
* constant here interacts with the "4x maximum growth" heuristic
* in canonicalize_qual().

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.41 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.42 2001/03/22 03:59:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,8 +33,8 @@
static List *expand_targetlist(List *tlist, int command_type,
Index result_relation, List *range_table);
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle,
int attrno);
TargetEntry *prior_tle,
int attrno);
/*
@ -49,9 +49,10 @@ preprocess_targetlist(List *tlist,
Index result_relation,
List *range_table)
{
/*
* Sanity check: if there is a result relation, it'd better be a
* real relation not a subquery. Else parser or rewriter messed up.
* Sanity check: if there is a result relation, it'd better be a real
* relation not a subquery. Else parser or rewriter messed up.
*/
if (result_relation)
{
@ -250,7 +251,7 @@ expand_targetlist(List *tlist, int command_type,
new_tle = makeTargetEntry(makeResdom(attrno,
atttype,
atttypmod,
pstrdup(attrname),
pstrdup(attrname),
false),
(Node *) temp_var);
break;
@ -280,7 +281,7 @@ expand_targetlist(List *tlist, int command_type,
{
Resdom *resdom = old_tle->resdom;
if (! resdom->resjunk)
if (!resdom->resjunk)
elog(ERROR, "Unexpected assignment to attribute \"%s\"",
resdom->resname);
/* Get the resno right, but don't copy unnecessarily */
@ -314,9 +315,10 @@ expand_targetlist(List *tlist, int command_type,
* Essentially, the expression we want to produce in this case is like
* foo = array_set(array_set(foo, 2, 42), 4, 43)
*/
static TargetEntry *process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle,
int attrno)
static TargetEntry *
process_matched_tle(TargetEntry *src_tle,
TargetEntry *prior_tle,
int attrno)
{
Resdom *resdom = src_tle->resdom;
Node *priorbottom;
@ -324,11 +326,13 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
if (prior_tle == NULL)
{
/*
* Normal case where this is the first assignment to the attribute.
* Normal case where this is the first assignment to the
* attribute.
*
* We can recycle the old TLE+resdom if right resno; else make a
* new one to avoid modifying the old tlist structure. (Is preserving
* We can recycle the old TLE+resdom if right resno; else make a new
* one to avoid modifying the old tlist structure. (Is preserving
* old tlist actually necessary? Not sure, be safe.)
*/
if (resdom->resno == attrno)
@ -339,7 +343,7 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
}
/*
* Multiple assignments to same attribute. Allow only if all are
* Multiple assignments to same attribute. Allow only if all are
* array-assign operators with same bottom array object.
*/
if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
@ -350,16 +354,19 @@ static TargetEntry *process_matched_tle(TargetEntry *src_tle,
((ArrayRef *) prior_tle->expr)->refelemtype)
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
resdom->resname);
/*
* Prior TLE could be a nest of ArrayRefs if we do this more than once.
* Prior TLE could be a nest of ArrayRefs if we do this more than
* once.
*/
priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
((ArrayRef *) priorbottom)->refassgnexpr != NULL)
priorbottom = ((ArrayRef *) priorbottom)->refexpr;
if (! equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
elog(ERROR, "Multiple assignments to same attribute \"%s\"",
resdom->resname);
/*
* Looks OK to nest 'em.
*/

View File

@ -5,7 +5,7 @@
* from a time when only UNIONs were implemented.
*
* There is also some code here to support planning of queries that use
* inheritance (SELECT FROM foo*). This no longer has much connection
* inheritance (SELECT FROM foo*). This no longer has much connection
* to the processing of UNION queries, but it's still here.
*
*
@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.60 2001/01/24 19:42:59 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.61 2001/03/22 03:59:38 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -50,22 +50,22 @@ typedef struct
} adjust_inherited_attrs_context;
static Plan *recurse_set_operations(Node *setOp, Query *parse,
List *colTypes, bool junkOK,
int flag, List *refnames_tlist);
List *colTypes, bool junkOK,
int flag, List *refnames_tlist);
static Plan *generate_union_plan(SetOperationStmt *op, Query *parse,
List *refnames_tlist);
List *refnames_tlist);
static Plan *generate_nonunion_plan(SetOperationStmt *op, Query *parse,
List *refnames_tlist);
List *refnames_tlist);
static List *recurse_union_children(Node *setOp, Query *parse,
SetOperationStmt *top_union,
List *refnames_tlist);
SetOperationStmt *top_union,
List *refnames_tlist);
static List *generate_setop_tlist(List *colTypes, int flag,
bool hack_constants,
List *input_tlist,
List *refnames_tlist);
bool hack_constants,
List *input_tlist,
List *refnames_tlist);
static bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context);
adjust_inherited_attrs_context *context);
/*
@ -99,10 +99,10 @@ plan_set_operations(Query *parse)
Assert(leftmostQuery != NULL);
/*
* Recurse on setOperations tree to generate plans for set ops.
* The final output plan should have just the column types shown
* as the output from the top-level node, plus possibly a resjunk
* working column (we can rely on upper-level nodes to deal with that).
* Recurse on setOperations tree to generate plans for set ops. The
* final output plan should have just the column types shown as the
* output from the top-level node, plus possibly a resjunk working
* column (we can rely on upper-level nodes to deal with that).
*/
return recurse_set_operations((Node *) topop, parse,
topop->colTypes, true, -1,
@ -127,16 +127,18 @@ recurse_set_operations(Node *setOp, Query *parse,
{
RangeTblRef *rtr = (RangeTblRef *) setOp;
RangeTblEntry *rte = rt_fetch(rtr->rtindex, parse->rtable);
Query *subquery = rte->subquery;
Plan *subplan,
*plan;
Query *subquery = rte->subquery;
Plan *subplan,
*plan;
Assert(subquery != NULL);
/*
* Generate plan for primitive subquery
*/
subplan = subquery_planner(subquery,
-1.0 /* default case */ );
/*
* Add a SubqueryScan with the caller-requested targetlist
*/
@ -152,28 +154,30 @@ recurse_set_operations(Node *setOp, Query *parse,
else if (IsA(setOp, SetOperationStmt))
{
SetOperationStmt *op = (SetOperationStmt *) setOp;
Plan *plan;
Plan *plan;
/* UNIONs are much different from INTERSECT/EXCEPT */
if (op->op == SETOP_UNION)
plan = generate_union_plan(op, parse, refnames_tlist);
else
plan = generate_nonunion_plan(op, parse, refnames_tlist);
/*
* If necessary, add a Result node to project the caller-requested
* output columns.
*
* XXX you don't really want to know about this: setrefs.c will apply
* replace_vars_with_subplan_refs() to the Result node's tlist.
* This would fail if the input plan's non-resjunk tlist entries were
* not all simple Vars equal() to the referencing Vars generated by
* generate_setop_tlist(). However, since the input plan was
* generated by generate_union_plan() or generate_nonunion_plan(),
* the referencing Vars will equal the tlist entries they reference.
* Ugly but I don't feel like making that code more general right now.
* This would fail if the input plan's non-resjunk tlist entries
* were not all simple Vars equal() to the referencing Vars
* generated by generate_setop_tlist(). However, since the input
* plan was generated by generate_union_plan() or
* generate_nonunion_plan(), the referencing Vars will equal the
* tlist entries they reference. Ugly but I don't feel like making
* that code more general right now.
*/
if (flag >= 0 ||
! tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
!tlist_same_datatypes(plan->targetlist, colTypes, junkOK))
{
plan = (Plan *)
make_result(generate_setop_tlist(colTypes, flag, false,
@ -199,8 +203,8 @@ static Plan *
generate_union_plan(SetOperationStmt *op, Query *parse,
List *refnames_tlist)
{
List *planlist;
Plan *plan;
List *planlist;
Plan *plan;
/*
* If any of my children are identical UNION nodes (same op, all-flag,
@ -212,27 +216,29 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
op, refnames_tlist),
recurse_union_children(op->rarg, parse,
op, refnames_tlist));
/*
* Append the child results together.
*
* The tlist for an Append plan isn't important as far as the Append
* is concerned, but we must make it look real anyway for the benefit
* of the next plan level up.
* The tlist for an Append plan isn't important as far as the Append is
* concerned, but we must make it look real anyway for the benefit of
* the next plan level up.
*/
plan = (Plan *)
make_append(planlist,
false,
generate_setop_tlist(op->colTypes, -1, false,
((Plan *) lfirst(planlist))->targetlist,
refnames_tlist));
((Plan *) lfirst(planlist))->targetlist,
refnames_tlist));
/*
* For UNION ALL, we just need the Append plan. For UNION,
* need to add Sort and Unique nodes to produce unique output.
* For UNION ALL, we just need the Append plan. For UNION, need to
* add Sort and Unique nodes to produce unique output.
*/
if (! op->all)
if (!op->all)
{
List *tlist,
*sortList;
List *tlist,
*sortList;
tlist = new_unsorted_tlist(plan->targetlist);
sortList = addAllTargetsToSortList(NIL, tlist);
@ -249,12 +255,12 @@ static Plan *
generate_nonunion_plan(SetOperationStmt *op, Query *parse,
List *refnames_tlist)
{
Plan *lplan,
*rplan,
*plan;
List *tlist,
*sortList;
SetOpCmd cmd;
Plan *lplan,
*rplan,
*plan;
List *tlist,
*sortList;
SetOpCmd cmd;
/* Recurse on children, ensuring their outputs are marked */
lplan = recurse_set_operations(op->larg, parse,
@ -263,12 +269,13 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
rplan = recurse_set_operations(op->rarg, parse,
op->colTypes, false, 1,
refnames_tlist);
/*
* Append the child results together.
*
* The tlist for an Append plan isn't important as far as the Append
* is concerned, but we must make it look real anyway for the benefit
* of the next plan level up.
* The tlist for an Append plan isn't important as far as the Append is
* concerned, but we must make it look real anyway for the benefit of
* the next plan level up.
*/
plan = (Plan *)
make_append(makeList2(lplan, rplan),
@ -276,9 +283,10 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
generate_setop_tlist(op->colTypes, 0, false,
lplan->targetlist,
refnames_tlist));
/*
* Sort the child results, then add a SetOp plan node to
* generate the correct output.
* Sort the child results, then add a SetOp plan node to generate the
* correct output.
*/
tlist = new_unsorted_tlist(plan->targetlist);
sortList = addAllTargetsToSortList(NIL, tlist);
@ -293,11 +301,11 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
break;
default:
elog(ERROR, "generate_nonunion_plan: bogus operation code");
cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
cmd = SETOPCMD_INTERSECT; /* keep compiler quiet */
break;
}
plan = (Plan *) make_setop(cmd, tlist, plan, sortList,
length(op->colTypes)+1);
length(op->colTypes) + 1);
return plan;
}
@ -322,20 +330,21 @@ recurse_union_children(Node *setOp, Query *parse,
{
/* Same UNION, so fold children into parent's subplan list */
return nconc(recurse_union_children(op->larg, parse,
top_union, refnames_tlist),
top_union, refnames_tlist),
recurse_union_children(op->rarg, parse,
top_union, refnames_tlist));
top_union, refnames_tlist));
}
}
/*
* Not same, so plan this child separately.
*
* Note we disallow any resjunk columns in child results. This
* is necessary since the Append node that implements the union
* won't do any projection, and upper levels will get confused if
* some of our output tuples have junk and some don't. This case
* only arises when we have an EXCEPT or INTERSECT as child, else
* there won't be resjunk anyway.
* Note we disallow any resjunk columns in child results. This is
* necessary since the Append node that implements the union won't do
* any projection, and upper levels will get confused if some of our
* output tuples have junk and some don't. This case only arises when
* we have an EXCEPT or INTERSECT as child, else there won't be
* resjunk anyway.
*/
return makeList1(recurse_set_operations(setOp, parse,
top_union->colTypes, false,
@ -359,7 +368,7 @@ generate_setop_tlist(List *colTypes, int flag,
foreach(i, colTypes)
{
Oid colType = (Oid) lfirsti(i);
Oid colType = (Oid) lfirsti(i);
TargetEntry *inputtle = (TargetEntry *) lfirst(input_tlist);
TargetEntry *reftle = (TargetEntry *) lfirst(refnames_tlist);
@ -367,18 +376,19 @@ generate_setop_tlist(List *colTypes, int flag,
Assert(reftle->resdom->resno == resno);
Assert(!inputtle->resdom->resjunk);
Assert(!reftle->resdom->resjunk);
/*
* Generate columns referencing input columns and having
* appropriate data types and column names. Insert datatype
* coercions where necessary.
*
* HACK: constants in the input's targetlist are copied up as-is
* rather than being referenced as subquery outputs. This is mainly
* to ensure that when we try to coerce them to the output column's
* datatype, the right things happen for UNKNOWN constants. But do
* this only at the first level of subquery-scan plans; we don't
* want phony constants appearing in the output tlists of upper-level
* nodes!
* rather than being referenced as subquery outputs. This is
* mainly to ensure that when we try to coerce them to the output
* column's datatype, the right things happen for UNKNOWN
* constants. But do this only at the first level of
* subquery-scan plans; we don't want phony constants appearing in
* the output tlists of upper-level nodes!
*/
resdom = makeResdom((AttrNumber) resno++,
colType,
@ -440,7 +450,7 @@ tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK)
if (tle->resdom->resjunk)
{
if (! junkOK)
if (!junkOK)
return false;
}
else
@ -484,11 +494,11 @@ find_all_inheritors(Oid parentrel)
currentchildren = find_inheritance_children(currentrel);
/*
* Add to the queue only those children not already seen.
* This avoids making duplicate entries in case of multiple
* inheritance paths from the same parent. (It'll also keep
* us from getting into an infinite loop, though theoretically
* there can't be any cycles in the inheritance graph anyway.)
* Add to the queue only those children not already seen. This
* avoids making duplicate entries in case of multiple inheritance
* paths from the same parent. (It'll also keep us from getting
* into an infinite loop, though theoretically there can't be any
* cycles in the inheritance graph anyway.)
*/
currentchildren = set_differencei(currentchildren, examined_relids);
unexamined_relids = set_unioni(unexamined_relids, currentchildren);
@ -524,20 +534,21 @@ expand_inherted_rtentry(Query *parse, Index rti)
List *l;
/* Does RT entry allow inheritance? */
if (! rte->inh)
if (!rte->inh)
return NIL;
Assert(parentOID != InvalidOid && rte->subquery == NULL);
/* Always clear the parent's inh flag, see above comments */
rte->inh = false;
/* Fast path for common case of childless table */
if (! has_subclass(parentOID))
if (!has_subclass(parentOID))
return NIL;
/* Scan for all members of inheritance set */
inhOIDs = find_all_inheritors(parentOID);
/*
* Check that there's at least one descendant, else treat as
* no-child case. This could happen despite above has_subclass()
* check, if table once had a child but no longer does.
* Check that there's at least one descendant, else treat as no-child
* case. This could happen despite above has_subclass() check, if
* table once had a child but no longer does.
*/
if (lnext(inhOIDs) == NIL)
return NIL;
@ -545,18 +556,19 @@ expand_inherted_rtentry(Query *parse, Index rti)
inhRTIs = makeListi1(rti);
foreach(l, inhOIDs)
{
Oid childOID = (Oid) lfirsti(l);
Oid childOID = (Oid) lfirsti(l);
RangeTblEntry *childrte;
Index childRTindex;
Index childRTindex;
/* parent will be in the list too, so ignore it */
if (childOID == parentOID)
continue;
/*
* Build an RTE for the child, and attach to query's rangetable list.
* We copy most fields of the parent's RTE, but replace relation
* real name and OID. Note that inh will be false at this point.
* Build an RTE for the child, and attach to query's rangetable
* list. We copy most fields of the parent's RTE, but replace
* relation real name and OID. Note that inh will be false at
* this point.
*/
childrte = copyObject(rte);
childrte->relname = get_rel_name(childOID);
@ -575,12 +587,12 @@ expand_inherted_rtentry(Query *parse, Index rti)
* to old_rt_index to refer to new_rt_index.
*
* We also adjust varattno to match the new table by column name, rather
* than column number. This hack makes it possible for child tables to have
* than column number. This hack makes it possible for child tables to have
* different column positions for the "same" attribute as a parent, which
* helps ALTER TABLE ADD COLUMN. Unfortunately this isn't nearly enough to
* make it work transparently; there are other places where things fall down
* if children and parents don't have the same column numbers for inherited
* attributes. It'd be better to rip this code out and fix ALTER TABLE...
* attributes. It'd be better to rip this code out and fix ALTER TABLE...
*/
Node *
adjust_inherited_attrs(Node *node,
@ -643,12 +655,13 @@ adjust_inherited_attrs_mutator(Node *node,
}
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
RangeTblRef *rtr = (RangeTblRef *) copyObject(node);
if (rtr->rtindex == context->old_rt_index)
rtr->rtindex = context->new_rt_index;
return (Node *) rtr;
}
/*
* We have to process RestrictInfo nodes specially: we do NOT want to
* copy the original subclauseindices list, since the new rel may have
@ -656,8 +669,8 @@ adjust_inherited_attrs_mutator(Node *node,
*/
if (IsA(node, RestrictInfo))
{
RestrictInfo *oldinfo = (RestrictInfo *) node;
RestrictInfo *newinfo = makeNode(RestrictInfo);
RestrictInfo *oldinfo = (RestrictInfo *) node;
RestrictInfo *newinfo = makeNode(RestrictInfo);
/* Copy all flat-copiable fields */
memcpy(newinfo, oldinfo, sizeof(RestrictInfo));
@ -666,18 +679,19 @@ adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context);
newinfo->subclauseindices = NIL;
newinfo->eval_cost = -1; /* reset this too */
newinfo->left_pathkey = NIL; /* and these */
newinfo->eval_cost = -1;/* reset this too */
newinfo->left_pathkey = NIL; /* and these */
newinfo->right_pathkey = NIL;
newinfo->left_dispersion = -1;
newinfo->right_dispersion = -1;
return (Node *) newinfo;
}
/*
* NOTE: we do not need to recurse into sublinks, because they should
* already have been converted to subplans before we see them.
*/
return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
(void *) context);
(void *) context);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.82 2001/03/08 01:49:01 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.83 2001/03/22 03:59:39 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -544,8 +544,8 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
/*
* If we find an aggregate function, do not recurse into its
* arguments. Subplans invoked within aggregate calls are allowed
* to receive ungrouped variables.
* arguments. Subplans invoked within aggregate calls are allowed to
* receive ungrouped variables.
*/
if (IsA(node, Aggref))
return false;
@ -630,7 +630,7 @@ check_subplans_for_ungrouped_vars_walker(Node *node,
* Recursively search for noncachable functions within a clause.
*
* Returns true if any noncachable function (or operator implemented by a
* noncachable function) is found. This test is needed so that we don't
* noncachable function) is found. This test is needed so that we don't
* mistakenly think that something like "WHERE random() < 0.5" can be treated
* as a constant qualification.
*
@ -655,11 +655,11 @@ contain_noncachable_functions_walker(Node *node, void *context)
switch (expr->opType)
{
case OP_EXPR:
if (! op_iscachable(((Oper *) expr->oper)->opno))
if (!op_iscachable(((Oper *) expr->oper)->opno))
return true;
break;
case FUNC_EXPR:
if (! func_iscachable(((Func *) expr->oper)->funcid))
if (!func_iscachable(((Func *) expr->oper)->funcid))
return true;
break;
default:
@ -680,13 +680,14 @@ contain_noncachable_functions_walker(Node *node, void *context)
* Detect whether a clause is "constant", ie, it contains no variables
* of the current query level and no uses of noncachable functions.
* Such a clause is not necessarily a true constant: it can still contain
* Params and outer-level Vars. However, its value will be constant over
* Params and outer-level Vars. However, its value will be constant over
* any one scan of the current query, so it can be used as an indexscan
* key or (if a top-level qual) can be pushed up to become a gating qual.
*/
bool
is_pseudo_constant_clause(Node *clause)
{
/*
* We could implement this check in one recursive scan. But since the
* check for noncachable functions is both moderately expensive and
@ -716,7 +717,7 @@ pull_constant_clauses(List *quals, List **constantQual)
foreach(q, quals)
{
Node *qual = (Node *) lfirst(q);
Node *qual = (Node *) lfirst(q);
if (is_pseudo_constant_clause(qual))
constqual = lappend(constqual, qual);
@ -1277,8 +1278,8 @@ eval_const_expressions_mutator(Node *node, void *context)
arg = eval_const_expressions_mutator(relabel->arg, context);
/*
* If we find stacked RelabelTypes (eg, from foo :: int :: oid)
* we can discard all but the top one.
* If we find stacked RelabelTypes (eg, from foo :: int :: oid) we
* can discard all but the top one.
*/
while (arg && IsA(arg, RelabelType))
arg = ((RelabelType *) arg)->arg;
@ -1472,8 +1473,8 @@ simplify_op_or_func(Expr *expr, List *args)
* If the function is strict and has a constant-NULL input, it will
* never be called at all, so we can replace the call by a NULL
* constant even if there are other inputs that aren't constant.
* Otherwise, we can only simplify if all inputs are constants.
* We can skip the function lookup if neither case applies.
* Otherwise, we can only simplify if all inputs are constants. We can
* skip the function lookup if neither case applies.
*/
if (has_nonconst_input && !has_null_input)
return NULL;
@ -1500,9 +1501,10 @@ simplify_op_or_func(Expr *expr, List *args)
funcid = func->funcid;
result_typeid = func->functype;
}
/*
* we could use func_iscachable() here, but we need several fields
* out of the func tuple, so might as well just look it up once.
* we could use func_iscachable() here, but we need several fields out
* of the func tuple, so might as well just look it up once.
*/
func_tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
@ -1530,6 +1532,7 @@ simplify_op_or_func(Expr *expr, List *args)
*/
if (proisstrict && has_null_input)
{
/*
* It's strict and has NULL input, so must produce NULL output.
* Return a NULL constant of the right type.
@ -1538,9 +1541,9 @@ simplify_op_or_func(Expr *expr, List *args)
}
/*
* Otherwise, can simplify only if all inputs are constants.
* (For a non-strict function, constant NULL inputs are treated
* the same as constant non-NULL inputs.)
* Otherwise, can simplify only if all inputs are constants. (For a
* non-strict function, constant NULL inputs are treated the same as
* constant non-NULL inputs.)
*/
if (has_nonconst_input)
return NULL;
@ -1565,10 +1568,10 @@ simplify_op_or_func(Expr *expr, List *args)
get_typlenbyval(result_typeid, &resultTypLen, &resultTypByVal);
/*
* It is OK to pass a dummy econtext because none of the ExecEvalExpr()
* code used in this situation will use econtext. That might seem
* fortuitous, but it's not so unreasonable --- a constant expression
* does not depend on context, by definition, n'est ce pas?
* It is OK to pass a dummy econtext because none of the
* ExecEvalExpr() code used in this situation will use econtext. That
* might seem fortuitous, but it's not so unreasonable --- a constant
* expression does not depend on context, by definition, n'est ce pas?
*/
econtext = MakeExprContext(NULL, CurrentMemoryContext);
@ -1657,10 +1660,10 @@ simplify_op_or_func(Expr *expr, List *args)
* expression_tree_walker itself is called on a Query node, it does nothing
* and returns "false". The net effect is that unless the walker does
* something special at a Query node, sub-selects will not be visited
* during an expression tree walk. This is exactly the behavior wanted
* during an expression tree walk. This is exactly the behavior wanted
* in many cases --- and for those walkers that do want to recurse into
* sub-selects, special behavior is typically needed anyway at the entry
* to a sub-select (such as incrementing a depth counter). A walker that
* to a sub-select (such as incrementing a depth counter). A walker that
* wants to examine sub-selects should include code along the lines of:
*
* if (IsA(node, Query))
@ -1780,9 +1783,10 @@ expression_tree_walker(Node *node,
/*
* If the SubLink has already been processed by
* subselect.c, it will have lefthand=NIL, and we need to
* scan the oper list. Otherwise we only need to look at
* the lefthand list (the incomplete Oper nodes in the oper
* list are deemed uninteresting, perhaps even confusing).
* scan the oper list. Otherwise we only need to look at
* the lefthand list (the incomplete Oper nodes in the
* oper list are deemed uninteresting, perhaps even
* confusing).
*/
if (sublink->lefthand)
{
@ -1794,9 +1798,10 @@ expression_tree_walker(Node *node,
if (walker((Node *) sublink->oper, context))
return true;
}
/*
* Also invoke the walker on the sublink's Query node,
* so it can recurse into the sub-query if it wants to.
* Also invoke the walker on the sublink's Query node, so
* it can recurse into the sub-query if it wants to.
*/
return walker(sublink->subselect, context);
}
@ -1815,7 +1820,7 @@ expression_tree_walker(Node *node,
return walker(((TargetEntry *) node)->expr, context);
case T_FromExpr:
{
FromExpr *from = (FromExpr *) node;
FromExpr *from = (FromExpr *) node;
if (walker(from->fromlist, context))
return true;
@ -1825,7 +1830,7 @@ expression_tree_walker(Node *node,
break;
case T_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
JoinExpr *join = (JoinExpr *) node;
if (walker(join->larg, context))
return true;
@ -1835,7 +1840,9 @@ expression_tree_walker(Node *node,
return true;
if (walker((Node *) join->colvars, context))
return true;
/* alias clause, using list, colnames list are deemed
/*
* alias clause, using list, colnames list are deemed
* uninteresting.
*/
}
@ -1890,7 +1897,7 @@ query_tree_walker(Query *query,
return true;
if (visitQueryRTEs)
{
List *rt;
List *rt;
foreach(rt, query->rtable)
{
@ -2176,8 +2183,8 @@ expression_tree_mutator(Node *node,
break;
case T_FromExpr:
{
FromExpr *from = (FromExpr *) node;
FromExpr *newnode;
FromExpr *from = (FromExpr *) node;
FromExpr *newnode;
FLATCOPY(newnode, from, FromExpr);
MUTATE(newnode->fromlist, from->fromlist, List *);
@ -2187,8 +2194,8 @@ expression_tree_mutator(Node *node,
break;
case T_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
JoinExpr *newnode;
JoinExpr *join = (JoinExpr *) node;
JoinExpr *newnode;
FLATCOPY(newnode, join, JoinExpr);
MUTATE(newnode->larg, join->larg, Node *);
@ -2226,7 +2233,7 @@ expression_tree_mutator(Node *node,
* This routine exists just to reduce the number of places that need to know
* where all the expression subtrees of a Query are. Note it can be used
* for starting a walk at top level of a Query regardless of whether the
* mutator intends to descend into subqueries. It is also useful for
* mutator intends to descend into subqueries. It is also useful for
* descending into subqueries within a mutator.
*
* The specified Query node is modified-in-place; do a FLATCOPY() beforehand
@ -2252,8 +2259,8 @@ query_tree_mutator(Query *query,
MUTATE(query->havingQual, query->havingQual, Node *);
if (visitQueryRTEs)
{
List *newrt = NIL;
List *rt;
List *newrt = NIL;
List *rt;
foreach(rt, query->rtable)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.28 2001/01/24 19:43:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.29 2001/03/22 03:59:39 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -33,7 +33,7 @@ static JoinInfo *joininfo_member(List *join_relids, List *joininfo_list);
* exists.
*
*/
static JoinInfo *
static JoinInfo *
joininfo_member(List *join_relids, List *joininfo_list)
{
List *i;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.70 2001/01/24 19:43:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.71 2001/03/22 03:59:39 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -192,7 +192,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
* possible for more than one old path to be tossed out because
* new_path dominates it.
*/
p1 = parent_rel->pathlist; /* cannot use foreach here */
p1 = parent_rel->pathlist; /* cannot use foreach here */
while (p1 != NIL)
{
Path *old_path = (Path *) lfirst(p1);
@ -243,7 +243,7 @@ add_path(RelOptInfo *parent_rel, Path *new_path)
*/
if (remove_old && parent_rel->pruneable)
{
List *p1_next = lnext(p1);
List *p1_next = lnext(p1);
if (p1_prev)
lnext(p1_prev) = p1_next;
@ -409,14 +409,15 @@ create_append_path(RelOptInfo *rel, List *subpaths)
pathnode->path.pathtype = T_Append;
pathnode->path.parent = rel;
pathnode->path.pathkeys = NIL; /* result is always considered unsorted */
pathnode->path.pathkeys = NIL; /* result is always considered
* unsorted */
pathnode->subpaths = subpaths;
pathnode->path.startup_cost = 0;
pathnode->path.total_cost = 0;
foreach(l, subpaths)
{
Path *subpath = (Path *) lfirst(l);
Path *subpath = (Path *) lfirst(l);
if (l == subpaths) /* first node? */
pathnode->path.startup_cost = subpath->startup_cost;

View File

@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.63 2001/01/24 19:43:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.64 2001/03/22 03:59:40 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -83,8 +83,8 @@ find_secondary_indexes(Oid relationObjectId)
Relation relation;
/*
* We used to scan pg_index directly, but now the relcache offers
* a cached list of OID indexes for each relation. So, get that list
* We used to scan pg_index directly, but now the relcache offers a
* cached list of OID indexes for each relation. So, get that list
* and then use the syscache to obtain pg_index entries.
*/
relation = heap_open(relationObjectId, AccessShareLock);
@ -126,7 +126,7 @@ find_secondary_indexes(Oid relationObjectId)
char *predString;
predString = DatumGetCString(DirectFunctionCall1(textout,
PointerGetDatum(&index->indpred)));
PointerGetDatum(&index->indpred)));
info->indpred = (List *) stringToNode(predString);
pfree(predString);
}
@ -213,11 +213,11 @@ restriction_selectivity(Oid functionObjectId,
float8 result;
result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId),
Int16GetDatum(attributeNumber),
constValue,
Int32GetDatum(constFlag)));
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId),
Int16GetDatum(attributeNumber),
constValue,
Int32GetDatum(constFlag)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "restriction_selectivity: bad value %f", result);
@ -246,11 +246,11 @@ join_selectivity(Oid functionObjectId,
float8 result;
result = DatumGetFloat8(OidFunctionCall5(functionObjectId,
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId1),
Int16GetDatum(attributeNumber1),
ObjectIdGetDatum(relationObjectId2),
Int16GetDatum(attributeNumber2)));
ObjectIdGetDatum(operatorObjectId),
ObjectIdGetDatum(relationObjectId1),
Int16GetDatum(attributeNumber1),
ObjectIdGetDatum(relationObjectId2),
Int16GetDatum(attributeNumber2)));
if (result < 0.0 || result > 1.0)
elog(ERROR, "join_selectivity: bad value %f", result);
@ -277,13 +277,13 @@ find_inheritance_children(Oid inhparent)
HeapScanDesc scan;
HeapTuple inheritsTuple;
Oid inhrelid;
ScanKeyData key[1];
ScanKeyData key[1];
/*
* Can skip the scan if pg_class shows the relation has never had
* a subclass.
* Can skip the scan if pg_class shows the relation has never had a
* subclass.
*/
if (! has_subclass(inhparent))
if (!has_subclass(inhparent))
return NIL;
ScanKeyEntryInitialize(&key[0],
@ -306,7 +306,7 @@ find_inheritance_children(Oid inhparent)
/*
* has_subclass
*
* In the current implementation, has_subclass returns whether a
* In the current implementation, has_subclass returns whether a
* particular class *might* have a subclass. It will not return the
* correct result if a class had a subclass which was later dropped.
* This is because relhassubclass in pg_class is not updated when a

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.49 2001/01/24 19:43:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.50 2001/03/22 03:59:40 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -60,6 +60,7 @@ matching_tlist_expr(Node *node, List *targetlist)
return (Node *) NULL;
}
#endif
/*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.29 2001/01/24 19:43:00 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.30 2001/03/22 03:59:40 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -34,7 +34,7 @@ typedef struct
} pull_var_clause_context;
static bool pull_varnos_walker(Node *node,
pull_varnos_context *context);
pull_varnos_context *context);
static bool contain_var_clause_walker(Node *node, void *context);
static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context);
@ -90,15 +90,16 @@ pull_varnos_walker(Node *node, pull_varnos_context *context)
}
if (is_subplan(node))
{
/*
* Already-planned subquery. Examine the args list (parameters
* to be passed to subquery), as well as the "oper" list which
* is executed by the outer query. But short-circuit recursion into
* Already-planned subquery. Examine the args list (parameters to
* be passed to subquery), as well as the "oper" list which is
* executed by the outer query. But short-circuit recursion into
* the subquery itself, which would be a waste of effort.
*/
Expr *expr = (Expr *) node;
if (pull_varnos_walker((Node*) ((SubPlan*) expr->oper)->sublink->oper,
if (pull_varnos_walker((Node *) ((SubPlan *) expr->oper)->sublink->oper,
context))
return true;
if (pull_varnos_walker((Node *) expr->args,