Newer
Older
/*-------------------------------------------------------------------------
*
* tlist.c--
* Target list manipulation routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.19 1998/09/22 20:28:07 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "nodes/pg_list.h"
#include "nodes/nodeFuncs.h"
#include "utils/elog.h"
#include "utils/lsyscache.h"
#include "optimizer/internal.h"
#include "optimizer/var.h"
#include "optimizer/tlist.h"
#include "optimizer/clauses.h"
#include "nodes/makefuncs.h"
static Node *flatten_tlistentry(Node *tlistentry, List *flat_tlist);
/*****************************************************************************
* ---------- RELATION node target list routines ----------
*****************************************************************************/
* RETURNS: the leftmost member of sequence "targetlist" that satisfies
* the predicate "var_equal"
* MODIFIES: nothing
* REQUIRES: test = function which can operate on a lispval union
* var = valid var-node
* targetlist = valid sequence
Bruce Momjian
committed
TargetEntry *
tlistentry_member(Var *var, List *targetlist)
Bruce Momjian
committed
List *temp = NIL;
foreach(temp, targetlist)
{
if (var_equal(var,
get_expr(lfirst(temp))))
* RETURNS: var node in a target list which is var_equal to 'var',
* if one exists.
* REQUIRES: "test" operates on lispval unions,
matching_tlvar(Var *var, List *targetlist)
Bruce Momjian
committed
TargetEntry *tlentry;
tlentry = tlistentry_member(var, targetlist);
if (tlentry)
* Creates a targetlist entry corresponding to the supplied var node
*
* 'var' and adds the new targetlist entry to the targetlist field of
* RETURNS: nothing
* MODIFIES: vartype and varid fields of leftmost varnode that matches
* argument "var" (sometimes).
* CREATES: new var-node iff no matching var-node exists in targetlist
*/
void
add_tl_element(RelOptInfo * rel, Var *var)
Bruce Momjian
committed
Expr *oldvar = (Expr *) NULL;
oldvar = matching_tlvar(var, rel->targetlist);
/*
* If 'var' is not already in 'rel's target list, add a new node.
*/
if (oldvar == NULL)
{
Bruce Momjian
committed
List *tlist = rel->targetlist;
Var *newvar = makeVar(var->varno,
var->varattno,
var->vartype,
var->vartypmod,
Bruce Momjian
committed
var->varno,
var->varoattno);
rel->targetlist =
lappend(tlist,
create_tl_element(newvar,
length(tlist) + 1));
}
* Creates a target list entry node and its associated (resdom var) pair
* with its resdom number equal to 'resdomno' and the joinlist field set
* to 'joinlist'.
*
* RETURNS: newly created tlist-entry
* CREATES: new targetlist entry (always).
*/
Bruce Momjian
committed
TargetEntry *
create_tl_element(Var *var, int resdomno)
return makeTargetEntry(makeResdom(resdomno,
var->vartype,
var->vartypmod,
NULL,
(Index) 0,
(Oid) 0,
0),
(Node *) var);
* Returns the targetlist elements from a relation tlist.
*
get_actual_tlist(List *tlist)
/*
* this function is not making sense. - ay 10/94
*/
#if 0
Bruce Momjian
committed
List *element = NIL;
List *result = NIL;
if (tlist == NULL)
{
elog(DEBUG, "calling get_actual_tlist with empty tlist");
}
/*
* XXX - it is unclear to me what exactly get_entry should be doing,
* as it is unclear to me the exact relationship between "TL" "TLE"
* and joinlists
*/
foreach(element, tlist)
result = lappend(result, lfirst((List *) lfirst(element)));
}
/*****************************************************************************
* ---------- GENERAL target list routines ----------
*****************************************************************************/
* Determines whether a var node is already contained within a
* target list.
*
* 'var' is the var node
* 'tlist' is the target list
* 'dots' is t if we must match dotfields to determine uniqueness
* Returns the resdom entry of the matching var node.
Resdom *
tlist_member(Var *var, List *tlist)
Bruce Momjian
committed
List *i = NIL;
TargetEntry *temp_tle = (TargetEntry *) NULL;
TargetEntry *tl_elt = (TargetEntry *) NULL;
if (var)
{
foreach(i, tlist)
{
temp_tle = (TargetEntry *) lfirst(i);
if (var_equal(var, get_expr(temp_tle)))
{
tl_elt = temp_tle;
break;
}
}
if (tl_elt != NULL)
* Routine to get the resdom out of a targetlist.
Resdom *
tlist_resdom(List *tlist, Resdom *resnode)
Bruce Momjian
committed
Resdom *resdom = (Resdom *) NULL;
List *i = NIL;
TargetEntry *temp_tle = (TargetEntry *) NULL;
foreach(i, tlist)
{
temp_tle = (TargetEntry *) lfirst(i);
resdom = temp_tle->resdom;
/* Since resnos are supposed to be unique */
if (resnode->resno == resdom->resno)
* Searches a target list for an entry with some desired varid.
*
* 'varid' is the desired id
* 'tlist' is the target list that is searched
* Returns the target list entry (resdom var) of the matching var.
*
* Now checks to make sure array references (in addition to range
* table indices) are identical - retrieve (a.b[1],a.b[2]) should
* not be turned into retrieve (a.b[1],a.b[1]).
* [what used to be varid is now broken up into two fields varnoold and
* varoattno. Also, nested attnos are long gone. - ay 2/95]
Bruce Momjian
committed
TargetEntry *
match_varid(Var *test_var, List *tlist)
Bruce Momjian
committed
List *tl;
Oid type_var;
type_var = (Oid) test_var->vartype;
Assert(test_var->varlevelsup == 0);
foreach(tl, tlist)
{
Bruce Momjian
committed
TargetEntry *entry;
Var *tlvar;
entry = lfirst(tl);
tlvar = get_expr(entry);
if (!IsA(tlvar, Var))
continue;
/*
* we test the original varno (instead of varno which might be
* changed to INNER/OUTER.
*/
Assert(tlvar->varlevelsup == 0);
if (tlvar->varnoold == test_var->varnoold &&
tlvar->varoattno == test_var->varoattno)
if (tlvar->vartype == type_var)
* Creates a copy of a target list by creating new resdom nodes
* without sort information.
*
* 'targetlist' is the target list to be copied.
* Returns the resulting target list.
new_unsorted_tlist(List *targetlist)
Bruce Momjian
committed
List *new_targetlist = (List *) copyObject((Node *) targetlist);
List *x = NIL;
foreach(x, new_targetlist)
{
Bruce Momjian
committed
TargetEntry *tle = (TargetEntry *) lfirst(x);
tle->resdom->reskey = 0;
tle->resdom->reskeyop = (Oid) 0;
}
* Replaces the var nodes in the first target list with those from
* the second target list. The two target lists are assumed to be
* identical except their actual resdoms and vars are different.
*
* 'target' is the target list to be replaced
* 'source' is the target list to be copied
* Returns a new target list.
copy_vars(List *target, List *source)
Bruce Momjian
committed
List *result = NIL;
List *src = NIL;
List *dest = NIL;
for (src = source, dest = target; src != NIL &&
dest != NIL; src = lnext(src), dest = lnext(dest))
{
TargetEntry *temp = makeTargetEntry(((TargetEntry *) lfirst(dest))->resdom,
(Node *) get_expr(lfirst(src)));
result = lappend(result, temp);
}
* Create a target list that only contains unique variables.
*
*
* 'tlist' is the current target list
* Returns the "flattened" new target list.
flatten_tlist(List *tlist)
Bruce Momjian
committed
int last_resdomno = 1;
List *new_tlist = NIL;
List *tlist_vars = NIL;
List *temp;
foreach(temp, tlist)
{
Bruce Momjian
committed
TargetEntry *temp_entry = NULL;
List *vars;
temp_entry = lfirst(temp);
vars = pull_var_clause((Node *) get_expr(temp_entry));
if (vars != NULL)
tlist_vars = nconc(tlist_vars, vars);
foreach(temp, tlist_vars)
{
Bruce Momjian
committed
Var *var = lfirst(temp);
if (!(tlist_member(var, new_tlist)))
{
Bruce Momjian
committed
Resdom *r;
r = makeResdom(last_resdomno,
var->vartype,
var->vartypmod,
NULL,
(Index) 0,
(Oid) 0,
0);
last_resdomno++;
new_tlist = lappend(new_tlist, makeTargetEntry(r, (Node *) var));
return new_tlist;
* Redoes the target list of a query with no nested attributes by
* replacing vars within computational expressions with vars from
* the 'flattened' target list of the query.
*
* 'full-tlist' is the actual target list
* 'flat-tlist' is the flattened (var-only) target list
* Returns the modified actual target list.
flatten_tlist_vars(List *full_tlist, List *flat_tlist)
Bruce Momjian
committed
List *x = NIL;
List *result = NIL;
foreach(x, full_tlist)
{
Bruce Momjian
committed
TargetEntry *tle = lfirst(x);
result = lappend(result, makeTargetEntry(tle->resdom,
flatten_tlistentry((Node *) get_expr(tle),
flat_tlist)));
* Replaces vars within a target list entry with vars from a flattened
* target list.
*
* 'tlistentry' is the target list entry to be modified
* 'flat-tlist' is the flattened target list
* Returns the (modified) target_list entry from the target list.
Bruce Momjian
committed
static Node *
flatten_tlistentry(Node *tlistentry, List *flat_tlist)
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
if (tlistentry == NULL)
{
return NULL;
}
else if (IsA(tlistentry, Var))
{
return
((Node *) get_expr(match_varid((Var *) tlistentry,
flat_tlist)));
}
else if (IsA(tlistentry, Iter))
{
((Iter *) tlistentry)->iterexpr =
flatten_tlistentry((Node *) ((Iter *) tlistentry)->iterexpr,
flat_tlist);
return tlistentry;
}
else if (single_node(tlistentry))
{
return tlistentry;
}
else if (is_funcclause(tlistentry))
{
Bruce Momjian
committed
Expr *expr = (Expr *) tlistentry;
List *temp_result = NIL;
List *elt = NIL;
foreach(elt, expr->args)
temp_result = lappend(temp_result,
flatten_tlistentry(lfirst(elt), flat_tlist));
return
((Node *) make_funcclause((Func *) expr->oper, temp_result));
}
else if (IsA(tlistentry, Aggreg))
{
return tlistentry;
}
else if (IsA(tlistentry, ArrayRef))
{
Bruce Momjian
committed
ArrayRef *aref = (ArrayRef *) tlistentry;
List *temp = NIL;
List *elt = NIL;
foreach(elt, aref->refupperindexpr)
temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
aref->refupperindexpr = temp;
temp = NIL;
foreach(elt, aref->reflowerindexpr)
temp = lappend(temp, flatten_tlistentry(lfirst(elt), flat_tlist));
aref->reflowerindexpr = temp;
aref->refexpr =
flatten_tlistentry(aref->refexpr, flat_tlist);
aref->refassgnexpr =
flatten_tlistentry(aref->refassgnexpr, flat_tlist);
return tlistentry;
}
else
{
Bruce Momjian
committed
Expr *expr = (Expr *) tlistentry;
Var *left =
(Var *) flatten_tlistentry((Node *) get_leftop(expr),
flat_tlist);
Bruce Momjian
committed
Var *right =
(Var *) flatten_tlistentry((Node *) get_rightop(expr),
flat_tlist);
return ((Node *)
make_opclause((Oper *) expr->oper, left, right));
}
get_expr(TargetEntry *tle)
Assert(tle != NULL);
Assert(tle->expr != NULL);
}
/*****************************************************************************
*
*****************************************************************************/
/*
* AddGroupAttrToTlist -
* append the group attribute to the target list if it's not already
* in there.
AddGroupAttrToTlist(List *tlist, List *grpCl)
Bruce Momjian
committed
List *gl;
int last_resdomno = length(tlist) + 1;
foreach(gl, grpCl)
{
Bruce Momjian
committed
GroupClause *gc = (GroupClause *) lfirst(gl);
Var *var = gc->grpAttr;
if (!(tlist_member(var, tlist)))
{
Bruce Momjian
committed
Resdom *r;
r = makeResdom(last_resdomno,
var->vartype,
var->vartypmod,
NULL,
(Index) 0,
(Oid) 0,
0);
last_resdomno++;
tlist = lappend(tlist, makeTargetEntry(r, (Node *) var));