/*-------------------------------------------------------------------------
 *
 * var.c
 *	  Var node manipulation routines
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.19 1999/05/25 16:10:05 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <sys/types.h>

#include "postgres.h"

#include <nodes/relation.h>

#include "nodes/primnodes.h"
#include "nodes/plannodes.h"
#include "nodes/nodeFuncs.h"

#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/var.h"

#include "parser/parsetree.h"

/*
 *		find_varnos
 *
 *		Descends down part of a parsetree (qual or tlist),
 *
 *		XXX assumes varno's are always integers, which shouldn't be true...
 *		(though it currently is, see primnodes.h)
 */
List *
pull_varnos(Node *me)
{
	List	   *i,
			   *result = NIL;

	if (me == NULL)
		return NIL;

	switch (nodeTag(me))
	{
		case T_List:
			foreach(i, (List *) me)
				result = nconc(result, pull_varnos(lfirst(i)));
			break;
		case T_ArrayRef:
			foreach(i, ((ArrayRef *) me)->refupperindexpr)
				result = nconc(result, pull_varnos(lfirst(i)));
			foreach(i, ((ArrayRef *) me)->reflowerindexpr)
				result = nconc(result, pull_varnos(lfirst(i)));
			result = nconc(result, pull_varnos(((ArrayRef *) me)->refassgnexpr));
			break;
		case T_Var:
			result = lconsi(((Var *) me)->varno, NIL);
			break;
		default:
			break;
	}
	return result;
}

/*
 * contain_var_clause
 *	  Recursively scan a clause to discover whether it contains any Var nodes.
 *
 *	  Returns true if any varnode found.
 */
bool
contain_var_clause(Node *clause)
{
	List	   *temp;

	if (clause == NULL)
		return FALSE;
	else if (IsA(clause, Var))
		return TRUE;
	else if (single_node(clause))
		return FALSE;
	else if (IsA(clause, Iter))
		return contain_var_clause(((Iter *) clause)->iterexpr);
	else if (is_subplan(clause))
	{
		foreach(temp, ((Expr *) clause)->args)
		{
			if (contain_var_clause(lfirst(temp)))
				return TRUE;
		}
		/* Also check left sides of Oper-s */
		foreach(temp, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper)
		{
			if (contain_var_clause(lfirst(((Expr *) lfirst(temp))->args)))
				return TRUE;
		}
		return FALSE;
	}
	else if (IsA(clause, Expr))
	{

		/*
		 * Recursively scan the arguments of an expression. NOTE: this
		 * must come after is_subplan() case since subplan is a kind of
		 * Expr node.
		 */
		foreach(temp, ((Expr *) clause)->args)
		{
			if (contain_var_clause(lfirst(temp)))
				return TRUE;
		}
		return FALSE;
	}
	else if (IsA(clause, Aggref))
		return contain_var_clause(((Aggref *) clause)->target);
	else if (IsA(clause, ArrayRef))
	{
		foreach(temp, ((ArrayRef *) clause)->refupperindexpr)
		{
			if (contain_var_clause(lfirst(temp)))
				return TRUE;
		}
		foreach(temp, ((ArrayRef *) clause)->reflowerindexpr)
		{
			if (contain_var_clause(lfirst(temp)))
				return TRUE;
		}
		if (contain_var_clause(((ArrayRef *) clause)->refexpr))
			return TRUE;
		if (contain_var_clause(((ArrayRef *) clause)->refassgnexpr))
			return TRUE;
		return FALSE;
	}
	else if (case_clause(clause))
	{
		foreach(temp, ((CaseExpr *) clause)->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(temp);

			if (contain_var_clause(when->expr))
				return TRUE;
			if (contain_var_clause(when->result))
				return TRUE;
		}
		return (contain_var_clause(((CaseExpr *) clause)->defresult));
	}
	else
	{
		elog(ERROR, "contain_var_clause: Cannot handle node type %d",
			 nodeTag(clause));
	}

	return FALSE;
}

/*
 * pull_var_clause
 *	  Recursively pulls all var nodes from a clause by pulling vars from the
 *	  left and right operands of the clause.
 *
 *	  Returns list of varnodes found.  Note the varnodes themselves are not
 *	  copied, only referenced.
 */
List *
pull_var_clause(Node *clause)
{
	List	   *retval = NIL;
	List	   *temp;

	if (clause == NULL)
		return NIL;
	else if (IsA(clause, Var))
		retval = lcons(clause, NIL);
	else if (single_node(clause))
		retval = NIL;
	else if (IsA(clause, Iter))
		retval = pull_var_clause(((Iter *) clause)->iterexpr);
	else if (is_subplan(clause))
	{
		foreach(temp, ((Expr *) clause)->args)
			retval = nconc(retval, pull_var_clause(lfirst(temp)));
		/* Also get Var-s from left sides of Oper-s */
		foreach(temp, ((SubPlan *) ((Expr *) clause)->oper)->sublink->oper)
			retval = nconc(retval,
				 pull_var_clause(lfirst(((Expr *) lfirst(temp))->args)));
	}
	else if (IsA(clause, Expr))
	{

		/*
		 * Recursively scan the arguments of an expression. NOTE: this
		 * must come after is_subplan() case since subplan is a kind of
		 * Expr node.
		 */
		foreach(temp, ((Expr *) clause)->args)
			retval = nconc(retval, pull_var_clause(lfirst(temp)));
	}
	else if (IsA(clause, Aggref))
		retval = pull_var_clause(((Aggref *) clause)->target);
	else if (IsA(clause, ArrayRef))
	{
		foreach(temp, ((ArrayRef *) clause)->refupperindexpr)
			retval = nconc(retval, pull_var_clause(lfirst(temp)));
		foreach(temp, ((ArrayRef *) clause)->reflowerindexpr)
			retval = nconc(retval, pull_var_clause(lfirst(temp)));
		retval = nconc(retval,
					   pull_var_clause(((ArrayRef *) clause)->refexpr));
		retval = nconc(retval,
				   pull_var_clause(((ArrayRef *) clause)->refassgnexpr));
	}
	else if (case_clause(clause))
	{
		foreach(temp, ((CaseExpr *) clause)->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(temp);

			retval = nconc(retval, pull_var_clause(when->expr));
			retval = nconc(retval, pull_var_clause(when->result));
		}
		retval = nconc(retval, pull_var_clause(((CaseExpr *) clause)->defresult));
	}
	else
	{
		elog(ERROR, "pull_var_clause: Cannot handle node type %d",
			 nodeTag(clause));
	}

	return retval;
}

/*
 *		var_equal
 *
 *		The only difference between this an equal() is that this does not
 *		test varnoold and varoattno.
 *
 *		Returns t iff two var nodes correspond to the same attribute.
 */
bool
var_equal(Var *var1, Var *var2)
{
	if (IsA(var1, Var) &&IsA(var2, Var) &&
		(((Var *) var1)->varno == ((Var *) var2)->varno) &&
		(((Var *) var1)->vartype == ((Var *) var2)->vartype) &&
		(((Var *) var1)->vartypmod == ((Var *) var2)->vartypmod) &&
		(((Var *) var1)->varlevelsup == ((Var *) var2)->varlevelsup) &&
		(((Var *) var1)->varattno == ((Var *) var2)->varattno))
	{
		Assert(((Var *) var1)->varlevelsup == 0);
		return true;
	}
	else
		return false;
}
