mirror of
https://github.com/postgres/postgres.git
synced 2025-06-17 17:02:08 +03:00
Move exprType(), exprTypmod(), expression_tree_walker(), and related routines
into nodes/nodeFuncs, so as to reduce wanton cross-subsystem #includes inside the backend. There's probably more that should be done along this line, but this is a start anyway.
This commit is contained in:
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.230 2008/08/22 00:16:04 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.231 2008/08/25 22:42:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -17,13 +17,10 @@
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/dbcommands.h"
|
||||
#include "mb/pg_wchar.h"
|
||||
#include "miscadmin.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "nodes/plannodes.h"
|
||||
#include "optimizer/clauses.h"
|
||||
#include "nodes/nodeFuncs.h"
|
||||
#include "parser/analyze.h"
|
||||
#include "parser/gramparse.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "parser/parse_func.h"
|
||||
@ -1863,484 +1860,6 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* exprType -
|
||||
* returns the Oid of the type of the expression. (Used for typechecking.)
|
||||
*/
|
||||
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_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:
|
||||
{
|
||||
/*
|
||||
* Although the parser does not ever deal with already-planned
|
||||
* expression trees, we support SubPlan nodes in this routine
|
||||
* for the convenience of ruleutils.c.
|
||||
*/
|
||||
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:
|
||||
{
|
||||
/* As above, supported for the convenience of ruleutils.c */
|
||||
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;
|
||||
default:
|
||||
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr));
|
||||
type = InvalidOid; /* keep compiler quiet */
|
||||
break;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
/*
|
||||
* exprTypmod -
|
||||
* returns the type-specific attrmod of the expression, if it can be
|
||||
* determined. In most 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_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;
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle an explicit CAST construct.
|
||||
*
|
||||
|
Reference in New Issue
Block a user