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:
15
src/backend/optimizer/plan/Makefile.inc
Normal file
15
src/backend/optimizer/plan/Makefile.inc
Normal 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
|
||||
1097
src/backend/optimizer/plan/createplan.c
Normal file
1097
src/backend/optimizer/plan/createplan.c
Normal file
File diff suppressed because it is too large
Load Diff
391
src/backend/optimizer/plan/initsplan.c
Normal file
391
src/backend/optimizer/plan/initsplan.c
Normal 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));
|
||||
}
|
||||
422
src/backend/optimizer/plan/planmain.c
Normal file
422
src/backend/optimizer/plan/planmain.c
Normal 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;
|
||||
}
|
||||
408
src/backend/optimizer/plan/planner.c
Normal file
408
src/backend/optimizer/plan/planner.c
Normal 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;
|
||||
}
|
||||
706
src/backend/optimizer/plan/setrefs.c
Normal file
706
src/backend/optimizer/plan/setrefs.c
Normal 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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user