blob: b1f887369d7338166430fd8a39b3cf2ad09b5ff0 [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* nodeFuncs.c
* All node routines more complicated than simple access/modification
*
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.27 2006/03/05 15:58:27 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/nodeFuncs.h"
static int leftmostLoc(int loc1, int loc2);
static bool var_is_inner(Var *var);
/*
* exprLocation -
* returns the parse location of an expression tree, for error reports
*
* -1 is returned if the location can't be determined.
*
* For expressions larger than a single token, the intent here is to
* return the location of the expression's leftmost token, not necessarily
* the topmost Node's location field. For example, an OpExpr's location
* field will point at the operator name, but if it is not a prefix operator
* then we should return the location of the left-hand operand instead.
* The reason is that we want to reference the entire expression not just
* that operator, and pointing to its start seems to be the most natural way.
*
* The location is not perfect --- for example, since the grammar doesn't
* explicitly represent parentheses in the parsetree, given something that
* had been written "(a + b) * c" we are going to point at "a" not "(".
* But it should be plenty good enough for error reporting purposes.
*
* You might think that this code is overly general, for instance why check
* the operands of a FuncExpr node, when the function name can be expected
* to be to the left of them? There are a couple of reasons. The grammar
* sometimes builds expressions that aren't quite what the user wrote;
* for instance x IS NOT BETWEEN ... becomes a NOT-expression whose keyword
* pointer is to the right of its leftmost argument. Also, nodes that were
* inserted implicitly by parse analysis (such as FuncExprs for implicit
* coercions) will have location -1, and so we can have odd combinations of
* known and unknown locations in a tree.
*/
int
exprLocation(Node *expr)
{
int loc;
if (expr == NULL)
return -1;
switch (nodeTag(expr))
{
case T_RangeVar:
loc = ((RangeVar *) expr)->location;
break;
case T_Var:
loc = ((Var *) expr)->location;
break;
case T_Const:
loc = ((Const *) expr)->location;
break;
case T_Param:
loc = ((Param *) expr)->location;
break;
case T_Aggref:
/* function name should always be the first thing */
loc = ((Aggref *) expr)->location;
break;
case T_WindowRef:
/* function name should always be the first thing */
loc = ((WindowRef *) expr)->location;
break;
case T_ArrayRef:
/* just use array argument's location */
loc = exprLocation((Node *) ((ArrayRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
FuncExpr *fexpr = (FuncExpr *) expr;
/* consider both function name and leftmost arg */
loc = leftmostLoc(fexpr->location,
exprLocation((Node *) fexpr->args));
}
break;
case T_OpExpr:
case T_DistinctExpr: /* struct-equivalent to OpExpr */
case T_NullIfExpr: /* struct-equivalent to OpExpr */
{
OpExpr *opexpr = (OpExpr *) expr;
/* consider both operator name and leftmost arg */
loc = leftmostLoc(opexpr->location,
exprLocation((Node *) opexpr->args));
}
break;
case T_ScalarArrayOpExpr:
{
ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) expr;
/* consider both operator name and leftmost arg */
loc = leftmostLoc(saopexpr->location,
exprLocation((Node *) saopexpr->args));
}
break;
case T_BoolExpr:
{
BoolExpr *bexpr = (BoolExpr *) expr;
/*
* Same as above, to handle either NOT or AND/OR. We can't
* special-case NOT because of the way that it's used for
* things like IS NOT BETWEEN.
*/
loc = leftmostLoc(bexpr->location,
exprLocation((Node *) bexpr->args));
}
break;
case T_SubLink:
{
SubLink *sublink = (SubLink *) expr;
/* check the testexpr, if any, and the operator/keyword */
loc = leftmostLoc(exprLocation(sublink->testexpr),
sublink->location);
}
break;
case T_FieldSelect:
/* just use argument's location */
loc = exprLocation((Node *) ((FieldSelect *) expr)->arg);
break;
case T_FieldStore:
/* just use argument's location */
loc = exprLocation((Node *) ((FieldStore *) expr)->arg);
break;
case T_RelabelType:
{
RelabelType *rexpr = (RelabelType *) expr;
/* Much as above */
loc = leftmostLoc(rexpr->location,
exprLocation((Node *) rexpr->arg));
}
break;
case T_ConvertRowtypeExpr:
{
ConvertRowtypeExpr *cexpr = (ConvertRowtypeExpr *) expr;
/* Much as above */
loc = leftmostLoc(cexpr->location,
exprLocation((Node *) cexpr->arg));
}
break;
case T_CaseExpr:
/* CASE keyword should always be the first thing */
loc = ((CaseExpr *) expr)->location;
break;
case T_CaseWhen:
/* WHEN keyword should always be the first thing */
loc = ((CaseWhen *) expr)->location;
break;
case T_ArrayExpr:
/* the location points at ARRAY or [, which must be leftmost */
loc = ((ArrayExpr *) expr)->location;
break;
case T_RowExpr:
/* the location points at ROW or (, which must be leftmost */
loc = ((RowExpr *) expr)->location;
break;
case T_RowCompareExpr:
/* just use leftmost argument's location */
loc = exprLocation((Node *) ((RowCompareExpr *) expr)->largs);
break;
case T_CoalesceExpr:
/* COALESCE keyword should always be the first thing */
loc = ((CoalesceExpr *) expr)->location;
break;
case T_MinMaxExpr:
/* GREATEST/LEAST keyword should always be the first thing */
loc = ((MinMaxExpr *) expr)->location;
break;
case T_NullTest:
/* just use argument's location */
loc = exprLocation((Node *) ((NullTest *) expr)->arg);
break;
case T_BooleanTest:
/* just use argument's location */
loc = exprLocation((Node *) ((BooleanTest *) expr)->arg);
break;
case T_CoerceToDomain:
{
CoerceToDomain *cexpr = (CoerceToDomain *) expr;
/* Much as above */
loc = leftmostLoc(cexpr->location,
exprLocation((Node *) cexpr->arg));
}
break;
case T_CoerceToDomainValue:
loc = ((CoerceToDomainValue *) expr)->location;
break;
case T_SetToDefault:
loc = ((SetToDefault *) expr)->location;
break;
case T_TargetEntry:
/* just use argument's location */
loc = exprLocation((Node *) ((TargetEntry *) expr)->expr);
break;
case T_IntoClause:
/* use the contained RangeVar's location --- close enough */
loc = exprLocation((Node *) ((IntoClause *) expr)->rel);
break;
case T_List:
{
/* report location of first list member that has a location */
ListCell *lc;
loc = -1; /* just to suppress compiler warning */
foreach(lc, (List *) expr)
{
loc = exprLocation((Node *) lfirst(lc));
if (loc >= 0)
break;
}
}
break;
case T_A_Expr:
{
A_Expr *aexpr = (A_Expr *) expr;
/* use leftmost of operator or left operand (if any) */
/* we assume right operand can't be to left of operator */
loc = leftmostLoc(aexpr->location,
exprLocation(aexpr->lexpr));
}
break;
case T_ColumnRef:
loc = ((ColumnRef *) expr)->location;
break;
case T_ParamRef:
loc = ((ParamRef *) expr)->location;
break;
case T_A_Const:
loc = ((A_Const *) expr)->location;
break;
case T_FuncCall:
{
FuncCall *fc = (FuncCall *) expr;
/* consider both function name and leftmost arg */
loc = leftmostLoc(fc->location,
exprLocation((Node *) fc->args));
}
break;
case T_ResTarget:
/* we need not examine the contained expression (if any) */
loc = ((ResTarget *) expr)->location;
break;
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
/*
* This could represent CAST(), ::, or TypeName 'literal',
* so any of the components might be leftmost.
*/
loc = exprLocation(tc->arg);
loc = leftmostLoc(loc, tc->typname->location);
loc = leftmostLoc(loc, tc->location);
}
break;
case T_SortBy:
/* just use argument's location (ignore operator, if any) */
loc = exprLocation(((SortBy *) expr)->node);
break;
case T_TypeName:
loc = ((TypeName *) expr)->location;
break;
case T_WithClause:
loc = ((WithClause *) expr)->location;
break;
case T_CommonTableExpr:
loc = ((CommonTableExpr *) expr)->location;
break;
default:
/* for any other node type it's just unknown... */
loc = -1;
break;
}
return loc;
}
/*
* leftmostLoc - support for exprLocation
*
* Take the minimum of two parse location values, but ignore unknowns
*/
static int
leftmostLoc(int loc1, int loc2)
{
if (loc1 < 0)
return loc2;
else if (loc2 < 0)
return loc1;
else
return Min(loc1, loc2);
}
/*
* single_node -
* Returns t if node corresponds to a single-noded expression
*/
bool
single_node(Node *node)
{
if (IsA(node, Const) ||
IsA(node, Var) ||
IsA(node, Param))
return true;
else
return false;
}
/*****************************************************************************
* VAR nodes
*****************************************************************************/
/*
* var_is_outer
* var_is_inner
* var_is_mat
* var_is_rel
*
* Returns t iff the var node corresponds to (respectively):
* the outer relation in a join
* the inner relation of a join
* a materialized relation
* a base relation (i.e., not an attribute reference, a variable from
* some lower join level, or a sort result)
* var node is an array reference
*
*/
bool
var_is_outer(Var *var)
{
return (bool) (var->varno == OUTER);
}
static bool
var_is_inner(Var *var)
{
return (bool) (var->varno == INNER);
}
bool
var_is_rel(Var *var)
{
return (bool)
!(var_is_inner(var) || var_is_outer(var));
}