diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 298 |
1 files changed, 102 insertions, 196 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 286eb590b1..bc73bc2c2b 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -3,21 +3,18 @@ * parse_expr.c * handle expressions in parser * - * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group + * Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.256 2010/07/06 19:18:57 momjian Exp $ + * src/backend/parser/parse_expr.c * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "catalog/pg_attrdef.h" -#include "catalog/pg_constraint.h" -#include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "commands/dbcommands.h" #include "miscadmin.h" @@ -26,6 +23,7 @@ #include "optimizer/var.h" #include "parser/analyze.h" #include "parser/parse_coerce.h" +#include "parser/parse_collate.h" #include "parser/parse_expr.h" #include "parser/parse_func.h" #include "parser/parse_oper.h" @@ -33,7 +31,6 @@ #include "parser/parse_target.h" #include "parser/parse_type.h" #include "utils/builtins.h" -#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/xml.h" @@ -69,6 +66,7 @@ static Node *transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, static Node *transformIndirection(ParseState *pstate, Node *basenode, List *indirection); static Node *transformTypeCast(ParseState *pstate, TypeCast *tc); +static Node *transformCollateClause(ParseState *pstate, CollateClause *c); static Node *make_row_comparison_op(ParseState *pstate, List *opname, List *largs, List *rargs, int location); static Node *make_row_distinct_op(ParseState *pstate, List *opname, @@ -163,21 +161,20 @@ transformExpr(ParseState *pstate, Node *expr) Oid elementType; int32 targetTypmod; - targetType = typenameTypeId(pstate, tc->typeName, - &targetTypmod); + typenameTypeIdAndMod(pstate, tc->typeName, + &targetType, &targetTypmod); + + /* + * If target is a domain over array, work with the base + * array type here. transformTypeCast below will cast the + * array type to the domain. In the usual case that the + * target is not a domain, transformTypeCast is a no-op. + */ + targetType = getBaseTypeAndTypmod(targetType, + &targetTypmod); elementType = get_element_type(targetType); if (OidIsValid(elementType)) { - /* - * tranformArrayExpr doesn't know how to check domain - * constraints, so ask it to return the base type - * instead. transformTypeCast below will cast it to - * the domain. In the usual case that the target is - * not a domain, transformTypeCast is a no-op. - */ - targetType = getBaseTypeAndTypmod(targetType, - &targetTypmod); - tc = copyObject(tc); tc->arg = transformArrayExpr(pstate, (A_ArrayExpr *) tc->arg, @@ -191,6 +188,10 @@ transformExpr(ParseState *pstate, Node *expr) break; } + case T_CollateClause: + result = transformCollateClause(pstate, (CollateClause *) expr); + break; + case T_A_Expr: { A_Expr *a = (A_Expr *) expr; @@ -310,8 +311,8 @@ transformExpr(ParseState *pstate, Node *expr) case T_FuncExpr: case T_OpExpr: case T_DistinctExpr: - case T_ScalarArrayOpExpr: case T_NullIfExpr: + case T_ScalarArrayOpExpr: case T_BoolExpr: case T_FieldSelect: case T_FieldStore: @@ -319,6 +320,7 @@ transformExpr(ParseState *pstate, Node *expr) case T_CoerceViaIO: case T_ArrayCoerceExpr: case T_ConvertRowtypeExpr: + case T_CollateExpr: case T_CaseTestExpr: case T_ArrayExpr: case T_CoerceToDomain: @@ -999,25 +1001,34 @@ transformAExprNullIf(ParseState *pstate, A_Expr *a) { Node *lexpr = transformExpr(pstate, a->lexpr); Node *rexpr = transformExpr(pstate, a->rexpr); - Node *result; + OpExpr *result; - result = (Node *) make_op(pstate, - a->name, - lexpr, - rexpr, - a->location); - if (((OpExpr *) result)->opresulttype != BOOLOID) + result = (OpExpr *) make_op(pstate, + a->name, + lexpr, + rexpr, + a->location); + + /* + * The comparison operator itself should yield boolean ... + */ + if (result->opresulttype != BOOLOID) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF requires = operator to yield boolean"), parser_errposition(pstate, a->location))); /* + * ... but the NullIfExpr will yield the first operand's type. + */ + result->opresulttype = exprType((Node *) linitial(result->args)); + + /* * We rely on NullIfExpr and OpExpr being the same struct */ NodeSetTag(result, T_NullIfExpr); - return result; + return (Node *) result; } static Node * @@ -1037,7 +1048,7 @@ transformAExprOf(ParseState *pstate, A_Expr *a) ltype = exprType(lexpr); foreach(telem, (List *) a->rexpr) { - rtype = typenameTypeId(pstate, lfirst(telem), NULL); + rtype = typenameTypeId(pstate, lfirst(telem)); matched = (rtype == ltype); if (matched) break; @@ -1151,6 +1162,7 @@ transformAExprIn(ParseState *pstate, A_Expr *a) } newa = makeNode(ArrayExpr); newa->array_typeid = array_type; + /* array_collid will be set by parse_collate.c */ newa->element_typeid = scalar_type; newa->elements = aexprs; newa->multidims = false; @@ -1214,7 +1226,6 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) { List *targs; ListCell *args; - Node *result; /* Transform the list of arguments ... */ targs = NIL; @@ -1225,97 +1236,16 @@ transformFuncCall(ParseState *pstate, FuncCall *fn) } /* ... and hand off to ParseFuncOrColumn */ - result = ParseFuncOrColumn(pstate, - fn->funcname, - targs, - fn->agg_order, - fn->agg_star, - fn->agg_distinct, - fn->func_variadic, - fn->over, - false, - fn->location); - - /* - * pg_get_expr() is a system function that exposes the expression - * deparsing functionality in ruleutils.c to users. Very handy, but it was - * later realized that the functions in ruleutils.c don't check the input - * rigorously, assuming it to come from system catalogs and to therefore - * be valid. That makes it easy for a user to crash the backend by passing - * a maliciously crafted string representation of an expression to - * pg_get_expr(). - * - * There's a lot of code in ruleutils.c, so it's not feasible to add - * water-proof input checking after the fact. Even if we did it once, it - * would need to be taken into account in any future patches too. - * - * Instead, we restrict pg_rule_expr() to only allow input from system - * catalogs instead. This is a hack, but it's the most robust and easiest - * to backpatch way of plugging the vulnerability. - * - * This is transparent to the typical usage pattern of - * "pg_get_expr(systemcolumn, ...)", but will break "pg_get_expr('foo', - * ...)", even if 'foo' is a valid expression fetched earlier from a - * system catalog. Hopefully there's isn't many clients doing that out - * there. - */ - if (result && IsA(result, FuncExpr) &&!superuser()) - { - FuncExpr *fe = (FuncExpr *) result; - - if (fe->funcid == F_PG_GET_EXPR || fe->funcid == F_PG_GET_EXPR_EXT) - { - Expr *arg = linitial(fe->args); - bool allowed = false; - - /* - * Check that the argument came directly from one of the allowed - * system catalog columns - */ - if (IsA(arg, Var)) - { - Var *var = (Var *) arg; - RangeTblEntry *rte; - - rte = GetRTEByRangeTablePosn(pstate, - var->varno, var->varlevelsup); - - switch (rte->relid) - { - case IndexRelationId: - if (var->varattno == Anum_pg_index_indexprs || - var->varattno == Anum_pg_index_indpred) - allowed = true; - break; - - case AttrDefaultRelationId: - if (var->varattno == Anum_pg_attrdef_adbin) - allowed = true; - break; - - case ProcedureRelationId: - if (var->varattno == Anum_pg_proc_proargdefaults) - allowed = true; - break; - - case ConstraintRelationId: - if (var->varattno == Anum_pg_constraint_conbin) - allowed = true; - break; - - case TypeRelationId: - if (var->varattno == Anum_pg_type_typdefaultbin) - allowed = true; - break; - } - } - if (!allowed) - ereport(ERROR, - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), - errmsg("argument to pg_get_expr() must come from system catalogs"))); - } - } - return result; + return ParseFuncOrColumn(pstate, + fn->funcname, + targs, + fn->agg_order, + fn->agg_star, + fn->agg_distinct, + fn->func_variadic, + fn->over, + false, + fn->location); } #ifdef PGXC @@ -1376,9 +1306,18 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) if (exprType(arg) == UNKNOWNOID) arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE"); + /* + * Run collation assignment on the test expression so that we know + * what collation to mark the placeholder with. In principle we could + * leave it to parse_collate.c to do that later, but propagating the + * result to the CaseTestExpr would be unnecessarily complicated. + */ + assign_expr_collations(pstate, arg); + placeholder = makeNode(CaseTestExpr); placeholder->typeId = exprType(arg); placeholder->typeMod = exprTypmod(arg); + placeholder->collation = exprCollation(arg); } else placeholder = NULL; @@ -1443,6 +1382,7 @@ transformCaseExpr(ParseState *pstate, CaseExpr *c) ptype = select_common_type(pstate, resultexprs, "CASE", NULL); Assert(OidIsValid(ptype)); newc->casetype = ptype; + /* casecollid will be set by parse_collate.c */ /* Convert default result clause, if necessary */ newc->defresult = (Expr *) @@ -1573,6 +1513,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink) param->paramid = tent->resno; param->paramtype = exprType((Node *) tent->expr); param->paramtypmod = exprTypmod((Node *) tent->expr); + param->paramcollid = exprCollation((Node *) tent->expr); param->location = -1; right_list = lappend(right_list, param); @@ -1760,6 +1701,7 @@ transformArrayExpr(ParseState *pstate, A_ArrayExpr *a, } newa->array_typeid = array_type; + /* array_collid will be set by parse_collate.c */ newa->element_typeid = element_type; newa->elements = newcoercedelems; newa->location = a->location; @@ -1802,6 +1744,7 @@ transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c) } newc->coalescetype = select_common_type(pstate, newargs, "COALESCE", NULL); + /* coalescecollid will be set by parse_collate.c */ /* Convert arguments if necessary */ foreach(args, newargs) @@ -1840,6 +1783,7 @@ transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m) } newm->minmaxtype = select_common_type(pstate, newargs, funcname, NULL); + /* minmaxcollid and inputcollid will be set by parse_collate.c */ /* Convert arguments if necessary */ foreach(args, newargs) @@ -2001,7 +1945,7 @@ transformXmlSerialize(ParseState *pstate, XmlSerialize *xs) XMLOID, "XMLSERIALIZE")); - targetType = typenameTypeId(pstate, xs->typeName, &targetTypmod); + typenameTypeIdAndMod(pstate, xs->typeName, &targetType, &targetTypmod); xexpr->xmloption = xs->xmloption; xexpr->location = xs->location; @@ -2131,12 +2075,6 @@ transformCurrentOfExpr(ParseState *pstate, CurrentOfExpr *cexpr) /* * Construct a whole-row reference to represent the notation "relation.*". - * - * A whole-row reference is a Var with varno set to the correct range - * table entry, and varattno == 0 to signal that it references the whole - * tuple. (Use of zero here is unclean, since it could easily be confused - * with error cases, but it's not worth changing now.) The vartype indicates - * a rowtype; either a named composite type, or RECORD. */ static Node * transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) @@ -2144,80 +2082,14 @@ transformWholeRowRef(ParseState *pstate, RangeTblEntry *rte, int location) Var *result; int vnum; int sublevels_up; - Oid toid; /* Find the RTE's rangetable location */ - vnum = RTERangeTablePosn(pstate, rte, &sublevels_up); /* Build the appropriate referencing node */ + result = makeWholeRowVar(rte, vnum, sublevels_up); - switch (rte->rtekind) - { - case RTE_RELATION: - /* relation: the rowtype is a named composite type */ - toid = get_rel_type_id(rte->relid); - if (!OidIsValid(toid)) - elog(ERROR, "could not find type OID for relation %u", - rte->relid); - result = makeVar(vnum, - InvalidAttrNumber, - toid, - -1, - sublevels_up); - break; - case RTE_FUNCTION: - toid = exprType(rte->funcexpr); - if (type_is_rowtype(toid)) - { - /* func returns composite; same as relation case */ - result = makeVar(vnum, - InvalidAttrNumber, - toid, - -1, - sublevels_up); - } - else - { - /* - * func returns scalar; instead of making a whole-row Var, - * just reference the function's scalar output. (XXX this - * seems a tad inconsistent, especially if "f.*" was - * explicitly written ...) - */ - result = makeVar(vnum, - 1, - toid, - -1, - sublevels_up); - } - break; - case RTE_VALUES: - toid = RECORDOID; - /* returns composite; same as relation case */ - result = makeVar(vnum, - InvalidAttrNumber, - toid, - -1, - sublevels_up); - break; - default: - - /* - * RTE is a join or subselect. We represent this as a whole-row - * Var of RECORD type. (Note that in most cases the Var will be - * expanded to a RowExpr during planning, but that is not our - * concern here.) - */ - result = makeVar(vnum, - InvalidAttrNumber, - RECORDOID, - -1, - sublevels_up); - break; - } - - /* location is not filled in by makeVar */ + /* location is not filled in by makeWholeRowVar */ result->location = location; /* mark relation as requiring whole-row SELECT access */ @@ -2242,7 +2114,7 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) int32 targetTypmod; int location; - targetType = typenameTypeId(pstate, tc->typeName, &targetTypmod); + typenameTypeIdAndMod(pstate, tc->typeName, &targetType, &targetTypmod); if (inputType == InvalidOid) return expr; /* do nothing if NULL input */ @@ -2273,6 +2145,39 @@ transformTypeCast(ParseState *pstate, TypeCast *tc) } /* + * Handle an explicit COLLATE clause. + * + * Transform the argument, and look up the collation name. + */ +static Node * +transformCollateClause(ParseState *pstate, CollateClause *c) +{ + CollateExpr *newc; + Oid argtype; + + newc = makeNode(CollateExpr); + newc->arg = (Expr *) transformExpr(pstate, c->arg); + + argtype = exprType((Node *) newc->arg); + + /* + * The unknown type is not collatable, but coerce_type() takes care of it + * separately, so we'll let it go here. + */ + if (!type_is_collatable(argtype) && argtype != UNKNOWNOID) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("collations are not supported by type %s", + format_type_be(argtype)), + parser_errposition(pstate, c->location))); + + newc->collOid = LookupCollation(pstate, c->collname, c->location); + newc->location = c->location; + + return (Node *) newc; +} + +/* * Transform a "row compare-op row" construct * * The inputs are lists of already-transformed expressions. @@ -2478,6 +2383,7 @@ make_row_comparison_op(ParseState *pstate, List *opname, rcexpr->rctype = rctype; rcexpr->opnos = opnos; rcexpr->opfamilies = opfamilies; + rcexpr->inputcollids = NIL; /* assign_expr_collations will fix this */ rcexpr->largs = largs; rcexpr->rargs = rargs; |