aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/util/tlist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/util/tlist.c')
-rw-r--r--src/backend/optimizer/util/tlist.c577
1 files changed, 577 insertions, 0 deletions
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
new file mode 100644
index 00000000000..073c2a08231
--- /dev/null
+++ b/src/backend/optimizer/util/tlist.c
@@ -0,0 +1,577 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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.1.1.1 1996/07/09 06:21:39 scrappy 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"
+#include "parser/catalog_utils.h"
+
+static Node *flatten_tlistentry(Node *tlistentry, List *flat_tlist);
+
+/*****************************************************************************
+ * ---------- RELATION node target list routines ----------
+ *****************************************************************************/
+
+/*
+ * tlistentry-member--
+ *
+ * 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
+ */
+TargetEntry *
+tlistentry_member(Var *var, List *targetlist)
+{
+ if (var) {
+ List *temp = NIL;
+
+ foreach (temp,targetlist) {
+ if (var_equal(var,
+ get_expr(lfirst(temp))))
+ return((TargetEntry*)lfirst(temp));
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * matching_tlvar--
+ *
+ * RETURNS: var node in a target list which is var_equal to 'var',
+ * if one exists.
+ * REQUIRES: "test" operates on lispval unions,
+ *
+ */
+Expr *
+matching_tlvar(Var *var, List *targetlist)
+{
+ TargetEntry *tlentry;
+
+ tlentry = tlistentry_member(var,targetlist);
+ if (tlentry)
+ return((Expr*)get_expr (tlentry) );
+
+ return((Expr*) NULL);
+}
+
+/*
+ * add_tl_element--
+ * Creates a targetlist entry corresponding to the supplied var node
+ *
+ * 'var' and adds the new targetlist entry to the targetlist field of
+ * 'rel'
+ *
+ * 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(Rel *rel, Var *var)
+{
+ 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) {
+ List *tlist = rel->targetlist;
+ Var *newvar = makeVar(var->varno,
+ var->varattno,
+ var->vartype,
+ var->varno,
+ var->varoattno);
+
+ rel->targetlist =
+ lappend (tlist,
+ create_tl_element(newvar,
+ length(tlist) + 1));
+
+ }
+}
+
+/*
+ * create_tl_element--
+ * 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).
+ */
+TargetEntry*
+create_tl_element(Var *var, int resdomno)
+{
+ TargetEntry *tlelement= makeNode(TargetEntry);
+
+ tlelement->resdom =
+ makeResdom(resdomno,
+ var->vartype,
+ get_typlen(var->vartype),
+ NULL,
+ (Index)0,
+ (Oid)0,
+ 0);
+ tlelement->expr = (Node*)var;
+
+ return(tlelement);
+}
+
+/*
+ * get-actual-tlist--
+ * Returns the targetlist elements from a relation tlist.
+ *
+ */
+List *
+get_actual_tlist(List *tlist)
+{
+ /*
+ * this function is not making sense. - ay 10/94
+ */
+#if 0
+ List *element = NIL;
+ List *result = NIL;
+
+ if (tlist==NULL) {
+ elog(DEBUG,"calling get_actual_tlist with empty tlist");
+ return(NIL);
+ }
+ /* 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)));
+
+ return(result);
+#endif
+ return tlist;
+}
+
+/*****************************************************************************
+ * ---------- GENERAL target list routines ----------
+ *****************************************************************************/
+
+/*
+ * tlist-member--
+ * 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)
+{
+ 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)
+ return(tl_elt->resdom);
+ else
+ return((Resdom*)NULL);
+ }
+ return ((Resdom*)NULL);
+}
+
+/*
+ * Routine to get the resdom out of a targetlist.
+ */
+Resdom *
+tlist_resdom(List *tlist, Resdom *resnode)
+{
+ 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)
+ return(resdom);
+ }
+ return((Resdom*)NULL);
+}
+
+
+/*
+ * match_varid--
+ * 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]
+ */
+TargetEntry *
+match_varid(Var *test_var, List *tlist)
+{
+ List *tl;
+ Oid type_var;
+
+ type_var = (Oid) test_var->vartype;
+
+ foreach (tl, tlist) {
+ TargetEntry *entry;
+ Var *tlvar;
+
+ entry = lfirst(tl);
+ tlvar = get_expr(entry);
+
+ /*
+ * we test the original varno (instead of varno which might
+ * be changed to INNER/OUTER.
+ */
+ if (tlvar->varnoold == test_var->varnoold &&
+ tlvar->varoattno == test_var->varoattno) {
+
+ if (tlvar->vartype == type_var)
+ return(entry);
+ }
+ }
+
+ return (NULL);
+}
+
+
+/*
+ * new-unsorted-tlist--
+ * 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.
+ *
+ */
+List *
+new_unsorted_tlist(List *targetlist)
+{
+ List *new_targetlist = (List*)copyObject ((Node*)targetlist);
+ List *x = NIL;
+
+ foreach (x, new_targetlist) {
+ TargetEntry *tle = (TargetEntry *)lfirst(x);
+ tle->resdom->reskey = 0;
+ tle->resdom->reskeyop = (Oid)0;
+ }
+ return(new_targetlist);
+}
+
+/*
+ * copy-vars--
+ * 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.
+ *
+ */
+List *
+copy_vars(List *target, List *source)
+{
+ 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 = MakeTLE(((TargetEntry *)lfirst(dest))->resdom,
+ (Node*)get_expr(lfirst(src)));
+ result = lappend(result,temp);
+ }
+ return(result);
+}
+
+/*
+ * flatten-tlist--
+ * Create a target list that only contains unique variables.
+ *
+ *
+ * 'tlist' is the current target list
+ *
+ * Returns the "flattened" new target list.
+ *
+ */
+List *
+flatten_tlist(List *tlist)
+{
+ int last_resdomno = 1;
+ List *new_tlist = NIL;
+ List *tlist_vars = NIL;
+ List *temp;
+
+ foreach (temp, tlist) {
+ 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) {
+ Var *var = lfirst(temp);
+ if (!(tlist_member(var, new_tlist))) {
+ Resdom *r;
+
+ r = makeResdom(last_resdomno,
+ var->vartype,
+ get_typlen(var->vartype),
+ NULL,
+ (Index)0,
+ (Oid)0,
+ 0);
+ last_resdomno++;
+ new_tlist = lappend(new_tlist, MakeTLE (r, (Node*)var));
+ }
+ }
+
+ return new_tlist;
+}
+
+/*
+ * flatten-tlist-vars--
+ * 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.
+ *
+ */
+List *
+flatten_tlist_vars(List *full_tlist, List *flat_tlist)
+{
+ List *x = NIL;
+ List *result = NIL;
+
+ foreach(x,full_tlist) {
+ TargetEntry *tle= lfirst(x);
+ result =
+ lappend(result,
+ MakeTLE(tle->resdom,
+ flatten_tlistentry((Node*)get_expr(tle),
+ flat_tlist)));
+ }
+
+ return(result);
+}
+
+/*
+ * flatten-tlistentry--
+ * 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.
+ *
+ */
+static Node *
+flatten_tlistentry(Node *tlistentry, List *flat_tlist)
+{
+ 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)) {
+ 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)) {
+ 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 {
+ Expr *expr = (Expr*)tlistentry;
+ Var *left =
+ (Var*)flatten_tlistentry((Node*)get_leftop(expr),
+ flat_tlist);
+ Var *right =
+ (Var*)flatten_tlistentry((Node*)get_rightop(expr),
+ flat_tlist);
+
+ return((Node *)
+ make_opclause((Oper*)expr->oper, left, right));
+ }
+}
+
+
+TargetEntry *
+MakeTLE(Resdom *resdom, Node *expr)
+{
+ TargetEntry *rt = makeNode(TargetEntry);
+
+ rt->resdom = resdom;
+ rt->expr = expr;
+ return rt;
+}
+
+Var *
+get_expr(TargetEntry *tle)
+{
+ Assert(tle!=NULL);
+ Assert(tle->expr!=NULL);
+
+ return ((Var *)tle->expr);
+}
+
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+
+/*
+ * AddGroupAttrToTlist -
+ * append the group attribute to the target list if it's not already
+ * in there.
+ */
+void
+AddGroupAttrToTlist(List *tlist, List *grpCl)
+{
+ List *gl;
+ int last_resdomno = length(tlist) + 1;
+
+ foreach (gl, grpCl) {
+ GroupClause *gc = (GroupClause*)lfirst(gl);
+ Var *var = gc->grpAttr;
+
+ if (!(tlist_member(var, tlist))) {
+ Resdom *r;
+
+ r = makeResdom(last_resdomno,
+ var->vartype,
+ get_typlen(var->vartype),
+ NULL,
+ (Index)0,
+ (Oid)0,
+ 0);
+ last_resdomno++;
+ tlist = lappend(tlist, MakeTLE(r, (Node*)var));
+ }
+ }
+}
+
+/* was ExecTargetListLength() in execQual.c,
+ moved here to reduce dependencies on the executor module */
+int
+exec_tlist_length(List *targetlist)
+{
+ int len;
+ List *tl;
+ TargetEntry *curTle;
+
+ len = 0;
+ foreach (tl, targetlist) {
+ curTle = lfirst(tl);
+
+ if (curTle->resdom != NULL)
+ len++;
+ }
+ return len;
+}
+
+