/*-------------------------------------------------------------------------
 *
 * tlist.c
 *	  Target list manipulation routines
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /home/projects/pgsql/cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.44 2000/04/12 17:15:24 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"

/*****************************************************************************
 *	---------- RELATION node target list routines ----------
 *****************************************************************************/

/*
 * tlistentry_member
 *	  Finds the (first) member of the given tlist whose expression is
 *	  equal() to the given expression.	Result is NULL if no such member.
 */
TargetEntry *
tlistentry_member(Node *node, List *targetlist)
{
	List	   *temp;

	foreach(temp, targetlist)
	{
		TargetEntry *tlentry = (TargetEntry *) lfirst(temp);

		if (equal(node, tlentry->expr))
			return tlentry;
	}
	return NULL;
}

/*
 * matching_tlist_expr
 *	  Same as tlistentry_member(), except returns the tlist expression
 *	  rather than its parent TargetEntry node.
 */
Node *
matching_tlist_expr(Node *node, List *targetlist)
{
	TargetEntry *tlentry;

	tlentry = tlistentry_member(node, targetlist);
	if (tlentry)
		return tlentry->expr;

	return (Node *) NULL;
}

/*
 * tlist_member
 *	  Same as tlistentry_member(), except returns the Resdom node
 *	  rather than its parent TargetEntry node.
 */
Resdom *
tlist_member(Node *node, List *targetlist)
{
	TargetEntry *tlentry;

	tlentry = tlistentry_member(node, targetlist);
	if (tlentry)
		return tlentry->resdom;

	return (Resdom *) NULL;
}

/*
 * add_var_to_tlist
 *	  Creates a targetlist entry corresponding to the supplied var node
 *	  'var' and adds the new targetlist entry to the targetlist field of
 *	  'rel'.  No entry is created if 'var' is already in the tlist.
 */
void
add_var_to_tlist(RelOptInfo *rel, Var *var)
{
	if (!tlistentry_member((Node *) var, rel->targetlist))
	{
		/* XXX is copyObject necessary here? */
		rel->targetlist = lappend(rel->targetlist,
							   create_tl_element((Var *) copyObject(var),
										   length(rel->targetlist) + 1));
	}
}

/*
 * create_tl_element
 *	  Creates a target list entry node and its associated (resdom var) pair
 *	  with its resdom number equal to 'resdomno'.
 */
TargetEntry *
create_tl_element(Var *var, int resdomno)
{
	return makeTargetEntry(makeResdom(resdomno,
									  var->vartype,
									  var->vartypmod,
									  NULL,
									  (Index) 0,
									  (Oid) 0,
									  false),
						   (Node *) var);
}

/*****************************************************************************
 *		---------- GENERAL target list routines ----------
 *****************************************************************************/

/*
 * 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;

	foreach(x, new_targetlist)
	{
		TargetEntry *tle = (TargetEntry *) lfirst(x);

		tle->resdom->reskey = 0;
		tle->resdom->reskeyop = (Oid) 0;
	}
	return new_targetlist;
}

/*
 * flatten_tlist
 *	  Create a target list that only contains unique variables.
 *
 * Note that Vars with varlevelsup > 0 are not included in the output
 * tlist.  We expect that those will eventually be replaced with Params,
 * but that probably has not happened at the time this routine is called.
 *
 * 'tlist' is the current target list
 *
 * Returns the "flattened" new target list.
 *
 * The result is entirely new structure sharing no nodes with the original.
 * Copying the Var nodes is probably overkill, but be safe for now.
 */
List *
flatten_tlist(List *tlist)
{
	List	   *vlist = pull_var_clause((Node *) tlist, false);
	List	   *new_tlist;

	new_tlist = add_to_flat_tlist(NIL, vlist);
	freeList(vlist);
	return new_tlist;
}

/*
 * add_to_flat_tlist
 *		Add more vars to a flattened tlist (if they're not already in it)
 *
 * 'tlist' is the flattened tlist
 * 'vars' is a list of var nodes
 *
 * Returns the extended tlist.
 */
List *
add_to_flat_tlist(List *tlist, List *vars)
{
	int			next_resdomno = length(tlist) + 1;
	List	   *v;

	foreach(v, vars)
	{
		Var		   *var = lfirst(v);

		if (!tlistentry_member((Node *) var, tlist))
		{
			Resdom	   *r;

			r = makeResdom(next_resdomno++,
						   var->vartype,
						   var->vartypmod,
						   NULL,
						   (Index) 0,
						   (Oid) 0,
						   false);
			tlist = lappend(tlist,
							makeTargetEntry(r, copyObject(var)));
		}
	}
	return tlist;
}

Var *
get_expr(TargetEntry *tle)
{
	Assert(tle != NULL);
	Assert(tle->expr != NULL);

	return (Var *) tle->expr;
}

/*
 * get_sortgroupclause_tle
 *		Find the targetlist entry matching the given SortClause
 *		(or GroupClause) by ressortgroupref, and return it.
 *
 * Because GroupClause is typedef'd as SortClause, either kind of
 * node can be passed without casting.
 */
TargetEntry *
get_sortgroupclause_tle(SortClause *sortClause,
						List *targetList)
{
	Index		refnumber = sortClause->tleSortGroupRef;
	List	   *l;

	foreach(l, targetList)
	{
		TargetEntry *tle = (TargetEntry *) lfirst(l);

		if (tle->resdom->ressortgroupref == refnumber)
			return tle;
	}

	elog(ERROR, "get_sortgroupclause_tle: ORDER/GROUP BY expression not found in targetlist");
	return NULL;				/* keep compiler quiet */
}

/*
 * get_sortgroupclause_expr
 *		Find the targetlist entry matching the given SortClause
 *		(or GroupClause) by ressortgroupref, and return its expression.
 *
 * Because GroupClause is typedef'd as SortClause, either kind of
 * node can be passed without casting.
 */
Node *
get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
{
	TargetEntry *tle = get_sortgroupclause_tle(sortClause, targetList);

	return tle->expr;
}
