mirror of
https://github.com/postgres/postgres.git
synced 2025-11-13 16:22:44 +03:00
Massive commit to run PGINDENT on all *.c and *.h files.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* initsplan.c--
|
||||
* Target list, qualification, joininfo initialization routines
|
||||
* 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.5 1997/04/24 16:04:23 vadim Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.6 1997/09/07 04:44:00 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -33,370 +33,397 @@
|
||||
#include "optimizer/clauses.h"
|
||||
#include "optimizer/cost.h"
|
||||
|
||||
extern int Quiet;
|
||||
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 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);
|
||||
static MergeOrder *mergesortop(Expr * clause);
|
||||
static Oid hashjoinop(Expr * clause);
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* TARGET LISTS
|
||||
* 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.
|
||||
* 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)
|
||||
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);
|
||||
List *tlist_vars = NIL;
|
||||
List *l = NIL;
|
||||
List *tvar = NIL;
|
||||
|
||||
tlist_vars = append(tlist_vars, pull_var_clause(entry->expr));
|
||||
}
|
||||
foreach(l, tlist)
|
||||
{
|
||||
TargetEntry *entry = (TargetEntry *) lfirst(l);
|
||||
|
||||
/* 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);
|
||||
tlist_vars = append(tlist_vars, pull_var_clause(entry->expr));
|
||||
}
|
||||
|
||||
add_tl_element(result, var);
|
||||
}
|
||||
/* 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.
|
||||
* 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)
|
||||
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;
|
||||
List *l;
|
||||
int varno;
|
||||
|
||||
relids = lconsi(varno, NIL);
|
||||
if (rte->inFromCl &&
|
||||
!rel_member(relids, root->base_relation_list_)) {
|
||||
varno = 1;
|
||||
foreach(l, root->rtable)
|
||||
{
|
||||
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
|
||||
List *relids;
|
||||
Rel *result;
|
||||
Var *var;
|
||||
|
||||
var = makeVar(varno, -2 , 26, varno, -2);
|
||||
/* add it to base_relation_list_ */
|
||||
result = get_base_rel(root, varno);
|
||||
add_tl_element(result, 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++;
|
||||
}
|
||||
pfree(relids);
|
||||
varno++;
|
||||
}
|
||||
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
* QUALIFICATIONS
|
||||
* 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.
|
||||
* 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)
|
||||
initialize_base_rels_jinfo(Query * root, List * clauses)
|
||||
{
|
||||
List *clause;
|
||||
List *clause;
|
||||
|
||||
foreach (clause, clauses) {
|
||||
add_clause_to_rels(root, lfirst(clause));
|
||||
}
|
||||
return;
|
||||
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.
|
||||
* 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)
|
||||
add_clause_to_rels(Query * root, List * clause)
|
||||
{
|
||||
List *relids;
|
||||
List *vars;
|
||||
CInfo *clauseinfo = makeNode(CInfo);
|
||||
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.
|
||||
* 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));
|
||||
|
||||
/* 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.
|
||||
* There is only one relation participating in 'clause', so
|
||||
* 'clause' must be a restriction clause.
|
||||
*/
|
||||
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.
|
||||
* the selectivity of the clause must be computed regardless of
|
||||
* whether it's a restriction or a join clause
|
||||
*/
|
||||
clauseinfo->selectivity = (Cost)0.3333333;
|
||||
}
|
||||
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
|
||||
{
|
||||
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);
|
||||
}
|
||||
{
|
||||
|
||||
/*
|
||||
* '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).
|
||||
*
|
||||
* 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)
|
||||
add_join_clause_info_to_rels(Query * root, CInfo * clauseinfo, List * join_relids)
|
||||
{
|
||||
List *join_relid;
|
||||
List *join_relid;
|
||||
|
||||
foreach (join_relid, join_relids) {
|
||||
JInfo *joininfo;
|
||||
List *other_rels = NIL;
|
||||
List *rel;
|
||||
|
||||
foreach (rel, join_relids)
|
||||
{
|
||||
if ( lfirsti(rel) != lfirsti(join_relid) )
|
||||
other_rels = lappendi (other_rels, lfirsti(rel));
|
||||
}
|
||||
|
||||
joininfo =
|
||||
find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
|
||||
other_rels);
|
||||
joininfo->jinfoclauseinfo =
|
||||
lcons(copyObject((void*)clauseinfo), joininfo->jinfoclauseinfo);
|
||||
foreach(join_relid, join_relids)
|
||||
{
|
||||
JInfo *joininfo;
|
||||
List *other_rels = NIL;
|
||||
List *rel;
|
||||
|
||||
}
|
||||
foreach(rel, join_relids)
|
||||
{
|
||||
if (lfirsti(rel) != lfirsti(join_relid))
|
||||
other_rels = lappendi(other_rels, lfirsti(rel));
|
||||
}
|
||||
|
||||
joininfo =
|
||||
find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
|
||||
other_rels);
|
||||
joininfo->jinfoclauseinfo =
|
||||
lcons(copyObject((void *) 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.
|
||||
* 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)
|
||||
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);
|
||||
}
|
||||
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
|
||||
* 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.
|
||||
* 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)
|
||||
initialize_join_clause_info(List * rel_list)
|
||||
{
|
||||
List *x, *y, *z;
|
||||
Rel *rel;
|
||||
JInfo *joininfo;
|
||||
CInfo *clauseinfo;
|
||||
Expr *clause;
|
||||
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;
|
||||
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 (_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;
|
||||
}
|
||||
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.
|
||||
* 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)
|
||||
mergesortop(Expr * clause)
|
||||
{
|
||||
Oid leftOp, rightOp;
|
||||
bool sortable;
|
||||
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);
|
||||
sortable = op_mergesortable(((Oper *) clause->oper)->opno,
|
||||
(get_leftop(clause))->vartype,
|
||||
(get_rightop(clause))->vartype,
|
||||
&leftOp,
|
||||
&rightOp);
|
||||
|
||||
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);
|
||||
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.
|
||||
* 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)
|
||||
static Oid
|
||||
hashjoinop(Expr * clause)
|
||||
{
|
||||
return(op_hashjoinable(((Oper*)clause->oper)->opno,
|
||||
(get_leftop(clause))->vartype,
|
||||
(get_rightop(clause))->vartype));
|
||||
return (op_hashjoinable(((Oper *) clause->oper)->opno,
|
||||
(get_leftop(clause))->vartype,
|
||||
(get_rightop(clause))->vartype));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* planner.c--
|
||||
* The query optimizer external interface.
|
||||
* 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.6 1997/09/05 20:20:48 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.7 1997/09/07 04:44:03 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include "optimizer/internal.h"
|
||||
#include "optimizer/planner.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/plancat.h"
|
||||
#include "optimizer/prep.h"
|
||||
#include "optimizer/planmain.h"
|
||||
#include "optimizer/paths.h"
|
||||
@@ -47,215 +47,225 @@
|
||||
|
||||
#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);
|
||||
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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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)
|
||||
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;
|
||||
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;
|
||||
Plan *result_plan = (Plan *) NULL;
|
||||
|
||||
int rt_index;
|
||||
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 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);
|
||||
}
|
||||
/*
|
||||
* 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 (special_plans)
|
||||
result_plan = special_plans;
|
||||
else
|
||||
result_plan = init_query_planner(parse); /* regular plans */
|
||||
|
||||
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);
|
||||
}
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* 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)
|
||||
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;
|
||||
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.
|
||||
/*
|
||||
* First make a copy of the tlist so that we don't corrupt the the
|
||||
* original .
|
||||
*/
|
||||
resdom->reskey = keyno;
|
||||
resdom->reskeyop = get_opcode(sortcl->opoid);
|
||||
keyno += 1;
|
||||
}
|
||||
|
||||
sortplan = (Plan*)make_sort(temp_tlist,
|
||||
_TEMP_RELATION_ID_,
|
||||
(Plan*)plannode,
|
||||
length(sortcls));
|
||||
temp_tlist = new_unsorted_tlist(tlist);
|
||||
|
||||
/*
|
||||
* 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....
|
||||
*/
|
||||
foreach(i, sortcls)
|
||||
{
|
||||
SortClause *sortcl = (SortClause *) lfirst(i);
|
||||
|
||||
sortplan->cost = plannode->cost;
|
||||
|
||||
return(sortplan);
|
||||
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.
|
||||
*
|
||||
* 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)
|
||||
static Plan *
|
||||
init_query_planner(Query * root)
|
||||
{
|
||||
List *primary_qual;
|
||||
List *existential_qual;
|
||||
Existential *exist_plan;
|
||||
List *tlist = root->targetList;
|
||||
List *primary_qual;
|
||||
List *existential_qual;
|
||||
Existential *exist_plan;
|
||||
List *tlist = root->targetList;
|
||||
|
||||
tlist = preprocess_targetlist(tlist,
|
||||
root->commandType,
|
||||
root->resultRelation,
|
||||
root->rtable);
|
||||
tlist = preprocess_targetlist(tlist,
|
||||
root->commandType,
|
||||
root->resultRelation,
|
||||
root->rtable);
|
||||
|
||||
primary_qual =
|
||||
preprocess_qualification((Expr*)root->qual,
|
||||
tlist,
|
||||
&existential_qual);
|
||||
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;
|
||||
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);
|
||||
}
|
||||
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.
|
||||
* Instantiates an existential plan node and fills in
|
||||
* the left and right subtree slots.
|
||||
*/
|
||||
static Existential *
|
||||
make_existential(Plan *left, Plan *right)
|
||||
make_existential(Plan * left, Plan * right)
|
||||
{
|
||||
Existential *node = makeNode(Existential);
|
||||
Existential *node = makeNode(Existential);
|
||||
|
||||
node->lefttree = left;
|
||||
node->righttree = left;
|
||||
return(node);
|
||||
node->lefttree = left;
|
||||
node->righttree = left;
|
||||
return (node);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_checkretval() -- check return value of a list of sql parse
|
||||
* trees.
|
||||
* 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
|
||||
@@ -263,145 +273,152 @@ make_existential(Plan *left, Plan *right)
|
||||
* type he claims.
|
||||
*/
|
||||
void
|
||||
pg_checkretval(Oid rettype, QueryTreeList *queryTreeList)
|
||||
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;
|
||||
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];
|
||||
/* 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) {
|
||||
/*
|
||||
* 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)
|
||||
return;
|
||||
else
|
||||
elog(WARN, "return type mismatch in function decl: final query is a catalog utility");
|
||||
}
|
||||
{
|
||||
if (cmd == CMD_SELECT)
|
||||
elog(WARN,
|
||||
"function declared with no return type, but final query is a retrieve");
|
||||
else
|
||||
return;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
/* 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 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
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)));
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
/*
|
||||
* 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));
|
||||
|
||||
if (!RelationIsValid(reln))
|
||||
elog(WARN, "cannot open relation relid %d", get_typrelid(typ));
|
||||
relid = reln->rd_id;
|
||||
relnatts = reln->rd_rel->relnatts;
|
||||
|
||||
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));
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
|
||||
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));
|
||||
} else
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
|
||||
#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));
|
||||
}
|
||||
else
|
||||
elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ));
|
||||
#endif
|
||||
/* 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));
|
||||
}
|
||||
/* 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);
|
||||
heap_close(reln);
|
||||
|
||||
/* success */
|
||||
return;
|
||||
/* success */
|
||||
return;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user