
for consistency with the (relatively) recent addition of typmod to SubLink. An example of why it's a good idea is to be seen in the recent "failed to locate grouping columns" bug, which wouldn't have happened if a SubPlan exposed the same typmod info as the SubLink it was derived from. This could be back-patched, since it doesn't affect any on-disk data format, but for the moment it doesn't seem necessary to do so.
2501 lines
67 KiB
C
2501 lines
67 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* nodeFuncs.c
|
|
* Various general-purpose manipulations of Node trees
|
|
*
|
|
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.39 2009/03/10 22:09:25 tgl Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/pg_type.h"
|
|
#include "miscadmin.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "nodes/relation.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/lsyscache.h"
|
|
|
|
|
|
static bool expression_returns_set_walker(Node *node, void *context);
|
|
static int leftmostLoc(int loc1, int loc2);
|
|
|
|
|
|
/*
|
|
* exprType -
|
|
* returns the Oid of the type of the expression's result.
|
|
*/
|
|
Oid
|
|
exprType(Node *expr)
|
|
{
|
|
Oid type;
|
|
|
|
if (!expr)
|
|
return InvalidOid;
|
|
|
|
switch (nodeTag(expr))
|
|
{
|
|
case T_Var:
|
|
type = ((Var *) expr)->vartype;
|
|
break;
|
|
case T_Const:
|
|
type = ((Const *) expr)->consttype;
|
|
break;
|
|
case T_Param:
|
|
type = ((Param *) expr)->paramtype;
|
|
break;
|
|
case T_Aggref:
|
|
type = ((Aggref *) expr)->aggtype;
|
|
break;
|
|
case T_WindowFunc:
|
|
type = ((WindowFunc *) expr)->wintype;
|
|
break;
|
|
case T_ArrayRef:
|
|
{
|
|
ArrayRef *arrayref = (ArrayRef *) expr;
|
|
|
|
/* slice and/or store operations yield the array type */
|
|
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
|
|
type = arrayref->refarraytype;
|
|
else
|
|
type = arrayref->refelemtype;
|
|
}
|
|
break;
|
|
case T_FuncExpr:
|
|
type = ((FuncExpr *) expr)->funcresulttype;
|
|
break;
|
|
case T_OpExpr:
|
|
type = ((OpExpr *) expr)->opresulttype;
|
|
break;
|
|
case T_DistinctExpr:
|
|
type = ((DistinctExpr *) expr)->opresulttype;
|
|
break;
|
|
case T_ScalarArrayOpExpr:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_BoolExpr:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) expr;
|
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
/* get the type of the subselect's first target column */
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
TargetEntry *tent;
|
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
elog(ERROR, "cannot get type for untransformed sublink");
|
|
tent = (TargetEntry *) linitial(qtree->targetList);
|
|
Assert(IsA(tent, TargetEntry));
|
|
Assert(!tent->resjunk);
|
|
type = exprType((Node *) tent->expr);
|
|
if (sublink->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
type = get_array_type(type);
|
|
if (!OidIsValid(type))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("could not find array type for data type %s",
|
|
format_type_be(exprType((Node *) tent->expr)))));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* for all other sublink types, result is boolean */
|
|
type = BOOLOID;
|
|
}
|
|
}
|
|
break;
|
|
case T_SubPlan:
|
|
{
|
|
SubPlan *subplan = (SubPlan *) expr;
|
|
|
|
if (subplan->subLinkType == EXPR_SUBLINK ||
|
|
subplan->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
/* get the type of the subselect's first target column */
|
|
type = subplan->firstColType;
|
|
if (subplan->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
type = get_array_type(type);
|
|
if (!OidIsValid(type))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("could not find array type for data type %s",
|
|
format_type_be(subplan->firstColType))));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* for all other subplan types, result is boolean */
|
|
type = BOOLOID;
|
|
}
|
|
}
|
|
break;
|
|
case T_AlternativeSubPlan:
|
|
{
|
|
AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr;
|
|
|
|
/* subplans should all return the same thing */
|
|
type = exprType((Node *) linitial(asplan->subplans));
|
|
}
|
|
break;
|
|
case T_FieldSelect:
|
|
type = ((FieldSelect *) expr)->resulttype;
|
|
break;
|
|
case T_FieldStore:
|
|
type = ((FieldStore *) expr)->resulttype;
|
|
break;
|
|
case T_RelabelType:
|
|
type = ((RelabelType *) expr)->resulttype;
|
|
break;
|
|
case T_CoerceViaIO:
|
|
type = ((CoerceViaIO *) expr)->resulttype;
|
|
break;
|
|
case T_ArrayCoerceExpr:
|
|
type = ((ArrayCoerceExpr *) expr)->resulttype;
|
|
break;
|
|
case T_ConvertRowtypeExpr:
|
|
type = ((ConvertRowtypeExpr *) expr)->resulttype;
|
|
break;
|
|
case T_CaseExpr:
|
|
type = ((CaseExpr *) expr)->casetype;
|
|
break;
|
|
case T_CaseTestExpr:
|
|
type = ((CaseTestExpr *) expr)->typeId;
|
|
break;
|
|
case T_ArrayExpr:
|
|
type = ((ArrayExpr *) expr)->array_typeid;
|
|
break;
|
|
case T_RowExpr:
|
|
type = ((RowExpr *) expr)->row_typeid;
|
|
break;
|
|
case T_RowCompareExpr:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_CoalesceExpr:
|
|
type = ((CoalesceExpr *) expr)->coalescetype;
|
|
break;
|
|
case T_MinMaxExpr:
|
|
type = ((MinMaxExpr *) expr)->minmaxtype;
|
|
break;
|
|
case T_XmlExpr:
|
|
if (((XmlExpr *) expr)->op == IS_DOCUMENT)
|
|
type = BOOLOID;
|
|
else if (((XmlExpr *) expr)->op == IS_XMLSERIALIZE)
|
|
type = TEXTOID;
|
|
else
|
|
type = XMLOID;
|
|
break;
|
|
case T_NullIfExpr:
|
|
type = exprType((Node *) linitial(((NullIfExpr *) expr)->args));
|
|
break;
|
|
case T_NullTest:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_BooleanTest:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_CoerceToDomain:
|
|
type = ((CoerceToDomain *) expr)->resulttype;
|
|
break;
|
|
case T_CoerceToDomainValue:
|
|
type = ((CoerceToDomainValue *) expr)->typeId;
|
|
break;
|
|
case T_SetToDefault:
|
|
type = ((SetToDefault *) expr)->typeId;
|
|
break;
|
|
case T_CurrentOfExpr:
|
|
type = BOOLOID;
|
|
break;
|
|
case T_PlaceHolderVar:
|
|
type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr);
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
|
type = InvalidOid; /* keep compiler quiet */
|
|
break;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
/*
|
|
* exprTypmod -
|
|
* returns the type-specific modifier of the expression's result type,
|
|
* if it can be determined. In many cases, it can't and we return -1.
|
|
*/
|
|
int32
|
|
exprTypmod(Node *expr)
|
|
{
|
|
if (!expr)
|
|
return -1;
|
|
|
|
switch (nodeTag(expr))
|
|
{
|
|
case T_Var:
|
|
return ((Var *) expr)->vartypmod;
|
|
case T_Const:
|
|
return ((Const *) expr)->consttypmod;
|
|
case T_Param:
|
|
return ((Param *) expr)->paramtypmod;
|
|
case T_ArrayRef:
|
|
/* typmod is the same for array or element */
|
|
return ((ArrayRef *) expr)->reftypmod;
|
|
case T_FuncExpr:
|
|
{
|
|
int32 coercedTypmod;
|
|
|
|
/* Be smart about length-coercion functions... */
|
|
if (exprIsLengthCoercion(expr, &coercedTypmod))
|
|
return coercedTypmod;
|
|
}
|
|
break;
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) expr;
|
|
|
|
if (sublink->subLinkType == EXPR_SUBLINK ||
|
|
sublink->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
/* get the typmod of the subselect's first target column */
|
|
Query *qtree = (Query *) sublink->subselect;
|
|
TargetEntry *tent;
|
|
|
|
if (!qtree || !IsA(qtree, Query))
|
|
elog(ERROR, "cannot get type for untransformed sublink");
|
|
tent = (TargetEntry *) linitial(qtree->targetList);
|
|
Assert(IsA(tent, TargetEntry));
|
|
Assert(!tent->resjunk);
|
|
return exprTypmod((Node *) tent->expr);
|
|
/* note we don't need to care if it's an array */
|
|
}
|
|
}
|
|
break;
|
|
case T_SubPlan:
|
|
{
|
|
SubPlan *subplan = (SubPlan *) expr;
|
|
|
|
if (subplan->subLinkType == EXPR_SUBLINK ||
|
|
subplan->subLinkType == ARRAY_SUBLINK)
|
|
{
|
|
/* get the typmod of the subselect's first target column */
|
|
/* note we don't need to care if it's an array */
|
|
return subplan->firstColTypmod;
|
|
}
|
|
else
|
|
{
|
|
/* for all other subplan types, result is boolean */
|
|
return -1;
|
|
}
|
|
}
|
|
break;
|
|
case T_AlternativeSubPlan:
|
|
{
|
|
AlternativeSubPlan *asplan = (AlternativeSubPlan *) expr;
|
|
|
|
/* subplans should all return the same thing */
|
|
return exprTypmod((Node *) linitial(asplan->subplans));
|
|
}
|
|
break;
|
|
case T_FieldSelect:
|
|
return ((FieldSelect *) expr)->resulttypmod;
|
|
case T_RelabelType:
|
|
return ((RelabelType *) expr)->resulttypmod;
|
|
case T_ArrayCoerceExpr:
|
|
return ((ArrayCoerceExpr *) expr)->resulttypmod;
|
|
case T_CaseExpr:
|
|
{
|
|
/*
|
|
* If all the alternatives agree on type/typmod, return that
|
|
* typmod, else use -1
|
|
*/
|
|
CaseExpr *cexpr = (CaseExpr *) expr;
|
|
Oid casetype = cexpr->casetype;
|
|
int32 typmod;
|
|
ListCell *arg;
|
|
|
|
if (!cexpr->defresult)
|
|
return -1;
|
|
if (exprType((Node *) cexpr->defresult) != casetype)
|
|
return -1;
|
|
typmod = exprTypmod((Node *) cexpr->defresult);
|
|
if (typmod < 0)
|
|
return -1; /* no point in trying harder */
|
|
foreach(arg, cexpr->args)
|
|
{
|
|
CaseWhen *w = (CaseWhen *) lfirst(arg);
|
|
|
|
Assert(IsA(w, CaseWhen));
|
|
if (exprType((Node *) w->result) != casetype)
|
|
return -1;
|
|
if (exprTypmod((Node *) w->result) != typmod)
|
|
return -1;
|
|
}
|
|
return typmod;
|
|
}
|
|
break;
|
|
case T_CaseTestExpr:
|
|
return ((CaseTestExpr *) expr)->typeMod;
|
|
case T_ArrayExpr:
|
|
{
|
|
/*
|
|
* If all the elements agree on type/typmod, return that
|
|
* typmod, else use -1
|
|
*/
|
|
ArrayExpr *arrayexpr = (ArrayExpr *) expr;
|
|
Oid commontype;
|
|
int32 typmod;
|
|
ListCell *elem;
|
|
|
|
if (arrayexpr->elements == NIL)
|
|
return -1;
|
|
typmod = exprTypmod((Node *) linitial(arrayexpr->elements));
|
|
if (typmod < 0)
|
|
return -1; /* no point in trying harder */
|
|
if (arrayexpr->multidims)
|
|
commontype = arrayexpr->array_typeid;
|
|
else
|
|
commontype = arrayexpr->element_typeid;
|
|
foreach(elem, arrayexpr->elements)
|
|
{
|
|
Node *e = (Node *) lfirst(elem);
|
|
|
|
if (exprType(e) != commontype)
|
|
return -1;
|
|
if (exprTypmod(e) != typmod)
|
|
return -1;
|
|
}
|
|
return typmod;
|
|
}
|
|
break;
|
|
case T_CoalesceExpr:
|
|
{
|
|
/*
|
|
* If all the alternatives agree on type/typmod, return that
|
|
* typmod, else use -1
|
|
*/
|
|
CoalesceExpr *cexpr = (CoalesceExpr *) expr;
|
|
Oid coalescetype = cexpr->coalescetype;
|
|
int32 typmod;
|
|
ListCell *arg;
|
|
|
|
if (exprType((Node *) linitial(cexpr->args)) != coalescetype)
|
|
return -1;
|
|
typmod = exprTypmod((Node *) linitial(cexpr->args));
|
|
if (typmod < 0)
|
|
return -1; /* no point in trying harder */
|
|
for_each_cell(arg, lnext(list_head(cexpr->args)))
|
|
{
|
|
Node *e = (Node *) lfirst(arg);
|
|
|
|
if (exprType(e) != coalescetype)
|
|
return -1;
|
|
if (exprTypmod(e) != typmod)
|
|
return -1;
|
|
}
|
|
return typmod;
|
|
}
|
|
break;
|
|
case T_MinMaxExpr:
|
|
{
|
|
/*
|
|
* If all the alternatives agree on type/typmod, return that
|
|
* typmod, else use -1
|
|
*/
|
|
MinMaxExpr *mexpr = (MinMaxExpr *) expr;
|
|
Oid minmaxtype = mexpr->minmaxtype;
|
|
int32 typmod;
|
|
ListCell *arg;
|
|
|
|
if (exprType((Node *) linitial(mexpr->args)) != minmaxtype)
|
|
return -1;
|
|
typmod = exprTypmod((Node *) linitial(mexpr->args));
|
|
if (typmod < 0)
|
|
return -1; /* no point in trying harder */
|
|
for_each_cell(arg, lnext(list_head(mexpr->args)))
|
|
{
|
|
Node *e = (Node *) lfirst(arg);
|
|
|
|
if (exprType(e) != minmaxtype)
|
|
return -1;
|
|
if (exprTypmod(e) != typmod)
|
|
return -1;
|
|
}
|
|
return typmod;
|
|
}
|
|
break;
|
|
case T_NullIfExpr:
|
|
{
|
|
NullIfExpr *nexpr = (NullIfExpr *) expr;
|
|
|
|
return exprTypmod((Node *) linitial(nexpr->args));
|
|
}
|
|
break;
|
|
case T_CoerceToDomain:
|
|
return ((CoerceToDomain *) expr)->resulttypmod;
|
|
case T_CoerceToDomainValue:
|
|
return ((CoerceToDomainValue *) expr)->typeMod;
|
|
case T_SetToDefault:
|
|
return ((SetToDefault *) expr)->typeMod;
|
|
case T_PlaceHolderVar:
|
|
return exprTypmod((Node *) ((PlaceHolderVar *) expr)->phexpr);
|
|
default:
|
|
break;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* exprIsLengthCoercion
|
|
* Detect whether an expression tree is an application of a datatype's
|
|
* typmod-coercion function. Optionally extract the result's typmod.
|
|
*
|
|
* If coercedTypmod is not NULL, the typmod is stored there if the expression
|
|
* is a length-coercion function, else -1 is stored there.
|
|
*
|
|
* Note that a combined type-and-length coercion will be treated as a
|
|
* length coercion by this routine.
|
|
*/
|
|
bool
|
|
exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
|
|
{
|
|
if (coercedTypmod != NULL)
|
|
*coercedTypmod = -1; /* default result on failure */
|
|
|
|
/*
|
|
* Scalar-type length coercions are FuncExprs, array-type length coercions
|
|
* are ArrayCoerceExprs
|
|
*/
|
|
if (expr && IsA(expr, FuncExpr))
|
|
{
|
|
FuncExpr *func = (FuncExpr *) expr;
|
|
int nargs;
|
|
Const *second_arg;
|
|
|
|
/*
|
|
* If it didn't come from a coercion context, reject.
|
|
*/
|
|
if (func->funcformat != COERCE_EXPLICIT_CAST &&
|
|
func->funcformat != COERCE_IMPLICIT_CAST)
|
|
return false;
|
|
|
|
/*
|
|
* If it's not a two-argument or three-argument function with the
|
|
* second argument being an int4 constant, it can't have been created
|
|
* from a length coercion (it must be a type coercion, instead).
|
|
*/
|
|
nargs = list_length(func->args);
|
|
if (nargs < 2 || nargs > 3)
|
|
return false;
|
|
|
|
second_arg = (Const *) lsecond(func->args);
|
|
if (!IsA(second_arg, Const) ||
|
|
second_arg->consttype != INT4OID ||
|
|
second_arg->constisnull)
|
|
return false;
|
|
|
|
/*
|
|
* OK, it is indeed a length-coercion function.
|
|
*/
|
|
if (coercedTypmod != NULL)
|
|
*coercedTypmod = DatumGetInt32(second_arg->constvalue);
|
|
|
|
return true;
|
|
}
|
|
|
|
if (expr && IsA(expr, ArrayCoerceExpr))
|
|
{
|
|
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
|
|
|
|
/* It's not a length coercion unless there's a nondefault typmod */
|
|
if (acoerce->resulttypmod < 0)
|
|
return false;
|
|
|
|
/*
|
|
* OK, it is indeed a length-coercion expression.
|
|
*/
|
|
if (coercedTypmod != NULL)
|
|
*coercedTypmod = acoerce->resulttypmod;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* expression_returns_set
|
|
* Test whether an expression returns a set result.
|
|
*
|
|
* Because we use expression_tree_walker(), this can also be applied to
|
|
* whole targetlists; it'll produce TRUE if any one of the tlist items
|
|
* returns a set.
|
|
*/
|
|
bool
|
|
expression_returns_set(Node *clause)
|
|
{
|
|
return expression_returns_set_walker(clause, NULL);
|
|
}
|
|
|
|
static bool
|
|
expression_returns_set_walker(Node *node, void *context)
|
|
{
|
|
if (node == NULL)
|
|
return false;
|
|
if (IsA(node, FuncExpr))
|
|
{
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
if (expr->funcretset)
|
|
return true;
|
|
/* else fall through to check args */
|
|
}
|
|
if (IsA(node, OpExpr))
|
|
{
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
if (expr->opretset)
|
|
return true;
|
|
/* else fall through to check args */
|
|
}
|
|
|
|
/* Avoid recursion for some cases that can't return a set */
|
|
if (IsA(node, Aggref))
|
|
return false;
|
|
if (IsA(node, WindowFunc))
|
|
return false;
|
|
if (IsA(node, DistinctExpr))
|
|
return false;
|
|
if (IsA(node, ScalarArrayOpExpr))
|
|
return false;
|
|
if (IsA(node, BoolExpr))
|
|
return false;
|
|
if (IsA(node, SubLink))
|
|
return false;
|
|
if (IsA(node, SubPlan))
|
|
return false;
|
|
if (IsA(node, AlternativeSubPlan))
|
|
return false;
|
|
if (IsA(node, ArrayExpr))
|
|
return false;
|
|
if (IsA(node, RowExpr))
|
|
return false;
|
|
if (IsA(node, RowCompareExpr))
|
|
return false;
|
|
if (IsA(node, CoalesceExpr))
|
|
return false;
|
|
if (IsA(node, MinMaxExpr))
|
|
return false;
|
|
if (IsA(node, XmlExpr))
|
|
return false;
|
|
if (IsA(node, NullIfExpr))
|
|
return false;
|
|
|
|
return expression_tree_walker(node, expression_returns_set_walker,
|
|
context);
|
|
}
|
|
|
|
|
|
/*
|
|
* 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_WindowFunc:
|
|
/* function name should always be the first thing */
|
|
loc = ((WindowFunc *) 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_CoerceViaIO:
|
|
{
|
|
CoerceViaIO *cexpr = (CoerceViaIO *) expr;
|
|
|
|
/* Much as above */
|
|
loc = leftmostLoc(cexpr->location,
|
|
exprLocation((Node *) cexpr->arg));
|
|
}
|
|
break;
|
|
case T_ArrayCoerceExpr:
|
|
{
|
|
ArrayCoerceExpr *cexpr = (ArrayCoerceExpr *) expr;
|
|
|
|
/* Much as above */
|
|
loc = leftmostLoc(cexpr->location,
|
|
exprLocation((Node *) cexpr->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_XmlExpr:
|
|
{
|
|
XmlExpr *xexpr = (XmlExpr *) expr;
|
|
|
|
/* consider both function name and leftmost arg */
|
|
loc = leftmostLoc(xexpr->location,
|
|
exprLocation((Node *) xexpr->args));
|
|
}
|
|
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_A_ArrayExpr:
|
|
/* the location points at ARRAY or [, which must be leftmost */
|
|
loc = ((A_ArrayExpr *) expr)->location;
|
|
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->typename->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_WindowDef:
|
|
loc = ((WindowDef *) expr)->location;
|
|
break;
|
|
case T_TypeName:
|
|
loc = ((TypeName *) expr)->location;
|
|
break;
|
|
case T_XmlSerialize:
|
|
/* XMLSERIALIZE keyword should always be the first thing */
|
|
loc = ((XmlSerialize *) expr)->location;
|
|
break;
|
|
case T_WithClause:
|
|
loc = ((WithClause *) expr)->location;
|
|
break;
|
|
case T_CommonTableExpr:
|
|
loc = ((CommonTableExpr *) expr)->location;
|
|
break;
|
|
case T_PlaceHolderVar:
|
|
/* just use argument's location */
|
|
loc = exprLocation((Node *) ((PlaceHolderVar *) expr)->phexpr);
|
|
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);
|
|
}
|
|
|
|
|
|
/*
|
|
* Standard expression-tree walking support
|
|
*
|
|
* We used to have near-duplicate code in many different routines that
|
|
* understood how to recurse through an expression node tree. That was
|
|
* a pain to maintain, and we frequently had bugs due to some particular
|
|
* routine neglecting to support a particular node type. In most cases,
|
|
* these routines only actually care about certain node types, and don't
|
|
* care about other types except insofar as they have to recurse through
|
|
* non-primitive node types. Therefore, we now provide generic tree-walking
|
|
* logic to consolidate the redundant "boilerplate" code. There are
|
|
* two versions: expression_tree_walker() and expression_tree_mutator().
|
|
*/
|
|
|
|
/*
|
|
* expression_tree_walker() is designed to support routines that traverse
|
|
* a tree in a read-only fashion (although it will also work for routines
|
|
* that modify nodes in-place but never add/delete/replace nodes).
|
|
* A walker routine should look like this:
|
|
*
|
|
* bool my_walker (Node *node, my_struct *context)
|
|
* {
|
|
* if (node == NULL)
|
|
* return false;
|
|
* // check for nodes that special work is required for, eg:
|
|
* if (IsA(node, Var))
|
|
* {
|
|
* ... do special actions for Var nodes
|
|
* }
|
|
* else if (IsA(node, ...))
|
|
* {
|
|
* ... do special actions for other node types
|
|
* }
|
|
* // for any node type not specially processed, do:
|
|
* return expression_tree_walker(node, my_walker, (void *) context);
|
|
* }
|
|
*
|
|
* The "context" argument points to a struct that holds whatever context
|
|
* information the walker routine needs --- it can be used to return data
|
|
* gathered by the walker, too. This argument is not touched by
|
|
* expression_tree_walker, but it is passed down to recursive sub-invocations
|
|
* of my_walker. The tree walk is started from a setup routine that
|
|
* fills in the appropriate context struct, calls my_walker with the top-level
|
|
* node of the tree, and then examines the results.
|
|
*
|
|
* The walker routine should return "false" to continue the tree walk, or
|
|
* "true" to abort the walk and immediately return "true" to the top-level
|
|
* caller. This can be used to short-circuit the traversal if the walker
|
|
* has found what it came for. "false" is returned to the top-level caller
|
|
* iff no invocation of the walker returned "true".
|
|
*
|
|
* The node types handled by expression_tree_walker include all those
|
|
* normally found in target lists and qualifier clauses during the planning
|
|
* stage. In particular, it handles List nodes since a cnf-ified qual clause
|
|
* will have List structure at the top level, and it handles TargetEntry nodes
|
|
* so that a scan of a target list can be handled without additional code.
|
|
* Also, RangeTblRef, FromExpr, JoinExpr, and SetOperationStmt nodes are
|
|
* handled, so that query jointrees and setOperation trees can be processed
|
|
* without additional code.
|
|
*
|
|
* expression_tree_walker will handle SubLink nodes by recursing normally
|
|
* into the "testexpr" subtree (which is an expression belonging to the outer
|
|
* plan). It will also call the walker on the sub-Query node; however, when
|
|
* expression_tree_walker itself is called on a Query node, it does nothing
|
|
* and returns "false". The net effect is that unless the walker does
|
|
* something special at a Query node, sub-selects will not be visited during
|
|
* an expression tree walk. This is exactly the behavior wanted in many cases
|
|
* --- and for those walkers that do want to recurse into sub-selects, special
|
|
* behavior is typically needed anyway at the entry to a sub-select (such as
|
|
* incrementing a depth counter). A walker that wants to examine sub-selects
|
|
* should include code along the lines of:
|
|
*
|
|
* if (IsA(node, Query))
|
|
* {
|
|
* adjust context for subquery;
|
|
* result = query_tree_walker((Query *) node, my_walker, context,
|
|
* 0); // adjust flags as needed
|
|
* restore context if needed;
|
|
* return result;
|
|
* }
|
|
*
|
|
* query_tree_walker is a convenience routine (see below) that calls the
|
|
* walker on all the expression subtrees of the given Query node.
|
|
*
|
|
* expression_tree_walker will handle SubPlan nodes by recursing normally
|
|
* into the "testexpr" and the "args" list (which are expressions belonging to
|
|
* the outer plan). It will not touch the completed subplan, however. Since
|
|
* there is no link to the original Query, it is not possible to recurse into
|
|
* subselects of an already-planned expression tree. This is OK for current
|
|
* uses, but may need to be revisited in future.
|
|
*/
|
|
|
|
bool
|
|
expression_tree_walker(Node *node,
|
|
bool (*walker) (),
|
|
void *context)
|
|
{
|
|
ListCell *temp;
|
|
|
|
/*
|
|
* The walker has already visited the current node, and so we need only
|
|
* recurse into any sub-nodes it has.
|
|
*
|
|
* We assume that the walker is not interested in List nodes per se, so
|
|
* when we expect a List we just recurse directly to self without
|
|
* bothering to call the walker.
|
|
*/
|
|
if (node == NULL)
|
|
return false;
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
check_stack_depth();
|
|
|
|
switch (nodeTag(node))
|
|
{
|
|
case T_Var:
|
|
case T_Const:
|
|
case T_Param:
|
|
case T_CoerceToDomainValue:
|
|
case T_CaseTestExpr:
|
|
case T_SetToDefault:
|
|
case T_CurrentOfExpr:
|
|
case T_RangeTblRef:
|
|
/* primitive node types with no expression subnodes */
|
|
break;
|
|
case T_Aggref:
|
|
{
|
|
Aggref *expr = (Aggref *) node;
|
|
|
|
/* recurse directly on List */
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_WindowFunc:
|
|
{
|
|
WindowFunc *expr = (WindowFunc *) node;
|
|
|
|
/* recurse directly on List */
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_ArrayRef:
|
|
{
|
|
ArrayRef *aref = (ArrayRef *) node;
|
|
|
|
/* recurse directly for upper/lower array index lists */
|
|
if (expression_tree_walker((Node *) aref->refupperindexpr,
|
|
walker, context))
|
|
return true;
|
|
if (expression_tree_walker((Node *) aref->reflowerindexpr,
|
|
walker, context))
|
|
return true;
|
|
/* walker must see the refexpr and refassgnexpr, however */
|
|
if (walker(aref->refexpr, context))
|
|
return true;
|
|
if (walker(aref->refassgnexpr, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_FuncExpr:
|
|
{
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_OpExpr:
|
|
{
|
|
OpExpr *expr = (OpExpr *) node;
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_DistinctExpr:
|
|
{
|
|
DistinctExpr *expr = (DistinctExpr *) node;
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_ScalarArrayOpExpr:
|
|
{
|
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_BoolExpr:
|
|
{
|
|
BoolExpr *expr = (BoolExpr *) node;
|
|
|
|
if (expression_tree_walker((Node *) expr->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) node;
|
|
|
|
if (walker(sublink->testexpr, context))
|
|
return true;
|
|
|
|
/*
|
|
* Also invoke the walker on the sublink's Query node, so it
|
|
* can recurse into the sub-query if it wants to.
|
|
*/
|
|
return walker(sublink->subselect, context);
|
|
}
|
|
break;
|
|
case T_SubPlan:
|
|
{
|
|
SubPlan *subplan = (SubPlan *) node;
|
|
|
|
/* recurse into the testexpr, but not into the Plan */
|
|
if (walker(subplan->testexpr, context))
|
|
return true;
|
|
/* also examine args list */
|
|
if (expression_tree_walker((Node *) subplan->args,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_AlternativeSubPlan:
|
|
return walker(((AlternativeSubPlan *) node)->subplans, context);
|
|
case T_FieldSelect:
|
|
return walker(((FieldSelect *) node)->arg, context);
|
|
case T_FieldStore:
|
|
{
|
|
FieldStore *fstore = (FieldStore *) node;
|
|
|
|
if (walker(fstore->arg, context))
|
|
return true;
|
|
if (walker(fstore->newvals, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_RelabelType:
|
|
return walker(((RelabelType *) node)->arg, context);
|
|
case T_CoerceViaIO:
|
|
return walker(((CoerceViaIO *) node)->arg, context);
|
|
case T_ArrayCoerceExpr:
|
|
return walker(((ArrayCoerceExpr *) node)->arg, context);
|
|
case T_ConvertRowtypeExpr:
|
|
return walker(((ConvertRowtypeExpr *) node)->arg, context);
|
|
case T_CaseExpr:
|
|
{
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
|
|
if (walker(caseexpr->arg, context))
|
|
return true;
|
|
/* we assume walker doesn't care about CaseWhens, either */
|
|
foreach(temp, caseexpr->args)
|
|
{
|
|
CaseWhen *when = (CaseWhen *) lfirst(temp);
|
|
|
|
Assert(IsA(when, CaseWhen));
|
|
if (walker(when->expr, context))
|
|
return true;
|
|
if (walker(when->result, context))
|
|
return true;
|
|
}
|
|
if (walker(caseexpr->defresult, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_ArrayExpr:
|
|
return walker(((ArrayExpr *) node)->elements, context);
|
|
case T_RowExpr:
|
|
/* Assume colnames isn't interesting */
|
|
return walker(((RowExpr *) node)->args, context);
|
|
case T_RowCompareExpr:
|
|
{
|
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
|
|
|
if (walker(rcexpr->largs, context))
|
|
return true;
|
|
if (walker(rcexpr->rargs, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_CoalesceExpr:
|
|
return walker(((CoalesceExpr *) node)->args, context);
|
|
case T_MinMaxExpr:
|
|
return walker(((MinMaxExpr *) node)->args, context);
|
|
case T_XmlExpr:
|
|
{
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
|
|
if (walker(xexpr->named_args, context))
|
|
return true;
|
|
/* we assume walker doesn't care about arg_names */
|
|
if (walker(xexpr->args, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_NullIfExpr:
|
|
return walker(((NullIfExpr *) node)->args, context);
|
|
case T_NullTest:
|
|
return walker(((NullTest *) node)->arg, context);
|
|
case T_BooleanTest:
|
|
return walker(((BooleanTest *) node)->arg, context);
|
|
case T_CoerceToDomain:
|
|
return walker(((CoerceToDomain *) node)->arg, context);
|
|
case T_TargetEntry:
|
|
return walker(((TargetEntry *) node)->expr, context);
|
|
case T_Query:
|
|
/* Do nothing with a sub-Query, per discussion above */
|
|
break;
|
|
case T_WindowClause:
|
|
{
|
|
WindowClause *wc = (WindowClause *) node;
|
|
|
|
if (walker(wc->partitionClause, context))
|
|
return true;
|
|
if (walker(wc->orderClause, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_CommonTableExpr:
|
|
{
|
|
CommonTableExpr *cte = (CommonTableExpr *) node;
|
|
|
|
/*
|
|
* Invoke the walker on the CTE's Query node, so it
|
|
* can recurse into the sub-query if it wants to.
|
|
*/
|
|
return walker(cte->ctequery, context);
|
|
}
|
|
break;
|
|
case T_List:
|
|
foreach(temp, (List *) node)
|
|
{
|
|
if (walker((Node *) lfirst(temp), context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_FromExpr:
|
|
{
|
|
FromExpr *from = (FromExpr *) node;
|
|
|
|
if (walker(from->fromlist, context))
|
|
return true;
|
|
if (walker(from->quals, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_JoinExpr:
|
|
{
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
|
|
if (walker(join->larg, context))
|
|
return true;
|
|
if (walker(join->rarg, context))
|
|
return true;
|
|
if (walker(join->quals, context))
|
|
return true;
|
|
|
|
/*
|
|
* alias clause, using list are deemed uninteresting.
|
|
*/
|
|
}
|
|
break;
|
|
case T_SetOperationStmt:
|
|
{
|
|
SetOperationStmt *setop = (SetOperationStmt *) node;
|
|
|
|
if (walker(setop->larg, context))
|
|
return true;
|
|
if (walker(setop->rarg, context))
|
|
return true;
|
|
|
|
/* groupClauses are deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_PlaceHolderVar:
|
|
return walker(((PlaceHolderVar *) node)->phexpr, context);
|
|
case T_AppendRelInfo:
|
|
{
|
|
AppendRelInfo *appinfo = (AppendRelInfo *) node;
|
|
|
|
if (expression_tree_walker((Node *) appinfo->translated_vars,
|
|
walker, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_PlaceHolderInfo:
|
|
return walker(((PlaceHolderInfo *) node)->ph_var, context);
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(node));
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* query_tree_walker --- initiate a walk of a Query's expressions
|
|
*
|
|
* This routine exists just to reduce the number of places that need to know
|
|
* where all the expression subtrees of a Query are. Note it can be used
|
|
* for starting a walk at top level of a Query regardless of whether the
|
|
* walker intends to descend into subqueries. It is also useful for
|
|
* descending into subqueries within a walker.
|
|
*
|
|
* Some callers want to suppress visitation of certain items in the sub-Query,
|
|
* typically because they need to process them specially, or don't actually
|
|
* want to recurse into subqueries. This is supported by the flags argument,
|
|
* which is the bitwise OR of flag values to suppress visitation of
|
|
* indicated items. (More flag bits may be added as needed.)
|
|
*/
|
|
bool
|
|
query_tree_walker(Query *query,
|
|
bool (*walker) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
Assert(query != NULL && IsA(query, Query));
|
|
|
|
if (walker((Node *) query->targetList, context))
|
|
return true;
|
|
if (walker((Node *) query->returningList, context))
|
|
return true;
|
|
if (walker((Node *) query->jointree, context))
|
|
return true;
|
|
if (walker(query->setOperations, context))
|
|
return true;
|
|
if (walker(query->havingQual, context))
|
|
return true;
|
|
if (walker(query->limitOffset, context))
|
|
return true;
|
|
if (walker(query->limitCount, context))
|
|
return true;
|
|
if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
|
|
{
|
|
if (walker((Node *) query->cteList, context))
|
|
return true;
|
|
}
|
|
if (range_table_walker(query->rtable, walker, context, flags))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
* range_table_walker is just the part of query_tree_walker that scans
|
|
* a query's rangetable. This is split out since it can be useful on
|
|
* its own.
|
|
*/
|
|
bool
|
|
range_table_walker(List *rtable,
|
|
bool (*walker) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
ListCell *rt;
|
|
|
|
foreach(rt, rtable)
|
|
{
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
|
|
|
/* For historical reasons, visiting RTEs is not the default */
|
|
if (flags & QTW_EXAMINE_RTES)
|
|
if (walker(rte, context))
|
|
return true;
|
|
|
|
switch (rte->rtekind)
|
|
{
|
|
case RTE_RELATION:
|
|
case RTE_SPECIAL:
|
|
case RTE_CTE:
|
|
/* nothing to do */
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
|
|
if (walker(rte->subquery, context))
|
|
return true;
|
|
break;
|
|
case RTE_JOIN:
|
|
if (!(flags & QTW_IGNORE_JOINALIASES))
|
|
if (walker(rte->joinaliasvars, context))
|
|
return true;
|
|
break;
|
|
case RTE_FUNCTION:
|
|
if (walker(rte->funcexpr, context))
|
|
return true;
|
|
break;
|
|
case RTE_VALUES:
|
|
if (walker(rte->values_lists, context))
|
|
return true;
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* expression_tree_mutator() is designed to support routines that make a
|
|
* modified copy of an expression tree, with some nodes being added,
|
|
* removed, or replaced by new subtrees. The original tree is (normally)
|
|
* not changed. Each recursion level is responsible for returning a copy of
|
|
* (or appropriately modified substitute for) the subtree it is handed.
|
|
* A mutator routine should look like this:
|
|
*
|
|
* Node * my_mutator (Node *node, my_struct *context)
|
|
* {
|
|
* if (node == NULL)
|
|
* return NULL;
|
|
* // check for nodes that special work is required for, eg:
|
|
* if (IsA(node, Var))
|
|
* {
|
|
* ... create and return modified copy of Var node
|
|
* }
|
|
* else if (IsA(node, ...))
|
|
* {
|
|
* ... do special transformations of other node types
|
|
* }
|
|
* // for any node type not specially processed, do:
|
|
* return expression_tree_mutator(node, my_mutator, (void *) context);
|
|
* }
|
|
*
|
|
* The "context" argument points to a struct that holds whatever context
|
|
* information the mutator routine needs --- it can be used to return extra
|
|
* data gathered by the mutator, too. This argument is not touched by
|
|
* expression_tree_mutator, but it is passed down to recursive sub-invocations
|
|
* of my_mutator. The tree walk is started from a setup routine that
|
|
* fills in the appropriate context struct, calls my_mutator with the
|
|
* top-level node of the tree, and does any required post-processing.
|
|
*
|
|
* Each level of recursion must return an appropriately modified Node.
|
|
* If expression_tree_mutator() is called, it will make an exact copy
|
|
* of the given Node, but invoke my_mutator() to copy the sub-node(s)
|
|
* of that Node. In this way, my_mutator() has full control over the
|
|
* copying process but need not directly deal with expression trees
|
|
* that it has no interest in.
|
|
*
|
|
* Just as for expression_tree_walker, the node types handled by
|
|
* expression_tree_mutator include all those normally found in target lists
|
|
* and qualifier clauses during the planning stage.
|
|
*
|
|
* expression_tree_mutator will handle SubLink nodes by recursing normally
|
|
* into the "testexpr" subtree (which is an expression belonging to the outer
|
|
* plan). It will also call the mutator on the sub-Query node; however, when
|
|
* expression_tree_mutator itself is called on a Query node, it does nothing
|
|
* and returns the unmodified Query node. The net effect is that unless the
|
|
* mutator does something special at a Query node, sub-selects will not be
|
|
* visited or modified; the original sub-select will be linked to by the new
|
|
* SubLink node. Mutators that want to descend into sub-selects will usually
|
|
* do so by recognizing Query nodes and calling query_tree_mutator (below).
|
|
*
|
|
* expression_tree_mutator will handle a SubPlan node by recursing into the
|
|
* "testexpr" and the "args" list (which belong to the outer plan), but it
|
|
* will simply copy the link to the inner plan, since that's typically what
|
|
* expression tree mutators want. A mutator that wants to modify the subplan
|
|
* can force appropriate behavior by recognizing SubPlan expression nodes
|
|
* and doing the right thing.
|
|
*/
|
|
|
|
Node *
|
|
expression_tree_mutator(Node *node,
|
|
Node *(*mutator) (),
|
|
void *context)
|
|
{
|
|
/*
|
|
* The mutator has already decided not to modify the current node, but we
|
|
* must call the mutator for any sub-nodes.
|
|
*/
|
|
|
|
#define FLATCOPY(newnode, node, nodetype) \
|
|
( (newnode) = (nodetype *) palloc(sizeof(nodetype)), \
|
|
memcpy((newnode), (node), sizeof(nodetype)) )
|
|
|
|
#define CHECKFLATCOPY(newnode, node, nodetype) \
|
|
( AssertMacro(IsA((node), nodetype)), \
|
|
(newnode) = (nodetype *) palloc(sizeof(nodetype)), \
|
|
memcpy((newnode), (node), sizeof(nodetype)) )
|
|
|
|
#define MUTATE(newfield, oldfield, fieldtype) \
|
|
( (newfield) = (fieldtype) mutator((Node *) (oldfield), context) )
|
|
|
|
if (node == NULL)
|
|
return NULL;
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
check_stack_depth();
|
|
|
|
switch (nodeTag(node))
|
|
{
|
|
/*
|
|
* Primitive node types with no expression subnodes. Var and
|
|
* Const are frequent enough to deserve special cases, the others
|
|
* we just use copyObject for.
|
|
*/
|
|
case T_Var:
|
|
{
|
|
Var *var = (Var *) node;
|
|
Var *newnode;
|
|
|
|
FLATCOPY(newnode, var, Var);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_Const:
|
|
{
|
|
Const *oldnode = (Const *) node;
|
|
Const *newnode;
|
|
|
|
FLATCOPY(newnode, oldnode, Const);
|
|
/* XXX we don't bother with datumCopy; should we? */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_Param:
|
|
case T_CoerceToDomainValue:
|
|
case T_CaseTestExpr:
|
|
case T_SetToDefault:
|
|
case T_CurrentOfExpr:
|
|
case T_RangeTblRef:
|
|
return (Node *) copyObject(node);
|
|
case T_Aggref:
|
|
{
|
|
Aggref *aggref = (Aggref *) node;
|
|
Aggref *newnode;
|
|
|
|
FLATCOPY(newnode, aggref, Aggref);
|
|
MUTATE(newnode->args, aggref->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_WindowFunc:
|
|
{
|
|
WindowFunc *wfunc = (WindowFunc *) node;
|
|
WindowFunc *newnode;
|
|
|
|
FLATCOPY(newnode, wfunc, WindowFunc);
|
|
MUTATE(newnode->args, wfunc->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_ArrayRef:
|
|
{
|
|
ArrayRef *arrayref = (ArrayRef *) node;
|
|
ArrayRef *newnode;
|
|
|
|
FLATCOPY(newnode, arrayref, ArrayRef);
|
|
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
|
|
List *);
|
|
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
|
|
List *);
|
|
MUTATE(newnode->refexpr, arrayref->refexpr,
|
|
Expr *);
|
|
MUTATE(newnode->refassgnexpr, arrayref->refassgnexpr,
|
|
Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_FuncExpr:
|
|
{
|
|
FuncExpr *expr = (FuncExpr *) node;
|
|
FuncExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, FuncExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_OpExpr:
|
|
{
|
|
OpExpr *expr = (OpExpr *) node;
|
|
OpExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, OpExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_DistinctExpr:
|
|
{
|
|
DistinctExpr *expr = (DistinctExpr *) node;
|
|
DistinctExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, DistinctExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_ScalarArrayOpExpr:
|
|
{
|
|
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
|
ScalarArrayOpExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, ScalarArrayOpExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_BoolExpr:
|
|
{
|
|
BoolExpr *expr = (BoolExpr *) node;
|
|
BoolExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, BoolExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) node;
|
|
SubLink *newnode;
|
|
|
|
FLATCOPY(newnode, sublink, SubLink);
|
|
MUTATE(newnode->testexpr, sublink->testexpr, Node *);
|
|
|
|
/*
|
|
* Also invoke the mutator on the sublink's Query node, so it
|
|
* can recurse into the sub-query if it wants to.
|
|
*/
|
|
MUTATE(newnode->subselect, sublink->subselect, Node *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_SubPlan:
|
|
{
|
|
SubPlan *subplan = (SubPlan *) node;
|
|
SubPlan *newnode;
|
|
|
|
FLATCOPY(newnode, subplan, SubPlan);
|
|
/* transform testexpr */
|
|
MUTATE(newnode->testexpr, subplan->testexpr, Node *);
|
|
/* transform args list (params to be passed to subplan) */
|
|
MUTATE(newnode->args, subplan->args, List *);
|
|
/* but not the sub-Plan itself, which is referenced as-is */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_AlternativeSubPlan:
|
|
{
|
|
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
|
|
AlternativeSubPlan *newnode;
|
|
|
|
FLATCOPY(newnode, asplan, AlternativeSubPlan);
|
|
MUTATE(newnode->subplans, asplan->subplans, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_FieldSelect:
|
|
{
|
|
FieldSelect *fselect = (FieldSelect *) node;
|
|
FieldSelect *newnode;
|
|
|
|
FLATCOPY(newnode, fselect, FieldSelect);
|
|
MUTATE(newnode->arg, fselect->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_FieldStore:
|
|
{
|
|
FieldStore *fstore = (FieldStore *) node;
|
|
FieldStore *newnode;
|
|
|
|
FLATCOPY(newnode, fstore, FieldStore);
|
|
MUTATE(newnode->arg, fstore->arg, Expr *);
|
|
MUTATE(newnode->newvals, fstore->newvals, List *);
|
|
newnode->fieldnums = list_copy(fstore->fieldnums);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_RelabelType:
|
|
{
|
|
RelabelType *relabel = (RelabelType *) node;
|
|
RelabelType *newnode;
|
|
|
|
FLATCOPY(newnode, relabel, RelabelType);
|
|
MUTATE(newnode->arg, relabel->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CoerceViaIO:
|
|
{
|
|
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
|
|
CoerceViaIO *newnode;
|
|
|
|
FLATCOPY(newnode, iocoerce, CoerceViaIO);
|
|
MUTATE(newnode->arg, iocoerce->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_ArrayCoerceExpr:
|
|
{
|
|
ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
|
|
ArrayCoerceExpr *newnode;
|
|
|
|
FLATCOPY(newnode, acoerce, ArrayCoerceExpr);
|
|
MUTATE(newnode->arg, acoerce->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_ConvertRowtypeExpr:
|
|
{
|
|
ConvertRowtypeExpr *convexpr = (ConvertRowtypeExpr *) node;
|
|
ConvertRowtypeExpr *newnode;
|
|
|
|
FLATCOPY(newnode, convexpr, ConvertRowtypeExpr);
|
|
MUTATE(newnode->arg, convexpr->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CaseExpr:
|
|
{
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
CaseExpr *newnode;
|
|
|
|
FLATCOPY(newnode, caseexpr, CaseExpr);
|
|
MUTATE(newnode->arg, caseexpr->arg, Expr *);
|
|
MUTATE(newnode->args, caseexpr->args, List *);
|
|
MUTATE(newnode->defresult, caseexpr->defresult, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CaseWhen:
|
|
{
|
|
CaseWhen *casewhen = (CaseWhen *) node;
|
|
CaseWhen *newnode;
|
|
|
|
FLATCOPY(newnode, casewhen, CaseWhen);
|
|
MUTATE(newnode->expr, casewhen->expr, Expr *);
|
|
MUTATE(newnode->result, casewhen->result, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_ArrayExpr:
|
|
{
|
|
ArrayExpr *arrayexpr = (ArrayExpr *) node;
|
|
ArrayExpr *newnode;
|
|
|
|
FLATCOPY(newnode, arrayexpr, ArrayExpr);
|
|
MUTATE(newnode->elements, arrayexpr->elements, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_RowExpr:
|
|
{
|
|
RowExpr *rowexpr = (RowExpr *) node;
|
|
RowExpr *newnode;
|
|
|
|
FLATCOPY(newnode, rowexpr, RowExpr);
|
|
MUTATE(newnode->args, rowexpr->args, List *);
|
|
/* Assume colnames needn't be duplicated */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_RowCompareExpr:
|
|
{
|
|
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
|
RowCompareExpr *newnode;
|
|
|
|
FLATCOPY(newnode, rcexpr, RowCompareExpr);
|
|
MUTATE(newnode->largs, rcexpr->largs, List *);
|
|
MUTATE(newnode->rargs, rcexpr->rargs, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CoalesceExpr:
|
|
{
|
|
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
|
|
CoalesceExpr *newnode;
|
|
|
|
FLATCOPY(newnode, coalesceexpr, CoalesceExpr);
|
|
MUTATE(newnode->args, coalesceexpr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_MinMaxExpr:
|
|
{
|
|
MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
|
|
MinMaxExpr *newnode;
|
|
|
|
FLATCOPY(newnode, minmaxexpr, MinMaxExpr);
|
|
MUTATE(newnode->args, minmaxexpr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_XmlExpr:
|
|
{
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
XmlExpr *newnode;
|
|
|
|
FLATCOPY(newnode, xexpr, XmlExpr);
|
|
MUTATE(newnode->named_args, xexpr->named_args, List *);
|
|
/* assume mutator does not care about arg_names */
|
|
MUTATE(newnode->args, xexpr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_NullIfExpr:
|
|
{
|
|
NullIfExpr *expr = (NullIfExpr *) node;
|
|
NullIfExpr *newnode;
|
|
|
|
FLATCOPY(newnode, expr, NullIfExpr);
|
|
MUTATE(newnode->args, expr->args, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_NullTest:
|
|
{
|
|
NullTest *ntest = (NullTest *) node;
|
|
NullTest *newnode;
|
|
|
|
FLATCOPY(newnode, ntest, NullTest);
|
|
MUTATE(newnode->arg, ntest->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_BooleanTest:
|
|
{
|
|
BooleanTest *btest = (BooleanTest *) node;
|
|
BooleanTest *newnode;
|
|
|
|
FLATCOPY(newnode, btest, BooleanTest);
|
|
MUTATE(newnode->arg, btest->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CoerceToDomain:
|
|
{
|
|
CoerceToDomain *ctest = (CoerceToDomain *) node;
|
|
CoerceToDomain *newnode;
|
|
|
|
FLATCOPY(newnode, ctest, CoerceToDomain);
|
|
MUTATE(newnode->arg, ctest->arg, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_TargetEntry:
|
|
{
|
|
TargetEntry *targetentry = (TargetEntry *) node;
|
|
TargetEntry *newnode;
|
|
|
|
FLATCOPY(newnode, targetentry, TargetEntry);
|
|
MUTATE(newnode->expr, targetentry->expr, Expr *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_Query:
|
|
/* Do nothing with a sub-Query, per discussion above */
|
|
return node;
|
|
case T_WindowClause:
|
|
{
|
|
WindowClause *wc = (WindowClause *) node;
|
|
WindowClause *newnode;
|
|
|
|
FLATCOPY(newnode, wc, WindowClause);
|
|
MUTATE(newnode->partitionClause, wc->partitionClause, List *);
|
|
MUTATE(newnode->orderClause, wc->orderClause, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_CommonTableExpr:
|
|
{
|
|
CommonTableExpr *cte = (CommonTableExpr *) node;
|
|
CommonTableExpr *newnode;
|
|
|
|
FLATCOPY(newnode, cte, CommonTableExpr);
|
|
|
|
/*
|
|
* Also invoke the mutator on the CTE's Query node, so it
|
|
* can recurse into the sub-query if it wants to.
|
|
*/
|
|
MUTATE(newnode->ctequery, cte->ctequery, Node *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_List:
|
|
{
|
|
/*
|
|
* We assume the mutator isn't interested in the list nodes
|
|
* per se, so just invoke it on each list element. NOTE: this
|
|
* would fail badly on a list with integer elements!
|
|
*/
|
|
List *resultlist;
|
|
ListCell *temp;
|
|
|
|
resultlist = NIL;
|
|
foreach(temp, (List *) node)
|
|
{
|
|
resultlist = lappend(resultlist,
|
|
mutator((Node *) lfirst(temp),
|
|
context));
|
|
}
|
|
return (Node *) resultlist;
|
|
}
|
|
break;
|
|
case T_FromExpr:
|
|
{
|
|
FromExpr *from = (FromExpr *) node;
|
|
FromExpr *newnode;
|
|
|
|
FLATCOPY(newnode, from, FromExpr);
|
|
MUTATE(newnode->fromlist, from->fromlist, List *);
|
|
MUTATE(newnode->quals, from->quals, Node *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_JoinExpr:
|
|
{
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
JoinExpr *newnode;
|
|
|
|
FLATCOPY(newnode, join, JoinExpr);
|
|
MUTATE(newnode->larg, join->larg, Node *);
|
|
MUTATE(newnode->rarg, join->rarg, Node *);
|
|
MUTATE(newnode->quals, join->quals, Node *);
|
|
/* We do not mutate alias or using by default */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_SetOperationStmt:
|
|
{
|
|
SetOperationStmt *setop = (SetOperationStmt *) node;
|
|
SetOperationStmt *newnode;
|
|
|
|
FLATCOPY(newnode, setop, SetOperationStmt);
|
|
MUTATE(newnode->larg, setop->larg, Node *);
|
|
MUTATE(newnode->rarg, setop->rarg, Node *);
|
|
/* We do not mutate groupClauses by default */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_PlaceHolderVar:
|
|
{
|
|
PlaceHolderVar *phv = (PlaceHolderVar *) node;
|
|
PlaceHolderVar *newnode;
|
|
|
|
FLATCOPY(newnode, phv, PlaceHolderVar);
|
|
MUTATE(newnode->phexpr, phv->phexpr, Expr *);
|
|
/* Assume we need not copy the relids bitmapset */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_AppendRelInfo:
|
|
{
|
|
AppendRelInfo *appinfo = (AppendRelInfo *) node;
|
|
AppendRelInfo *newnode;
|
|
|
|
FLATCOPY(newnode, appinfo, AppendRelInfo);
|
|
MUTATE(newnode->translated_vars, appinfo->translated_vars, List *);
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
case T_PlaceHolderInfo:
|
|
{
|
|
PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node;
|
|
PlaceHolderInfo *newnode;
|
|
|
|
FLATCOPY(newnode, phinfo, PlaceHolderInfo);
|
|
MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *);
|
|
/* Assume we need not copy the relids bitmapsets */
|
|
return (Node *) newnode;
|
|
}
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(node));
|
|
break;
|
|
}
|
|
/* can't get here, but keep compiler happy */
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* query_tree_mutator --- initiate modification of a Query's expressions
|
|
*
|
|
* This routine exists just to reduce the number of places that need to know
|
|
* where all the expression subtrees of a Query are. Note it can be used
|
|
* for starting a walk at top level of a Query regardless of whether the
|
|
* mutator intends to descend into subqueries. It is also useful for
|
|
* descending into subqueries within a mutator.
|
|
*
|
|
* Some callers want to suppress mutating of certain items in the Query,
|
|
* typically because they need to process them specially, or don't actually
|
|
* want to recurse into subqueries. This is supported by the flags argument,
|
|
* which is the bitwise OR of flag values to suppress mutating of
|
|
* indicated items. (More flag bits may be added as needed.)
|
|
*
|
|
* Normally the Query node itself is copied, but some callers want it to be
|
|
* modified in-place; they must pass QTW_DONT_COPY_QUERY in flags. All
|
|
* modified substructure is safely copied in any case.
|
|
*/
|
|
Query *
|
|
query_tree_mutator(Query *query,
|
|
Node *(*mutator) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
Assert(query != NULL && IsA(query, Query));
|
|
|
|
if (!(flags & QTW_DONT_COPY_QUERY))
|
|
{
|
|
Query *newquery;
|
|
|
|
FLATCOPY(newquery, query, Query);
|
|
query = newquery;
|
|
}
|
|
|
|
MUTATE(query->targetList, query->targetList, List *);
|
|
MUTATE(query->returningList, query->returningList, List *);
|
|
MUTATE(query->jointree, query->jointree, FromExpr *);
|
|
MUTATE(query->setOperations, query->setOperations, Node *);
|
|
MUTATE(query->havingQual, query->havingQual, Node *);
|
|
MUTATE(query->limitOffset, query->limitOffset, Node *);
|
|
MUTATE(query->limitCount, query->limitCount, Node *);
|
|
if (!(flags & QTW_IGNORE_CTE_SUBQUERIES))
|
|
MUTATE(query->cteList, query->cteList, List *);
|
|
else /* else copy CTE list as-is */
|
|
query->cteList = copyObject(query->cteList);
|
|
query->rtable = range_table_mutator(query->rtable,
|
|
mutator, context, flags);
|
|
return query;
|
|
}
|
|
|
|
/*
|
|
* range_table_mutator is just the part of query_tree_mutator that processes
|
|
* a query's rangetable. This is split out since it can be useful on
|
|
* its own.
|
|
*/
|
|
List *
|
|
range_table_mutator(List *rtable,
|
|
Node *(*mutator) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
List *newrt = NIL;
|
|
ListCell *rt;
|
|
|
|
foreach(rt, rtable)
|
|
{
|
|
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
|
|
RangeTblEntry *newrte;
|
|
|
|
FLATCOPY(newrte, rte, RangeTblEntry);
|
|
switch (rte->rtekind)
|
|
{
|
|
case RTE_RELATION:
|
|
case RTE_SPECIAL:
|
|
case RTE_CTE:
|
|
/* we don't bother to copy eref, aliases, etc; OK? */
|
|
break;
|
|
case RTE_SUBQUERY:
|
|
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
|
|
{
|
|
CHECKFLATCOPY(newrte->subquery, rte->subquery, Query);
|
|
MUTATE(newrte->subquery, newrte->subquery, Query *);
|
|
}
|
|
else
|
|
{
|
|
/* else, copy RT subqueries as-is */
|
|
newrte->subquery = copyObject(rte->subquery);
|
|
}
|
|
break;
|
|
case RTE_JOIN:
|
|
if (!(flags & QTW_IGNORE_JOINALIASES))
|
|
MUTATE(newrte->joinaliasvars, rte->joinaliasvars, List *);
|
|
else
|
|
{
|
|
/* else, copy join aliases as-is */
|
|
newrte->joinaliasvars = copyObject(rte->joinaliasvars);
|
|
}
|
|
break;
|
|
case RTE_FUNCTION:
|
|
MUTATE(newrte->funcexpr, rte->funcexpr, Node *);
|
|
break;
|
|
case RTE_VALUES:
|
|
MUTATE(newrte->values_lists, rte->values_lists, List *);
|
|
break;
|
|
}
|
|
newrt = lappend(newrt, newrte);
|
|
}
|
|
return newrt;
|
|
}
|
|
|
|
/*
|
|
* query_or_expression_tree_walker --- hybrid form
|
|
*
|
|
* This routine will invoke query_tree_walker if called on a Query node,
|
|
* else will invoke the walker directly. This is a useful way of starting
|
|
* the recursion when the walker's normal change of state is not appropriate
|
|
* for the outermost Query node.
|
|
*/
|
|
bool
|
|
query_or_expression_tree_walker(Node *node,
|
|
bool (*walker) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
if (node && IsA(node, Query))
|
|
return query_tree_walker((Query *) node,
|
|
walker,
|
|
context,
|
|
flags);
|
|
else
|
|
return walker(node, context);
|
|
}
|
|
|
|
/*
|
|
* query_or_expression_tree_mutator --- hybrid form
|
|
*
|
|
* This routine will invoke query_tree_mutator if called on a Query node,
|
|
* else will invoke the mutator directly. This is a useful way of starting
|
|
* the recursion when the mutator's normal change of state is not appropriate
|
|
* for the outermost Query node.
|
|
*/
|
|
Node *
|
|
query_or_expression_tree_mutator(Node *node,
|
|
Node *(*mutator) (),
|
|
void *context,
|
|
int flags)
|
|
{
|
|
if (node && IsA(node, Query))
|
|
return (Node *) query_tree_mutator((Query *) node,
|
|
mutator,
|
|
context,
|
|
flags);
|
|
else
|
|
return mutator(node, context);
|
|
}
|
|
|
|
|
|
/*
|
|
* raw_expression_tree_walker --- walk raw parse trees
|
|
*
|
|
* This has exactly the same API as expression_tree_walker, but instead of
|
|
* walking post-analysis parse trees, it knows how to walk the node types
|
|
* found in raw grammar output. (There is not currently any need for a
|
|
* combined walker, so we keep them separate in the name of efficiency.)
|
|
* Unlike expression_tree_walker, there is no special rule about query
|
|
* boundaries: we descend to everything that's possibly interesting.
|
|
*
|
|
* Currently, the node type coverage extends to SelectStmt and everything
|
|
* that could appear under it, but not other statement types.
|
|
*/
|
|
bool
|
|
raw_expression_tree_walker(Node *node, bool (*walker) (), void *context)
|
|
{
|
|
ListCell *temp;
|
|
|
|
/*
|
|
* The walker has already visited the current node, and so we need only
|
|
* recurse into any sub-nodes it has.
|
|
*/
|
|
if (node == NULL)
|
|
return false;
|
|
|
|
/* Guard against stack overflow due to overly complex expressions */
|
|
check_stack_depth();
|
|
|
|
switch (nodeTag(node))
|
|
{
|
|
case T_SetToDefault:
|
|
case T_CurrentOfExpr:
|
|
case T_Integer:
|
|
case T_Float:
|
|
case T_String:
|
|
case T_BitString:
|
|
case T_Null:
|
|
case T_ParamRef:
|
|
case T_A_Const:
|
|
case T_A_Star:
|
|
/* primitive node types with no subnodes */
|
|
break;
|
|
case T_Alias:
|
|
/* we assume the colnames list isn't interesting */
|
|
break;
|
|
case T_RangeVar:
|
|
return walker(((RangeVar *) node)->alias, context);
|
|
case T_SubLink:
|
|
{
|
|
SubLink *sublink = (SubLink *) node;
|
|
|
|
if (walker(sublink->testexpr, context))
|
|
return true;
|
|
/* we assume the operName is not interesting */
|
|
if (walker(sublink->subselect, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_CaseExpr:
|
|
{
|
|
CaseExpr *caseexpr = (CaseExpr *) node;
|
|
|
|
if (walker(caseexpr->arg, context))
|
|
return true;
|
|
/* we assume walker doesn't care about CaseWhens, either */
|
|
foreach(temp, caseexpr->args)
|
|
{
|
|
CaseWhen *when = (CaseWhen *) lfirst(temp);
|
|
|
|
Assert(IsA(when, CaseWhen));
|
|
if (walker(when->expr, context))
|
|
return true;
|
|
if (walker(when->result, context))
|
|
return true;
|
|
}
|
|
if (walker(caseexpr->defresult, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_RowExpr:
|
|
/* Assume colnames isn't interesting */
|
|
return walker(((RowExpr *) node)->args, context);
|
|
case T_CoalesceExpr:
|
|
return walker(((CoalesceExpr *) node)->args, context);
|
|
case T_MinMaxExpr:
|
|
return walker(((MinMaxExpr *) node)->args, context);
|
|
case T_XmlExpr:
|
|
{
|
|
XmlExpr *xexpr = (XmlExpr *) node;
|
|
|
|
if (walker(xexpr->named_args, context))
|
|
return true;
|
|
/* we assume walker doesn't care about arg_names */
|
|
if (walker(xexpr->args, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_NullTest:
|
|
return walker(((NullTest *) node)->arg, context);
|
|
case T_BooleanTest:
|
|
return walker(((BooleanTest *) node)->arg, context);
|
|
case T_JoinExpr:
|
|
{
|
|
JoinExpr *join = (JoinExpr *) node;
|
|
|
|
if (walker(join->larg, context))
|
|
return true;
|
|
if (walker(join->rarg, context))
|
|
return true;
|
|
if (walker(join->quals, context))
|
|
return true;
|
|
if (walker(join->alias, context))
|
|
return true;
|
|
/* using list is deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_IntoClause:
|
|
{
|
|
IntoClause *into = (IntoClause *) node;
|
|
|
|
if (walker(into->rel, context))
|
|
return true;
|
|
/* colNames, options are deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_List:
|
|
foreach(temp, (List *) node)
|
|
{
|
|
if (walker((Node *) lfirst(temp), context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_SelectStmt:
|
|
{
|
|
SelectStmt *stmt = (SelectStmt *) node;
|
|
|
|
if (walker(stmt->distinctClause, context))
|
|
return true;
|
|
if (walker(stmt->intoClause, context))
|
|
return true;
|
|
if (walker(stmt->targetList, context))
|
|
return true;
|
|
if (walker(stmt->fromClause, context))
|
|
return true;
|
|
if (walker(stmt->whereClause, context))
|
|
return true;
|
|
if (walker(stmt->groupClause, context))
|
|
return true;
|
|
if (walker(stmt->havingClause, context))
|
|
return true;
|
|
if (walker(stmt->windowClause, context))
|
|
return true;
|
|
if (walker(stmt->withClause, context))
|
|
return true;
|
|
if (walker(stmt->valuesLists, context))
|
|
return true;
|
|
if (walker(stmt->sortClause, context))
|
|
return true;
|
|
if (walker(stmt->limitOffset, context))
|
|
return true;
|
|
if (walker(stmt->limitCount, context))
|
|
return true;
|
|
if (walker(stmt->lockingClause, context))
|
|
return true;
|
|
if (walker(stmt->larg, context))
|
|
return true;
|
|
if (walker(stmt->rarg, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_A_Expr:
|
|
{
|
|
A_Expr *expr = (A_Expr *) node;
|
|
|
|
if (walker(expr->lexpr, context))
|
|
return true;
|
|
if (walker(expr->rexpr, context))
|
|
return true;
|
|
/* operator name is deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_ColumnRef:
|
|
/* we assume the fields contain nothing interesting */
|
|
break;
|
|
case T_FuncCall:
|
|
{
|
|
FuncCall *fcall = (FuncCall *) node;
|
|
|
|
if (walker(fcall->args, context))
|
|
return true;
|
|
if (walker(fcall->over, context))
|
|
return true;
|
|
/* function name is deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_A_Indices:
|
|
{
|
|
A_Indices *indices = (A_Indices *) node;
|
|
|
|
if (walker(indices->lidx, context))
|
|
return true;
|
|
if (walker(indices->uidx, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_A_Indirection:
|
|
{
|
|
A_Indirection *indir = (A_Indirection *) node;
|
|
|
|
if (walker(indir->arg, context))
|
|
return true;
|
|
if (walker(indir->indirection, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_A_ArrayExpr:
|
|
return walker(((A_ArrayExpr *) node)->elements, context);
|
|
case T_ResTarget:
|
|
{
|
|
ResTarget *rt = (ResTarget *) node;
|
|
|
|
if (walker(rt->indirection, context))
|
|
return true;
|
|
if (walker(rt->val, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_TypeCast:
|
|
{
|
|
TypeCast *tc = (TypeCast *) node;
|
|
|
|
if (walker(tc->arg, context))
|
|
return true;
|
|
if (walker(tc->typename, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_SortBy:
|
|
return walker(((SortBy *) node)->node, context);
|
|
case T_WindowDef:
|
|
{
|
|
WindowDef *wd = (WindowDef *) node;
|
|
|
|
if (walker(wd->partitionClause, context))
|
|
return true;
|
|
if (walker(wd->orderClause, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_RangeSubselect:
|
|
{
|
|
RangeSubselect *rs = (RangeSubselect *) node;
|
|
|
|
if (walker(rs->subquery, context))
|
|
return true;
|
|
if (walker(rs->alias, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_RangeFunction:
|
|
{
|
|
RangeFunction *rf = (RangeFunction *) node;
|
|
|
|
if (walker(rf->funccallnode, context))
|
|
return true;
|
|
if (walker(rf->alias, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_TypeName:
|
|
{
|
|
TypeName *tn = (TypeName *) node;
|
|
|
|
if (walker(tn->typmods, context))
|
|
return true;
|
|
if (walker(tn->arrayBounds, context))
|
|
return true;
|
|
/* type name itself is deemed uninteresting */
|
|
}
|
|
break;
|
|
case T_ColumnDef:
|
|
{
|
|
ColumnDef *coldef = (ColumnDef *) node;
|
|
|
|
if (walker(coldef->typename, context))
|
|
return true;
|
|
if (walker(coldef->raw_default, context))
|
|
return true;
|
|
/* for now, constraints are ignored */
|
|
}
|
|
break;
|
|
case T_LockingClause:
|
|
return walker(((LockingClause *) node)->lockedRels, context);
|
|
case T_XmlSerialize:
|
|
{
|
|
XmlSerialize *xs = (XmlSerialize *) node;
|
|
|
|
if (walker(xs->expr, context))
|
|
return true;
|
|
if (walker(xs->typename, context))
|
|
return true;
|
|
}
|
|
break;
|
|
case T_WithClause:
|
|
return walker(((WithClause *) node)->ctes, context);
|
|
case T_CommonTableExpr:
|
|
return walker(((CommonTableExpr *) node)->ctequery, context);
|
|
default:
|
|
elog(ERROR, "unrecognized node type: %d",
|
|
(int) nodeTag(node));
|
|
break;
|
|
}
|
|
return false;
|
|
}
|