1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-13 16:22:44 +03:00

Postgres95 1.01 Distribution - Virgin Sources

This commit is contained in:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
#-------------------------------------------------------------------------
#
# Makefile.inc--
# Makefile for optimizer/plan
#
# Copyright (c) 1994, Regents of the University of California
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/plan/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
#
#-------------------------------------------------------------------------
SUBSRCS+= createplan.c initsplan.c planmain.c planner.c \
setrefs.c

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,391 @@
/*-------------------------------------------------------------------------
*
* initsplan.c--
* Target list, qualification, joininfo initialization routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/parsenodes.h"
#include "nodes/relation.h"
#include "nodes/makefuncs.h"
#include "utils/lsyscache.h"
#include "utils/palloc.h"
#include "optimizer/internal.h"
#include "optimizer/planmain.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
extern int Quiet;
static void add_clause_to_rels(Query *root, List *clause);
static void add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo,
List *join_relids);
static void add_vars_to_rels(Query *root, List *vars, List *join_relids);
static MergeOrder *mergesortop(Expr *clause);
static Oid hashjoinop(Expr *clause);
/*****************************************************************************
*
* TARGET LISTS
*
*****************************************************************************/
/*
* initialize_rel_nodes--
* Creates rel nodes for every relation mentioned in the target list
* 'tlist' (if a node hasn't already been created) and adds them to
* *query-relation-list*. Creates targetlist entries for each member of
* 'tlist' and adds them to the tlist field of the appropriate rel node.
*
* Returns nothing.
*/
void
initialize_base_rels_list(Query *root, List *tlist)
{
List *tlist_vars = NIL;
List *l = NIL;
List *tvar = NIL;
foreach (l, tlist) {
TargetEntry *entry = (TargetEntry *) lfirst(l);
tlist_vars = append(tlist_vars, pull_var_clause(entry->expr));
}
/* now, the target list only contains Var nodes */
foreach (tvar, tlist_vars) {
Var *var;
Index varno;
Rel *result;
var = (Var*)lfirst(tvar);
varno = var->varno;
result = get_base_rel(root, varno);
add_tl_element(result, var);
}
}
/*
* add_missing_variables_to_base_rels -
* If we have range variable(s) in the FROM clause that does not appear
* in the target list nor qualifications, we add it to the base relation
* list. For instance, "select f.x from foo f, foo f2" is a join of f and
* f2. Note that if we have "select foo.x from foo f", it also gets turned
* into a join.
*/
void
add_missing_vars_to_base_rels(Query *root, List *tlist)
{
List *l;
int varno;
varno = 1;
foreach (l, root->rtable) {
RangeTblEntry *rte = (RangeTblEntry *)lfirst(l);
List *relids;
Rel *result;
Var *var;
relids = lconsi(varno, NIL);
if (rte->inFromCl &&
!rel_member(relids, root->base_relation_list_)) {
var = makeVar(varno, -2 , 26, varno, -2);
/* add it to base_relation_list_ */
result = get_base_rel(root, varno);
add_tl_element(result, var);
}
pfree(relids);
varno++;
}
return;
}
/*****************************************************************************
*
* QUALIFICATIONS
*
*****************************************************************************/
/*
* initialize-qualification--
* Initializes ClauseInfo and JoinInfo fields of relation entries for all
* relations appearing within clauses. Creates new relation entries if
* necessary, adding them to *query-relation-list*.
*
* Returns nothing of interest.
*/
void
initialize_base_rels_jinfo(Query *root, List *clauses)
{
List *clause;
foreach (clause, clauses) {
add_clause_to_rels(root, lfirst(clause));
}
return;
}
/*
* add-clause-to-rels--
* Add clause information to either the 'ClauseInfo' or 'JoinInfo' field
* of a relation entry(depending on whether or not the clause is a join)
* by creating a new ClauseInfo node and setting appropriate fields
* within the nodes.
*
* Returns nothing of interest.
*/
static void
add_clause_to_rels(Query *root, List *clause)
{
List *relids;
List *vars;
CInfo *clauseinfo = makeNode(CInfo);
/*
* Retrieve all relids and vars contained within the clause.
*/
clause_relids_vars((Node*)clause, &relids, &vars);
clauseinfo->clause = (Expr*)clause;
clauseinfo->notclause = contains_not((Node*)clause);
clauseinfo->selectivity = 0;
clauseinfo->indexids = NIL;
clauseinfo->mergesortorder = (MergeOrder*)NULL;
clauseinfo->hashjoinoperator = (Oid)0;
if(length(relids) == 1) {
Rel *rel = get_base_rel(root, lfirsti(relids));
/*
* There is only one relation participating in 'clause',
* so 'clause' must be a restriction clause.
*/
/* the selectivity of the clause must be computed
regardless of whether it's a restriction or a join clause */
if (is_funcclause((Node*)clause))
{
/*
* XXX If we have a func clause set selectivity to 1/3,
* really need a true selectivity function.
*/
clauseinfo->selectivity = (Cost)0.3333333;
}
else
{
clauseinfo->selectivity =
compute_clause_selec(root, (Node*)clause,
NIL);
}
rel->clauseinfo = lcons(clauseinfo,
rel->clauseinfo);
} else {
/*
* 'clause' is a join clause, since there is more than one
* atom in the relid list.
*/
if (is_funcclause((Node*)clause))
{
/*
* XXX If we have a func clause set selectivity to 1/3,
* really need a true selectivity function.
*/
clauseinfo->selectivity = (Cost)0.3333333;
}
else
{
clauseinfo->selectivity =
compute_clause_selec(root, (Node*)clause,
NIL);
}
add_join_clause_info_to_rels(root, clauseinfo, relids);
add_vars_to_rels(root,vars, relids);
}
}
/*
* add-join-clause-info-to-rels--
* For every relation participating in a join clause, add 'clauseinfo' to
* the appropriate joininfo node(creating a new one and adding it to the
* appropriate rel node if necessary).
*
* 'clauseinfo' describes the join clause
* 'join-relids' is the list of relations participating in the join clause
*
* Returns nothing.
*
*/
static void
add_join_clause_info_to_rels(Query *root, CInfo *clauseinfo, List *join_relids)
{
List *join_relid;
foreach (join_relid, join_relids) {
JInfo *joininfo =
find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
intLispRemove((int)lfirst(join_relid),
join_relids));
joininfo->jinfoclauseinfo =
lcons(clauseinfo, joininfo->jinfoclauseinfo);
}
}
/*
* add-vars-to-rels--
* For each variable appearing in a clause,
* (1) If a targetlist entry for the variable is not already present in
* the appropriate relation's target list, add one.
* (2) If a targetlist entry is already present, but the var is part of a
* join clause, add the relids of the join relations to the JoinList
* entry of the targetlist entry.
*
* 'vars' is the list of var nodes
* 'join-relids' is the list of relids appearing in the join clause
* (if this is a join clause)
*
* Returns nothing.
*/
static void
add_vars_to_rels(Query *root, List *vars, List *join_relids)
{
Var *var;
List *temp = NIL;
Rel *rel = (Rel*)NULL;
TargetEntry *tlistentry;
foreach (temp, vars) {
var = (Var*)lfirst(temp);
rel = get_base_rel(root, var->varno);
tlistentry = tlistentry_member(var, rel->targetlist);
if(tlistentry==NULL)
/* add a new entry */
add_tl_element(rel, var);
}
}
/*****************************************************************************
*
* JOININFO
*
*****************************************************************************/
/*
* initialize-join-clause-info--
* Set the MergeSortable or HashJoinable field for every joininfo node
* (within a rel node) and the MergeSortOrder or HashJoinOp field for
* each clauseinfo node(within a joininfo node) for all relations in a
* query.
*
* Returns nothing.
*/
void
initialize_join_clause_info(List *rel_list)
{
List *x, *y, *z;
Rel *rel;
JInfo *joininfo;
CInfo *clauseinfo;
Expr *clause;
foreach (x, rel_list) {
rel = (Rel*)lfirst(x);
foreach (y, rel->joininfo) {
joininfo = (JInfo*)lfirst(y);
foreach (z, joininfo->jinfoclauseinfo) {
clauseinfo = (CInfo*)lfirst(z);
clause = clauseinfo->clause;
if(join_clause_p((Node*)clause)) {
MergeOrder *sortop = (MergeOrder*)NULL;
Oid hashop = (Oid)NULL;
if (_enable_mergesort_)
sortop = mergesortop(clause);
if (_enable_hashjoin_)
hashop = hashjoinop(clause);
if (sortop) {
clauseinfo->mergesortorder = sortop;
joininfo->mergesortable = true;
}
if (hashop) {
clauseinfo->hashjoinoperator = hashop;
joininfo->hashjoinable = true;
}
}
}
}
}
}
/*
* mergesortop--
* Returns the mergesort operator of an operator iff 'clause' is
* mergesortable, i.e., both operands are single vars and the operator is
* a mergesortable operator.
*/
static MergeOrder *
mergesortop(Expr *clause)
{
Oid leftOp, rightOp;
bool sortable;
sortable = op_mergesortable(((Oper*)clause->oper)->opno,
(get_leftop(clause))->vartype,
(get_rightop(clause))->vartype,
&leftOp,
&rightOp);
if (sortable) {
MergeOrder *morder = makeNode(MergeOrder);
morder->join_operator = ((Oper*)clause->oper)->opno;
morder->left_operator = leftOp;
morder->right_operator = rightOp;
morder->left_type = (get_leftop(clause))->vartype;
morder->right_type = (get_rightop(clause))->vartype;
return (morder);
} else
return(NULL);
}
/*
* hashjoinop--
* Returns the hashjoin operator of an operator iff 'clause' is
* hashjoinable, i.e., both operands are single vars and the operator is
* a hashjoinable operator.
*/
static Oid
hashjoinop(Expr *clause)
{
return(op_hashjoinable(((Oper*)clause->oper)->opno,
(get_leftop(clause))->vartype,
(get_rightop(clause))->vartype));
}

View File

@@ -0,0 +1,422 @@
/*-------------------------------------------------------------------------
*
* planmain.c--
* Routines to plan a single query
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/parsenodes.h"
#include "nodes/relation.h"
#include "optimizer/planmain.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/clauses.h"
#include "optimizer/keys.h"
#include "optimizer/tlist.h"
#include "optimizer/xfunc.h"
#include "optimizer/cost.h"
#include "tcop/dest.h"
#include "utils/elog.h"
#include "utils/palloc.h"
#include "nodes/memnodes.h"
#include "utils/mcxt.h"
#include "utils/lsyscache.h"
static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
static Plan *make_groupPlan(List *tlist, bool tuplePerGroup,
List *groupClause, Plan *subplan);
/*
* query_planner--
* Routine to create a query plan. It does so by first creating a
* subplan for the topmost level of attributes in the query. Then,
* it modifies all target list and qualifications to consider the next
* level of nesting and creates a plan for this modified query by
* recursively calling itself. The two pieces are then merged together
* by creating a result node that indicates which attributes should
* be placed where and any relation level qualifications to be
* satisfied.
*
* command-type is the query command, e.g., retrieve, delete, etc.
* tlist is the target list of the query
* qual is the qualification of the query
*
* Returns a query plan.
*/
Plan *
query_planner(Query *root,
int command_type,
List *tlist,
List *qual)
{
List *constant_qual = NIL;
List *flattened_tlist = NIL;
List *level_tlist = NIL;
Plan *subplan = (Plan*)NULL;
Agg *aggplan = NULL;
/*
* A command without a target list or qualification is an error,
* except for "delete foo".
*/
if (tlist==NIL && qual==NULL) {
if (command_type == CMD_DELETE ||
/* Total hack here. I don't know how to handle
statements like notify in action bodies.
Notify doesn't return anything but
scans a system table. */
command_type == CMD_NOTIFY) {
return ((Plan*)make_seqscan(NIL,
NIL,
root->resultRelation,
(Plan*)NULL));
} else
return((Plan*)NULL);
}
/*
* Pull out any non-variable qualifications so these can be put in
* the topmost result node. The opids for the remaining
* qualifications will be changed to regprocs later.
*/
qual = pull_constant_clauses(qual, &constant_qual);
fix_opids(constant_qual);
/*
* Create a target list that consists solely of (resdom var) target
* list entries, i.e., contains no arbitrary expressions.
*/
flattened_tlist = flatten_tlist(tlist);
if (flattened_tlist) {
level_tlist = flattened_tlist;
} else {
/* from old code. the logic is beyond me. - ay 2/95 */
level_tlist = tlist;
}
/*
* Needs to add the group attribute(s) to the target list so that they
* are available to either the Group node or the Agg node. (The target
* list may not contain the group attribute(s).)
*/
if (root->groupClause) {
AddGroupAttrToTlist(level_tlist, root->groupClause);
}
if (root->qry_aggs) {
aggplan = make_agg(tlist, root->qry_numAgg, root->qry_aggs);
tlist = level_tlist;
}
/*
* A query may have a non-variable target list and a non-variable
* qualification only under certain conditions:
* - the query creates all-new tuples, or
* - the query is a replace (a scan must still be done in this case).
*/
if (flattened_tlist==NULL && qual==NULL) {
switch (command_type) {
case CMD_SELECT:
case CMD_INSERT:
return ((Plan*)make_result(tlist,
(Node*)constant_qual,
(Plan*)NULL));
break;
case CMD_DELETE:
case CMD_UPDATE:
{
SeqScan *scan = make_seqscan(tlist,
(List *)NULL,
root->resultRelation,
(Plan*)NULL);
if (constant_qual!=NULL) {
return ((Plan*)make_result(tlist,
(Node*)constant_qual,
(Plan*)scan));
} else {
return ((Plan*)scan);
}
}
break;
default:
return ((Plan*)NULL);
}
}
/*
* Find the subplan (access path) and destructively modify the
* target list of the newly created subplan to contain the appropriate
* join references.
*/
subplan = subplanner(root, level_tlist, qual);
set_tlist_references(subplan);
/*
* If we have a GROUP BY clause, insert a group node (with the appropriate
* sort node.)
*/
if (root->groupClause != NULL) {
bool tuplePerGroup;
/*
* decide whether how many tuples per group the Group node needs
* to return. (Needs only one tuple per group if no aggregate is
* present. Otherwise, need every tuple from the group to do the
* aggregation.)
*/
tuplePerGroup = (aggplan == NULL) ? FALSE : TRUE;
subplan =
make_groupPlan(tlist, tuplePerGroup, root->groupClause, subplan);
/* XXX fake it: this works for the Group node too! very very ugly,
please change me -ay 2/95 */
set_agg_tlist_references((Agg*)subplan);
}
/*
* If aggregate is present, insert the agg node
*/
if (aggplan != NULL) {
aggplan->plan.lefttree = subplan;
subplan = (Plan*)aggplan;
/*
* set the varno/attno entries to the appropriate references to
* the result tuple of the subplans. (We need to set those in the
* array of aggreg's in the Agg node also. Even though they're
* pointers, after a few dozen's of copying, they're not the same as
* those in the target list.)
*/
set_agg_tlist_references((Agg*)subplan);
set_agg_agglist_references((Agg*)subplan);
tlist = aggplan->plan.targetlist;
}
/*
* Build a result node linking the plan if we have constant quals
*/
if (constant_qual) {
Plan *plan;
plan = (Plan*)make_result(tlist,
(Node*)constant_qual,
subplan);
/*
* Change all varno's of the Result's node target list.
*/
set_result_tlist_references((Result*)plan);
return (plan);
}
/*
* fix up the flattened target list of the plan root node so that
* expressions are evaluated. this forces expression evaluations
* that may involve expensive function calls to be delayed to
* the very last stage of query execution. this could be bad.
* but it is joey's responsibility to optimally push these
* expressions down the plan tree. -- Wei
*/
subplan->targetlist = flatten_tlist_vars(tlist,
subplan->targetlist);
/*
* Destructively modify the query plan's targetlist to add fjoin
* lists to flatten functions that return sets of base types
*/
subplan->targetlist = generate_fjoin(subplan->targetlist);
return (subplan);
}
/*
* subplanner
*
* Subplanner creates an entire plan consisting of joins and scans
* for processing a single level of attributes.
*
* flat-tlist is the flattened target list
* qual is the qualification to be satisfied
*
* Returns a subplan.
*
*/
static Plan *
subplanner(Query *root,
List *flat_tlist,
List *qual)
{
Rel *final_relation;
List *final_relation_list;
/* Initialize the targetlist and qualification, adding entries to
* *query-relation-list* as relation references are found (e.g., in the
* qualification, the targetlist, etc.)
*/
root->base_relation_list_ = NIL;
root->join_relation_list_ = NIL;
initialize_base_rels_list(root, flat_tlist);
initialize_base_rels_jinfo(root, qual);
add_missing_vars_to_base_rels(root, flat_tlist);
/* Find all possible scan and join paths.
* Mark all the clauses and relations that can be processed using special
* join methods, then do the exhaustive path search.
*/
initialize_join_clause_info(root->base_relation_list_);
final_relation_list = find_paths(root,
root->base_relation_list_);
if (final_relation_list)
final_relation = (Rel*)lfirst (final_relation_list);
else
final_relation = (Rel*)NIL;
#if 0 /* fix xfunc */
/*
* Perform Predicate Migration on each path, to optimize and correctly
* assess the cost of each before choosing the cheapest one.
* -- JMH, 11/16/92
*
* Needn't do so if the top rel is pruneable: that means there's no
* expensive functions left to pull up. -- JMH, 11/22/92
*/
if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM &&
XfuncMode != XFUNC_NOPULL && !final_relation->pruneable)
{
List *pathnode;
foreach(pathnode, final_relation->pathlist)
{
if (xfunc_do_predmig((Path*)lfirst(pathnode)))
set_cheapest(final_relation, final_relation->pathlist);
}
}
#endif
/*
* Determine the cheapest path and create a subplan corresponding to it.
*/
if (final_relation) {
return (create_plan ((Path*)final_relation->cheapestpath));
}else {
elog(NOTICE, "final relation is nil");
return(create_plan ((Path*)NULL));
}
}
/*****************************************************************************
*
*****************************************************************************/
static Result *
make_result(List *tlist,
Node *resconstantqual,
Plan *subplan)
{
Result *node = makeNode(Result);
Plan *plan = &node->plan;
tlist = generate_fjoin(tlist);
plan->cost = 0.0;
plan->state = (EState *)NULL;
plan->targetlist = tlist;
plan->lefttree = subplan;
plan->righttree = NULL;
node->resconstantqual = resconstantqual;
node->resstate = NULL;
return(node);
}
/*****************************************************************************
*
*****************************************************************************/
static Plan *
make_groupPlan(List *tlist,
bool tuplePerGroup,
List *groupClause,
Plan *subplan)
{
List *sort_tlist;
List *gl;
int keyno;
Sort *sortplan;
Group *grpplan;
int numCols;
AttrNumber *grpColIdx;
numCols = length(groupClause);
grpColIdx = (AttrNumber *)palloc(sizeof(AttrNumber)*numCols);
/*
* first, make a sort node. Group node expects the tuples it gets
* from the subplan is in the order as specified by the group columns.
*/
keyno = 1;
sort_tlist = new_unsorted_tlist(subplan->targetlist);
{
/* if this is a mergejoin node, varno could be OUTER/INNER */
List *l;
foreach(l, sort_tlist) {
TargetEntry *tle;
tle = lfirst(l);
((Var*)tle->expr)->varno = 1;
}
}
foreach (gl, groupClause) {
GroupClause *grpcl = (GroupClause*)lfirst(gl);
TargetEntry *tle;
tle = match_varid(grpcl->grpAttr, sort_tlist);
/*
* the parser should have checked to make sure the group attribute
* is valid but the optimizer might have screwed up and hence we
* check again.
*/
if (tle==NULL) {
elog(WARN, "group attribute disappeared from target list");
}
tle->resdom->reskey = keyno;
tle->resdom->reskeyop = get_opcode(grpcl->grpOpoid);
grpColIdx[keyno-1] = tle->resdom->resno;
keyno++;
}
sortplan = make_sort(sort_tlist,
_TEMP_RELATION_ID_,
subplan,
numCols);
sortplan->plan.cost = subplan->cost; /* XXX assume no cost */
/*
* make the Group node
*/
tlist = copyObject(tlist); /* make a copy */
grpplan = make_group(tlist, tuplePerGroup, numCols, grpColIdx, sortplan);
return (Plan*)grpplan;
}

View File

@@ -0,0 +1,408 @@
/*-------------------------------------------------------------------------
*
* planner.c--
* The query optimizer external interface.
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/parsenodes.h"
#include "nodes/relation.h"
#include "parser/catalog_utils.h"
#include "parser/parse_query.h"
#include "utils/elog.h"
#include "utils/lsyscache.h"
#include "access/heapam.h"
#include "optimizer/internal.h"
#include "optimizer/planner.h"
#include "optimizer/plancat.h"
#include "optimizer/prep.h"
#include "optimizer/planmain.h"
#include "optimizer/paths.h"
#include "optimizer/cost.h"
/* DATA STRUCTURE CREATION/MANIPULATION ROUTINES */
#include "nodes/relation.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/joininfo.h"
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/pathnode.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "executor/executor.h"
static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
static Plan *init_query_planner(Query *parse);
static Existential *make_existential(Plan *left, Plan *right);
/*****************************************************************************
*
* Query optimizer entry point
*
*****************************************************************************/
/*
* planner--
* Main query optimizer routine.
*
* Invokes the planner on union queries if there are any left,
* recursing if necessary to get them all, then processes normal plans.
*
* Returns a query plan.
*
*/
Plan*
planner(Query *parse)
{
List *tlist = parse->targetList;
List *rangetable = parse->rtable;
char* uniqueflag = parse->uniqueFlag;
List *sortclause = parse->sortClause;
Plan *special_plans = (Plan*)NULL;
Plan *result_plan = (Plan*) NULL;
int rt_index;
/*
* plan inheritance
*/
rt_index = first_matching_rt_entry(rangetable, INHERITS_FLAG);
if (rt_index != -1) {
special_plans = (Plan *)plan_union_queries((Index)rt_index,
parse,
INHERITS_FLAG);
}
/*
* plan archive queries
*/
rt_index = first_matching_rt_entry(rangetable, ARCHIVE_FLAG);
if (rt_index != -1) {
special_plans = (Plan *)plan_union_queries((Index)rt_index,
parse,
ARCHIVE_FLAG);
}
if (special_plans)
result_plan = special_plans;
else
result_plan = init_query_planner(parse); /* regular plans */
/*
* For now, before we hand back the plan, check to see if there
* is a user-specified sort that needs to be done. Eventually, this
* will be moved into the guts of the planner s.t. user specified
* sorts will be considered as part of the planning process.
* Since we can only make use of user-specified sorts in
* special cases, we can do the optimization step later.
*/
if (uniqueflag) {
Plan *sortplan = make_sortplan(tlist, sortclause, result_plan);
return((Plan*)make_unique(tlist,sortplan,uniqueflag));
} else {
if (sortclause)
return(make_sortplan(tlist,sortclause,result_plan));
else
return((Plan*)result_plan);
}
}
/*
* make_sortplan--
* Returns a sortplan which is basically a SORT node attached to the
* top of the plan returned from the planner. It also adds the
* cost of sorting into the plan.
*
* sortkeys: ( resdom1 resdom2 resdom3 ...)
* sortops: (sortop1 sortop2 sortop3 ...)
*/
static Plan *
make_sortplan(List *tlist, List *sortcls, Plan *plannode)
{
Plan *sortplan = (Plan*)NULL;
List *temp_tlist = NIL;
List *i = NIL;
Resdom *resnode = (Resdom*)NULL;
Resdom *resdom = (Resdom*)NULL;
int keyno =1;
/* First make a copy of the tlist so that we don't corrupt the
* the original .
*/
temp_tlist = new_unsorted_tlist(tlist);
foreach (i, sortcls) {
SortClause *sortcl = (SortClause*)lfirst(i);
resnode = sortcl->resdom;
resdom = tlist_resdom(temp_tlist, resnode);
/* Order the resdom keys and replace the operator OID for each
* key with the regproc OID.
*/
resdom->reskey = keyno;
resdom->reskeyop = get_opcode(sortcl->opoid);
keyno += 1;
}
sortplan = (Plan*)make_sort(temp_tlist,
_TEMP_RELATION_ID_,
(Plan*)plannode,
length(sortcls));
/*
* XXX Assuming that an internal sort has no. cost.
* This is wrong, but given that at this point, we don't
* know the no. of tuples returned, etc, we can't do
* better than to add a constant cost.
* This will be fixed once we move the sort further into the planner,
* but for now ... functionality....
*/
sortplan->cost = plannode->cost;
return(sortplan);
}
/*
* init-query-planner--
* Deals with all non-union preprocessing, including existential
* qualifications and CNFifying the qualifications.
*
* Returns a query plan.
* MODIFIES: tlist,qual
*
*/
static Plan *
init_query_planner(Query *root)
{
List *primary_qual;
List *existential_qual;
Existential *exist_plan;
List *tlist = root->targetList;
tlist = preprocess_targetlist(tlist,
root->commandType,
root->resultRelation,
root->rtable);
primary_qual =
preprocess_qualification((Expr*)root->qual,
tlist,
&existential_qual);
if(existential_qual==NULL) {
return(query_planner(root,
root->commandType,
tlist,
primary_qual));
} else {
int temp = root->commandType;
Plan *existential_plan;
root->commandType = CMD_SELECT;
existential_plan = query_planner(root,
temp,
NIL,
existential_qual);
exist_plan = make_existential(existential_plan,
query_planner(root,
root->commandType,
tlist,
primary_qual));
return((Plan*)exist_plan);
}
}
/*
* make_existential--
* Instantiates an existential plan node and fills in
* the left and right subtree slots.
*/
static Existential *
make_existential(Plan *left, Plan *right)
{
Existential *node = makeNode(Existential);
node->lefttree = left;
node->righttree = left;
return(node);
}
/*
* pg_checkretval() -- check return value of a list of sql parse
* trees.
*
* The return value of a sql function is the value returned by
* the final query in the function. We do some ad-hoc define-time
* type checking here to be sure that the user is returning the
* type he claims.
*/
void
pg_checkretval(Oid rettype, QueryTreeList *queryTreeList)
{
Query *parse;
List *tlist;
List *rt;
int cmd;
Type typ;
Resdom *resnode;
Relation reln;
Oid relid;
Oid tletype;
int relnatts;
int i;
/* find the final query */
parse = queryTreeList->qtrees[queryTreeList->len - 1];
/*
* test 1: if the last query is a utility invocation, then there
* had better not be a return value declared.
*/
if (parse->commandType == CMD_UTILITY) {
if (rettype == InvalidOid)
return;
else
elog(WARN, "return type mismatch in function decl: final query is a catalog utility");
}
/* okay, it's an ordinary query */
tlist = parse->targetList;
rt = parse->rtable;
cmd = parse->commandType;
/*
* test 2: if the function is declared to return no value, then the
* final query had better not be a retrieve.
*/
if (rettype == InvalidOid) {
if (cmd == CMD_SELECT)
elog(WARN,
"function declared with no return type, but final query is a retrieve");
else
return;
}
/* by here, the function is declared to return some type */
if ((typ = (Type)get_id_type(rettype)) == NULL)
elog(WARN, "can't find return type %d for function\n", rettype);
/*
* test 3: if the function is declared to return a value, then the
* final query had better be a retrieve.
*/
if (cmd != CMD_SELECT)
elog(WARN, "function declared to return type %s, but final query is not a retrieve", tname(typ));
/*
* test 4: for base type returns, the target list should have exactly
* one entry, and its type should agree with what the user declared.
*/
if (get_typrelid(typ) == InvalidOid) {
if (exec_tlist_length(tlist) > 1)
elog(WARN, "function declared to return %s returns multiple values in final retrieve", tname(typ));
resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
if (resnode->restype != rettype)
elog(WARN, "return type mismatch in function: declared to return %s, returns %s", tname(typ), tname(get_id_type(resnode->restype)));
/* by here, base return types match */
return;
}
/*
* If the target list is of length 1, and the type of the varnode
* in the target list is the same as the declared return type, this
* is okay. This can happen, for example, where the body of the
* function is 'retrieve (x = func2())', where func2 has the same
* return type as the function that's calling it.
*/
if (exec_tlist_length(tlist) == 1) {
resnode = (Resdom*) ((TargetEntry*)lfirst(tlist))->resdom;
if (resnode->restype == rettype)
return;
}
/*
* By here, the procedure returns a (set of) tuples. This part of
* the typechecking is a hack. We look up the relation that is
* the declared return type, and be sure that attributes 1 .. n
* in the target list match the declared types.
*/
reln = heap_open(get_typrelid(typ));
if (!RelationIsValid(reln))
elog(WARN, "cannot open relation relid %d", get_typrelid(typ));
relid = reln->rd_id;
relnatts = reln->rd_rel->relnatts;
if (exec_tlist_length(tlist) != relnatts)
elog(WARN, "function declared to return type %s does not retrieve (%s.*)", tname(typ), tname(typ));
/* expect attributes 1 .. n in order */
for (i = 1; i <= relnatts; i++) {
TargetEntry *tle = lfirst(tlist);
Node *thenode = tle->expr;
tlist = lnext(tlist);
tletype = exprType(thenode);
#if 0 /* fix me */
/* this is tedious */
if (IsA(thenode,Var))
tletype = (Oid) ((Var*)thenode)->vartype;
else if (IsA(thenode,Const))
tletype = (Oid) ((Const*)thenode)->consttype;
else if (IsA(thenode,Param)) {
tletype = (Oid) ((Param*)thenode)->paramtype;
else if (IsA(thenode,Expr)) {
tletype = Expr
}
} else if (IsA(thenode,LispList)) {
thenode = lfirst(thenode);
if (IsA(thenode,Oper))
tletype = (Oid) get_opresulttype((Oper*)thenode);
else if (IsA(thenode,Func))
tletype = (Oid) get_functype((Func*)thenode);
else
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
#endif
/*
} else
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
*/
/* reach right in there, why don't you? */
if (tletype != reln->rd_att->attrs[i-1]->atttypid)
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
}
heap_close(reln);
/* success */
return;
}

View File

@@ -0,0 +1,706 @@
/*-------------------------------------------------------------------------
*
* setrefs.c--
* Routines to change varno/attno entries to contain references
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.1.1.1 1996/07/09 06:21:37 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/plannodes.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "utils/elog.h"
#include "nodes/nodeFuncs.h"
#include "nodes/makefuncs.h"
#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/keys.h"
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
static void set_join_tlist_references(Join *join);
static void set_tempscan_tlist_references(SeqScan *tempscan);
static void set_temp_tlist_references(Temp *temp);
static List *replace_clause_joinvar_refs(Expr *clause,
List *outer_tlist, List *inner_tlist);
static List *replace_subclause_joinvar_refs(List *clauses,
List *outer_tlist, List *inner_tlist);
static Var *replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist);
static List *tlist_temp_references(Oid tempid, List *tlist);
static void replace_result_clause(List *clause, List *subplanTargetList);
static bool OperandIsInner(Node *opnd, int inner_relid);
static void replace_agg_clause(Node *expr, List *targetlist);
/*****************************************************************************
*
* SUBPLAN REFERENCES
*
*****************************************************************************/
/*
* set-tlist-references--
* Modifies the target list of nodes in a plan to reference target lists
* at lower levels.
*
* 'plan' is the plan whose target list and children's target lists will
* be modified
*
* Returns nothing of interest, but modifies internal fields of nodes.
*
*/
void
set_tlist_references(Plan *plan)
{
if(plan==NULL)
return;
if (IsA_Join(plan)) {
set_join_tlist_references((Join*)plan);
} else if (IsA(plan,SeqScan) && plan->lefttree &&
IsA_Temp(plan->lefttree)) {
set_tempscan_tlist_references((SeqScan*)plan);
} else if (IsA(plan,Sort)) {
set_temp_tlist_references ((Temp*)plan);
} else if (IsA(plan,Result)) {
set_result_tlist_references((Result*)plan);
} else if (IsA(plan,Hash)) {
set_tlist_references(plan->lefttree);
} else if (IsA(plan,Choose)) {
List *x;
foreach (x, ((Choose*)plan)->chooseplanlist) {
set_tlist_references((Plan*)lfirst(x));
}
}
}
/*
* set-join-tlist-references--
* Modifies the target list of a join node by setting the varnos and
* varattnos to reference the target list of the outer and inner join
* relations.
*
* Creates a target list for a join node to contain references by setting
* varno values to OUTER or INNER and setting attno values to the
* result domain number of either the corresponding outer or inner join
* tuple.
*
* 'join' is a join plan node
*
* Returns nothing of interest, but modifies internal fields of nodes.
*
*/
static void
set_join_tlist_references(Join *join)
{
Plan *outer = ((Plan*)join)->lefttree;
Plan *inner = ((Plan*)join)->righttree;
List *new_join_targetlist = NIL;
TargetEntry *temp = (TargetEntry *)NULL;
List *entry = NIL;
List *inner_tlist = NULL;
List *outer_tlist = NULL;
TargetEntry *xtl = (TargetEntry *)NULL;
List *qptlist = ((Plan*)join)->targetlist;
foreach(entry, qptlist) {
List *joinvar;
xtl = (TargetEntry *)lfirst(entry);
inner_tlist = ((inner==NULL) ? NIL : inner->targetlist);
outer_tlist = ((outer==NULL) ? NIL : outer->targetlist);
joinvar = replace_clause_joinvar_refs((Expr*)get_expr(xtl),
outer_tlist,
inner_tlist);
temp = MakeTLE(xtl->resdom, (Node*)joinvar);
new_join_targetlist = lappend(new_join_targetlist,temp);
}
((Plan*)join)->targetlist = new_join_targetlist;
if (outer!=NULL)
set_tlist_references(outer);
if (inner!=NULL)
set_tlist_references(inner);
}
/*
* set-tempscan-tlist-references--
* Modifies the target list of a node that scans a temp relation (i.e., a
* sort or hash node) so that the varnos refer to the child temporary.
*
* 'tempscan' is a seqscan node
*
* Returns nothing of interest, but modifies internal fields of nodes.
*
*/
static void
set_tempscan_tlist_references(SeqScan *tempscan)
{
Temp *temp = (Temp*)((Plan*)tempscan)->lefttree;
((Plan*)tempscan)->targetlist =
tlist_temp_references(temp->tempid,
((Plan*)tempscan)->targetlist);
set_temp_tlist_references(temp);
}
/*
* set-temp-tlist-references--
* The temp's vars are made consistent with (actually, identical to) the
* modified version of the target list of the node from which temp node
* receives its tuples.
*
* 'temp' is a temp (e.g., sort, hash) plan node
*
* Returns nothing of interest, but modifies internal fields of nodes.
*
*/
static void
set_temp_tlist_references(Temp *temp)
{
Plan *source = ((Plan*)temp)->lefttree;
if (source!=NULL) {
set_tlist_references(source);
((Plan*)temp)->targetlist =
copy_vars(((Plan*)temp)->targetlist ,
(source)->targetlist);
} else {
elog(WARN, "calling set_temp_tlist_references with empty lefttree");
}
}
/*
* join-references--
* Creates a new set of join clauses by replacing the varno/varattno
* values of variables in the clauses to reference target list values
* from the outer and inner join relation target lists.
*
* 'clauses' is the list of join clauses
* 'outer-tlist' is the target list of the outer join relation
* 'inner-tlist' is the target list of the inner join relation
*
* Returns the new join clauses.
*
*/
List *
join_references(List *clauses,
List *outer_tlist,
List *inner_tlist)
{
return (replace_subclause_joinvar_refs(clauses,
outer_tlist,
inner_tlist));
}
/*
* index-outerjoin-references--
* Given a list of join clauses, replace the operand corresponding to the
* outer relation in the join with references to the corresponding target
* list element in 'outer-tlist' (the outer is rather obscurely
* identified as the side that doesn't contain a var whose varno equals
* 'inner-relid').
*
* As a side effect, the operator is replaced by the regproc id.
*
* 'inner-indxqual' is the list of join clauses (so-called because they
* are used as qualifications for the inner (inbex) scan of a nestloop)
*
* Returns the new list of clauses.
*
*/
List *
index_outerjoin_references(List *inner_indxqual,
List *outer_tlist,
Index inner_relid)
{
List *t_list = NIL;
Expr *temp = NULL;
List *t_clause = NIL;
Expr *clause = NULL;
foreach (t_clause,inner_indxqual) {
clause = lfirst(t_clause);
/*
* if inner scan on the right.
*/
if (OperandIsInner((Node*)get_rightop(clause), inner_relid)) {
Var *joinvar = (Var*)
replace_clause_joinvar_refs((Expr*)get_leftop(clause),
outer_tlist,
NIL);
temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
joinvar,
get_rightop(clause));
t_list = lappend(t_list,temp);
} else {
/* inner scan on left */
Var *joinvar = (Var*)
replace_clause_joinvar_refs((Expr*)get_rightop(clause),
outer_tlist,
NIL);
temp = make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
joinvar,
get_leftop(clause));
t_list = lappend(t_list,temp);
}
}
return(t_list);
}
/*
* replace-clause-joinvar-refs
* replace-subclause-joinvar-refs
* replace-joinvar-refs
*
* Replaces all variables within a join clause with a new var node
* whose varno/varattno fields contain a reference to a target list
* element from either the outer or inner join relation.
*
* 'clause' is the join clause
* 'outer-tlist' is the target list of the outer join relation
* 'inner-tlist' is the target list of the inner join relation
*
* Returns the new join clause.
*
*/
static List *
replace_clause_joinvar_refs(Expr *clause,
List *outer_tlist,
List *inner_tlist)
{
List *temp = NULL;
if(IsA (clause,Var)) {
temp = (List*)replace_joinvar_refs((Var*)clause,
outer_tlist,inner_tlist);
if(temp)
return(temp);
else
if (clause != NULL)
return((List*)clause);
else
return(NIL);
} else if (single_node((Node*)clause)) {
return ((List*)clause);
} else if (or_clause((Node*)clause)) {
List *orclause =
replace_subclause_joinvar_refs(((Expr*)clause)->args,
outer_tlist,
inner_tlist);
return ((List*)make_orclause(orclause));
} else if (IsA(clause,ArrayRef)) {
ArrayRef *aref = (ArrayRef *)clause;
temp = replace_subclause_joinvar_refs(aref->refupperindexpr,
outer_tlist,
inner_tlist);
aref->refupperindexpr = (List*)temp;
temp = replace_subclause_joinvar_refs(aref->reflowerindexpr,
outer_tlist,
inner_tlist);
aref->reflowerindexpr = (List*)temp;
temp = replace_clause_joinvar_refs((Expr*)aref->refexpr,
outer_tlist,
inner_tlist);
aref->refexpr = (Node*)temp;
/*
* no need to set refassgnexpr. we only set that in the
* target list on replaces, and this is an array reference
* in the qualification. if we got this far, it's 0x0 in
* the ArrayRef structure 'clause'.
*/
return((List*)clause);
} else if (is_funcclause((Node*)clause)) {
List *funcclause =
replace_subclause_joinvar_refs(((Expr*)clause)->args,
outer_tlist,
inner_tlist);
return ((List*)make_funcclause((Func*)((Expr*)clause)->oper,
funcclause));
} else if (not_clause((Node*)clause)) {
List *notclause =
replace_clause_joinvar_refs(get_notclausearg(clause),
outer_tlist,
inner_tlist);
return ((List*)make_notclause((Expr*)notclause));
} else if (is_opclause((Node*)clause)) {
Var *leftvar =
(Var*)replace_clause_joinvar_refs((Expr*)get_leftop(clause),
outer_tlist,
inner_tlist);
Var *rightvar =
(Var*)replace_clause_joinvar_refs((Expr*)get_rightop(clause),
outer_tlist,
inner_tlist);
return ((List*)make_opclause(replace_opid((Oper*)((Expr*)clause)->oper),
leftvar,
rightvar));
}
/* shouldn't reach here */
return NULL;
}
static List *
replace_subclause_joinvar_refs(List *clauses,
List *outer_tlist,
List *inner_tlist)
{
List *t_list = NIL;
List *temp = NIL;
List *clause = NIL;
foreach (clause,clauses) {
temp = replace_clause_joinvar_refs(lfirst(clause),
outer_tlist,
inner_tlist);
t_list = lappend(t_list,temp);
}
return(t_list);
}
static Var *
replace_joinvar_refs(Var *var, List *outer_tlist, List *inner_tlist)
{
Resdom *outer_resdom =(Resdom*)NULL;
outer_resdom= tlist_member(var,outer_tlist);
if (outer_resdom!=NULL && IsA (outer_resdom,Resdom) ) {
return (makeVar (OUTER,
outer_resdom->resno,
var->vartype,
var->varnoold,
var->varoattno));
} else {
Resdom *inner_resdom;
inner_resdom = tlist_member(var,inner_tlist);
if ( inner_resdom!=NULL && IsA (inner_resdom,Resdom) ) {
return (makeVar (INNER,
inner_resdom->resno,
var->vartype,
var->varnoold,
var->varoattno));
}
}
return (Var*)NULL;
}
/*
* tlist-temp-references--
* Creates a new target list for a node that scans a temp relation,
* setting the varnos to the id of the temp relation and setting varids
* if necessary (varids are only needed if this is a targetlist internal
* to the tree, in which case the targetlist entry always contains a var
* node, so we can just copy it from the temp).
*
* 'tempid' is the id of the temp relation
* 'tlist' is the target list to be modified
*
* Returns new target list
*
*/
static List *
tlist_temp_references(Oid tempid,
List *tlist)
{
List *t_list = NIL;
TargetEntry *temp = (TargetEntry *)NULL;
TargetEntry *xtl = NULL;
List *entry;
foreach (entry, tlist) {
AttrNumber oattno;
xtl = lfirst(entry);
if (IsA(get_expr(xtl), Var))
oattno = ((Var*)xtl->expr)->varoattno;
else
oattno = 0;
temp = MakeTLE(xtl->resdom,
(Node*)makeVar(tempid,
xtl->resdom->resno,
xtl->resdom->restype,
tempid,
oattno));
t_list = lappend(t_list,temp);
}
return(t_list);
}
/*---------------------------------------------------------
*
* set_result_tlist_references
*
* Change the target list of a Result node, so that it correctly
* addresses the tuples returned by its left tree subplan.
*
* NOTE:
* 1) we ignore the right tree! (in the current implementation
* it is always nil
* 2) this routine will probably *NOT* work with nested dot
* fields....
*/
void
set_result_tlist_references(Result *resultNode)
{
Plan *subplan;
List *resultTargetList;
List *subplanTargetList;
List *t;
TargetEntry *entry;
Expr *expr;
resultTargetList= ((Plan*)resultNode)->targetlist;
/*
* NOTE: we only consider the left tree subplan.
* This is usually a seq scan.
*/
subplan = ((Plan*)resultNode)->lefttree;
if (subplan != NULL) {
subplanTargetList = subplan->targetlist;
} else {
subplanTargetList = NIL;
}
/*
* now for traverse all the entris of the target list.
* These should be of the form (Resdom_Node Expression).
* For every expression clause, call "replace_result_clause()"
* to appropriatelly change all the Var nodes.
*/
foreach (t, resultTargetList) {
entry = (TargetEntry *)lfirst(t);
expr = (Expr*) get_expr(entry);
replace_result_clause((List*)expr, subplanTargetList);
}
}
/*---------------------------------------------------------
*
* replace_result_clause
*
* This routine is called from set_result_tlist_references().
* and modifies the expressions of the target list of a Result
* node so that all Var nodes reference the target list of its subplan.
*
*/
static void
replace_result_clause(List *clause,
List *subplanTargetList) /* target list of the
subplan */
{
List *t;
List *subClause;
TargetEntry *subplanVar;
if (IsA(clause,Var)) {
/*
* Ha! A Var node!
*/
subplanVar = match_varid((Var*)clause, subplanTargetList);
/*
* Change the varno & varattno fields of the
* var node.
*
*/
((Var*)clause)->varno = (Index)OUTER;
((Var*)clause)->varattno = subplanVar->resdom->resno;
} else if (is_funcclause((Node*)clause)) {
/*
* This is a function. Recursively call this routine
* for its arguments...
*/
subClause = ((Expr*)clause)->args;
foreach (t, subClause) {
replace_result_clause(lfirst(t),subplanTargetList);
}
} else if (IsA(clause,ArrayRef)) {
ArrayRef *aref = (ArrayRef *)clause;
/*
* This is an arrayref. Recursively call this routine
* for its expression and its index expression...
*/
subClause = aref->refupperindexpr;
foreach (t, subClause) {
replace_result_clause(lfirst(t),subplanTargetList);
}
subClause = aref->reflowerindexpr;
foreach (t, subClause) {
replace_result_clause(lfirst(t),subplanTargetList);
}
replace_result_clause((List*)aref->refexpr,
subplanTargetList);
replace_result_clause((List*)aref->refassgnexpr,
subplanTargetList);
} else if (is_opclause((Node*)clause)) {
/*
* This is an operator. Recursively call this routine
* for both its left and right operands
*/
subClause = (List*)get_leftop((Expr*)clause);
replace_result_clause(subClause,subplanTargetList);
subClause = (List*)get_rightop((Expr*)clause);
replace_result_clause(subClause,subplanTargetList);
} else if (IsA(clause,Param) || IsA(clause,Const)) {
/* do nothing! */
} else {
/*
* Ooops! we can not handle that!
*/
elog(WARN,"replace_result_clause: Can not handle this tlist!\n");
}
}
static
bool OperandIsInner(Node *opnd, int inner_relid)
{
/*
* Can be the inner scan if its a varnode or a function and the
* inner_relid is equal to the varnode's var number or in the
* case of a function the first argument's var number (all args
* in a functional index are from the same relation).
*/
if ( IsA (opnd,Var) &&
(inner_relid == ((Var*)opnd)->varno) )
{
return true;
}
if (is_funcclause(opnd))
{
List *firstArg = lfirst(((Expr*)opnd)->args);
if ( IsA (firstArg,Var) &&
(inner_relid == ((Var*)firstArg)->varno) )
{
return true;
}
}
return false;
}
/*****************************************************************************
*
*****************************************************************************/
/*---------------------------------------------------------
*
* set_agg_tlist_references -
* changes the target list of an Agg node so that it points to
* the tuples returned by its left tree subplan.
*
*/
void
set_agg_tlist_references(Agg *aggNode)
{
List *aggTargetList;
List *subplanTargetList;
List *tl;
aggTargetList = aggNode->plan.targetlist;
subplanTargetList = aggNode->plan.lefttree->targetlist;
foreach (tl, aggTargetList) {
TargetEntry *tle = lfirst(tl);
replace_agg_clause(tle->expr, subplanTargetList);
}
}
void
set_agg_agglist_references(Agg *aggNode)
{
List *subplanTargetList;
Aggreg **aggs;
int i;
aggs = aggNode->aggs;
subplanTargetList = aggNode->plan.lefttree->targetlist;
for (i = 0; i < aggNode->numAgg; i++) {
replace_agg_clause(aggs[i]->target, subplanTargetList);
}
}
static void
replace_agg_clause(Node *clause, List *subplanTargetList)
{
List *t;
TargetEntry *subplanVar;
if (IsA(clause,Var)) {
/*
* Ha! A Var node!
*/
subplanVar = match_varid((Var*)clause, subplanTargetList);
/*
* Change the varno & varattno fields of the
* var node.
*
*/
((Var*)clause)->varattno = subplanVar->resdom->resno;
} else if (is_funcclause(clause)) {
/*
* This is a function. Recursively call this routine
* for its arguments...
*/
foreach (t, ((Expr*)clause)->args) {
replace_agg_clause(lfirst(t), subplanTargetList);
}
} else if (IsA(clause,Aggreg)) {
replace_agg_clause(((Aggreg*)clause)->target, subplanTargetList);
} else if (IsA(clause,ArrayRef)) {
ArrayRef *aref = (ArrayRef *)clause;
/*
* This is an arrayref. Recursively call this routine
* for its expression and its index expression...
*/
foreach (t, aref->refupperindexpr) {
replace_agg_clause(lfirst(t),subplanTargetList);
}
foreach (t, aref->reflowerindexpr) {
replace_agg_clause(lfirst(t),subplanTargetList);
}
replace_agg_clause(aref->refexpr, subplanTargetList);
replace_agg_clause(aref->refassgnexpr, subplanTargetList);
} else if (is_opclause(clause)) {
/*
* This is an operator. Recursively call this routine
* for both its left and right operands
*/
replace_agg_clause((Node*)get_leftop((Expr*)clause),
subplanTargetList);
replace_agg_clause((Node*)get_rightop((Expr*)clause),
subplanTargetList);
} else if (IsA(clause,Param) || IsA(clause,Const)) {
/* do nothing! */
} else {
/*
* Ooops! we can not handle that!
*/
elog(WARN,"replace_agg_clause: Can not handle this tlist!\n");
}
}