diff options
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r-- | src/backend/parser/parse_expr.c | 1170 |
1 files changed, 0 insertions, 1170 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c deleted file mode 100644 index f911238ecce..00000000000 --- a/src/backend/parser/parse_expr.c +++ /dev/null @@ -1,1170 +0,0 @@ -/*------------------------------------------------------------------------- - * - * parse_expr.c - * handle expressions in parser - * - * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group - * Portions Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.119 2002/06/20 20:29:32 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#include "postgres.h" - -#include "catalog/pg_operator.h" -#include "catalog/pg_proc.h" -#include "miscadmin.h" -#include "nodes/makefuncs.h" -#include "nodes/params.h" -#include "parser/analyze.h" -#include "parser/gramparse.h" -#include "parser/parse.h" -#include "parser/parse_coerce.h" -#include "parser/parse_expr.h" -#include "parser/parse_func.h" -#include "parser/parse_oper.h" -#include "parser/parse_relation.h" -#include "parser/parse_target.h" -#include "parser/parse_type.h" -#include "utils/builtins.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - - -int max_expr_depth = DEFAULT_MAX_EXPR_DEPTH; -static int expr_depth_counter = 0; - -bool Transform_null_equals = false; - -static Node *parser_typecast_constant(Value *expr, TypeName *typename); -static Node *parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename); -static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref); -static Node *transformIndirection(ParseState *pstate, Node *basenode, - List *indirection); - - -/* - * Initialize for parsing a new query. - * - * We reset the expression depth counter here, in case it was left nonzero - * due to elog()'ing out of the last parsing operation. - */ -void -parse_expr_init(void) -{ - expr_depth_counter = 0; -} - - -/* - * transformExpr - - * Analyze and transform expressions. Type checking and type casting is - * done here. The optimizer and the executor cannot handle the original - * (raw) expressions collected by the parse tree. Hence the transformation - * here. - * - * NOTE: there are various cases in which this routine will get applied to - * an already-transformed expression. Some examples: - * 1. At least one construct (BETWEEN/AND) puts the same nodes - * into two branches of the parse tree; hence, some nodes - * are transformed twice. - * 2. Another way it can happen is that coercion of an operator or - * function argument to the required type (via coerce_type()) - * can apply transformExpr to an already-transformed subexpression. - * An example here is "SELECT count(*) + 1.0 FROM table". - * While it might be possible to eliminate these cases, the path of - * least resistance so far has been to ensure that transformExpr() does - * no damage if applied to an already-transformed tree. This is pretty - * easy for cases where the transformation replaces one node type with - * another, such as A_Const => Const; we just do nothing when handed - * a Const. More care is needed for node types that are used as both - * input and output of transformExpr; see SubLink for example. - */ -Node * -transformExpr(ParseState *pstate, Node *expr) -{ - Node *result = NULL; - - if (expr == NULL) - return NULL; - - /* - * Guard against an overly complex expression leading to coredump due - * to stack overflow here, or in later recursive routines that - * traverse expression trees. Note that this is very unlikely to - * happen except with pathological queries; but we don't want someone - * to be able to crash the backend quite that easily... - */ - if (++expr_depth_counter > max_expr_depth) - elog(ERROR, "Expression too complex: nesting depth exceeds max_expr_depth = %d", - max_expr_depth); - - switch (nodeTag(expr)) - { - case T_ColumnRef: - { - result = transformColumnRef(pstate, (ColumnRef *) expr); - break; - } - case T_ParamRef: - { - ParamRef *pref = (ParamRef *) expr; - int paramno = pref->number; - Oid paramtyp = param_type(paramno); - Param *param; - List *fields; - - if (!OidIsValid(paramtyp)) - elog(ERROR, "Parameter '$%d' is out of range", paramno); - param = makeNode(Param); - param->paramkind = PARAM_NUM; - param->paramid = (AttrNumber) paramno; - param->paramname = "<unnamed>"; - param->paramtype = paramtyp; - result = (Node *) param; - /* handle qualification, if any */ - foreach(fields, pref->fields) - { - result = ParseFuncOrColumn(pstate, - makeList1(lfirst(fields)), - makeList1(result), - false, false, true); - } - /* handle subscripts, if any */ - result = transformIndirection(pstate, result, - pref->indirection); - break; - } - case T_A_Const: - { - A_Const *con = (A_Const *) expr; - Value *val = &con->val; - - if (con->typename != NULL) - result = parser_typecast_constant(val, con->typename); - else - result = (Node *) make_const(val); - break; - } - case T_ExprFieldSelect: - { - ExprFieldSelect *efs = (ExprFieldSelect *) expr; - List *fields; - - result = transformExpr(pstate, efs->arg); - /* handle qualification, if any */ - foreach(fields, efs->fields) - { - result = ParseFuncOrColumn(pstate, - makeList1(lfirst(fields)), - makeList1(result), - false, false, true); - } - /* handle subscripts, if any */ - result = transformIndirection(pstate, result, - efs->indirection); - break; - } - case T_TypeCast: - { - TypeCast *tc = (TypeCast *) expr; - Node *arg = transformExpr(pstate, tc->arg); - - result = parser_typecast_expression(pstate, arg, tc->typename); - break; - } - case T_A_Expr: - { - A_Expr *a = (A_Expr *) expr; - - switch (a->oper) - { - case OP: - { - /* - * Special-case "foo = NULL" and "NULL = foo" - * for compatibility with standards-broken - * products (like Microsoft's). Turn these - * into IS NULL exprs. - */ - if (Transform_null_equals && - length(a->name) == 1 && - strcmp(strVal(lfirst(a->name)), "=") == 0 && - (exprIsNullConstant(a->lexpr) || - exprIsNullConstant(a->rexpr))) - { - NullTest *n = makeNode(NullTest); - - n->nulltesttype = IS_NULL; - - if (exprIsNullConstant(a->lexpr)) - n->arg = a->rexpr; - else - n->arg = a->lexpr; - - result = transformExpr(pstate, - (Node *) n); - } - else - { - Node *lexpr = transformExpr(pstate, - a->lexpr); - Node *rexpr = transformExpr(pstate, - a->rexpr); - - result = (Node *) make_op(a->name, - lexpr, - rexpr); - } - } - break; - case AND: - { - Node *lexpr = transformExpr(pstate, - a->lexpr); - Node *rexpr = transformExpr(pstate, - a->rexpr); - Expr *expr = makeNode(Expr); - - lexpr = coerce_to_boolean(lexpr, "AND"); - rexpr = coerce_to_boolean(rexpr, "AND"); - - expr->typeOid = BOOLOID; - expr->opType = AND_EXPR; - expr->args = makeList2(lexpr, rexpr); - result = (Node *) expr; - } - break; - case OR: - { - Node *lexpr = transformExpr(pstate, - a->lexpr); - Node *rexpr = transformExpr(pstate, - a->rexpr); - Expr *expr = makeNode(Expr); - - lexpr = coerce_to_boolean(lexpr, "OR"); - rexpr = coerce_to_boolean(rexpr, "OR"); - - expr->typeOid = BOOLOID; - expr->opType = OR_EXPR; - expr->args = makeList2(lexpr, rexpr); - result = (Node *) expr; - } - break; - case NOT: - { - Node *rexpr = transformExpr(pstate, - a->rexpr); - Expr *expr = makeNode(Expr); - - rexpr = coerce_to_boolean(rexpr, "NOT"); - - expr->typeOid = BOOLOID; - expr->opType = NOT_EXPR; - expr->args = makeList1(rexpr); - result = (Node *) expr; - } - break; - } - break; - } - case T_FuncCall: - { - FuncCall *fn = (FuncCall *) expr; - List *args; - - /* transform the list of arguments */ - foreach(args, fn->args) - lfirst(args) = transformExpr(pstate, - (Node *) lfirst(args)); - result = ParseFuncOrColumn(pstate, - fn->funcname, - fn->args, - fn->agg_star, - fn->agg_distinct, - false); - break; - } - case T_SubLink: - { - SubLink *sublink = (SubLink *) expr; - List *qtrees; - Query *qtree; - - /* If we already transformed this node, do nothing */ - if (IsA(sublink->subselect, Query)) - { - result = expr; - break; - } - pstate->p_hasSubLinks = true; - qtrees = parse_analyze(sublink->subselect, pstate); - if (length(qtrees) != 1) - elog(ERROR, "Bad query in subselect"); - qtree = (Query *) lfirst(qtrees); - if (qtree->commandType != CMD_SELECT || - qtree->resultRelation != 0) - elog(ERROR, "Bad query in subselect"); - sublink->subselect = (Node *) qtree; - - if (sublink->subLinkType == EXISTS_SUBLINK) - { - /* - * EXISTS needs no lefthand or combining operator. - * These fields should be NIL already, but make sure. - */ - sublink->lefthand = NIL; - sublink->oper = NIL; - } - else if (sublink->subLinkType == EXPR_SUBLINK) - { - List *tlist = qtree->targetList; - - /* - * Make sure the subselect delivers a single column - * (ignoring resjunk targets). - */ - if (tlist == NIL || - ((TargetEntry *) lfirst(tlist))->resdom->resjunk) - elog(ERROR, "Subselect must have a field"); - while ((tlist = lnext(tlist)) != NIL) - { - if (!((TargetEntry *) lfirst(tlist))->resdom->resjunk) - elog(ERROR, "Subselect must have only one field"); - } - - /* - * EXPR needs no lefthand or combining operator. These - * fields should be NIL already, but make sure. - */ - sublink->lefthand = NIL; - sublink->oper = NIL; - } - else - { - /* ALL, ANY, or MULTIEXPR: generate operator list */ - List *left_list = sublink->lefthand; - List *right_list = qtree->targetList; - List *op; - char *opname; - List *elist; - - foreach(elist, left_list) - lfirst(elist) = transformExpr(pstate, lfirst(elist)); - - Assert(IsA(sublink->oper, A_Expr)); - op = ((A_Expr *) sublink->oper)->name; - opname = strVal(llast(op)); - sublink->oper = NIL; - - /* Combining operators other than =/<> is dubious... */ - if (length(left_list) != 1 && - strcmp(opname, "=") != 0 && strcmp(opname, "<>") != 0) - elog(ERROR, "Row comparison cannot use operator %s", - opname); - - /* - * Scan subquery's targetlist to find values that will - * be matched against lefthand values. We need to - * ignore resjunk targets, so doing the outer - * iteration over right_list is easier than doing it - * over left_list. - */ - while (right_list != NIL) - { - TargetEntry *tent = (TargetEntry *) lfirst(right_list); - Node *lexpr; - Operator optup; - Form_pg_operator opform; - Oper *newop; - - right_list = lnext(right_list); - if (tent->resdom->resjunk) - continue; - - if (left_list == NIL) - elog(ERROR, "Subselect has too many fields"); - lexpr = lfirst(left_list); - left_list = lnext(left_list); - - /* - * It's OK to use oper() not compatible_oper() - * here, because make_subplan() will insert type - * coercion calls if needed. - */ - optup = oper(op, - exprType(lexpr), - exprType(tent->expr), - false); - opform = (Form_pg_operator) GETSTRUCT(optup); - - if (opform->oprresult != BOOLOID) - elog(ERROR, "%s has result type of %s, but must return %s" - " to be used with quantified predicate subquery", - opname, format_type_be(opform->oprresult), - format_type_be(BOOLOID)); - - if (get_func_retset(opform->oprcode)) - elog(ERROR, "%s must not return a set" - " to be used with quantified predicate subquery", - opname); - - newop = makeOper(oprid(optup), /* opno */ - InvalidOid, /* opid */ - opform->oprresult, - false); - sublink->oper = lappend(sublink->oper, newop); - ReleaseSysCache(optup); - } - if (left_list != NIL) - elog(ERROR, "Subselect has too few fields"); - } - result = (Node *) expr; - break; - } - - case T_CaseExpr: - { - CaseExpr *c = (CaseExpr *) expr; - CaseExpr *newc = makeNode(CaseExpr); - List *newargs = NIL; - List *typeids = NIL; - List *args; - Node *defresult; - Oid ptype; - - /* transform the list of arguments */ - foreach(args, c->args) - { - CaseWhen *w = (CaseWhen *) lfirst(args); - CaseWhen *neww = makeNode(CaseWhen); - Node *warg; - - Assert(IsA(w, CaseWhen)); - - warg = w->expr; - if (c->arg != NULL) - { - /* shorthand form was specified, so expand... */ - warg = (Node *) makeSimpleA_Expr(OP, "=", - c->arg, warg); - } - neww->expr = transformExpr(pstate, warg); - - neww->expr = coerce_to_boolean(neww->expr, "CASE/WHEN"); - - /* - * result is NULL for NULLIF() construct - thomas - * 1998-11-11 - */ - warg = w->result; - if (warg == NULL) - { - A_Const *n = makeNode(A_Const); - - n->val.type = T_Null; - warg = (Node *) n; - } - neww->result = transformExpr(pstate, warg); - - newargs = lappend(newargs, neww); - typeids = lappendi(typeids, exprType(neww->result)); - } - - newc->args = newargs; - - /* - * It's not shorthand anymore, so drop the implicit - * argument. This is necessary to keep any re-application - * of transformExpr from doing the wrong thing. - */ - newc->arg = NULL; - - /* transform the default clause */ - defresult = c->defresult; - if (defresult == NULL) - { - A_Const *n = makeNode(A_Const); - - n->val.type = T_Null; - defresult = (Node *) n; - } - newc->defresult = transformExpr(pstate, defresult); - - /* - * Note: default result is considered the most significant - * type in determining preferred type. This is how the - * code worked before, but it seems a little bogus to me - * --- tgl - */ - typeids = lconsi(exprType(newc->defresult), typeids); - - ptype = select_common_type(typeids, "CASE"); - newc->casetype = ptype; - - /* Convert default result clause, if necessary */ - newc->defresult = coerce_to_common_type(pstate, - newc->defresult, - ptype, - "CASE/ELSE"); - - /* Convert when-clause results, if necessary */ - foreach(args, newc->args) - { - CaseWhen *w = (CaseWhen *) lfirst(args); - - w->result = coerce_to_common_type(pstate, - w->result, - ptype, - "CASE/WHEN"); - } - - result = (Node *) newc; - break; - } - - case T_NullTest: - { - NullTest *n = (NullTest *) expr; - - n->arg = transformExpr(pstate, n->arg); - /* the argument can be any type, so don't coerce it */ - result = expr; - break; - } - - case T_BooleanTest: - { - BooleanTest *b = (BooleanTest *) expr; - const char *clausename; - - switch (b->booltesttype) - { - case IS_TRUE: - clausename = "IS TRUE"; - break; - case IS_NOT_TRUE: - clausename = "IS NOT TRUE"; - break; - case IS_FALSE: - clausename = "IS FALSE"; - break; - case IS_NOT_FALSE: - clausename = "IS NOT FALSE"; - break; - case IS_UNKNOWN: - clausename = "IS UNKNOWN"; - break; - case IS_NOT_UNKNOWN: - clausename = "IS NOT UNKNOWN"; - break; - default: - elog(ERROR, "transformExpr: unexpected booltesttype %d", - (int) b->booltesttype); - clausename = NULL; /* keep compiler quiet */ - } - - b->arg = transformExpr(pstate, b->arg); - - b->arg = coerce_to_boolean(b->arg, clausename); - - result = expr; - break; - } - - /* - * Quietly accept node types that may be presented when we are - * called on an already-transformed tree. - * - * Do any other node types need to be accepted? For now we are - * taking a conservative approach, and only accepting node - * types that are demonstrably necessary to accept. - */ - case T_Expr: - case T_Var: - case T_Const: - case T_Param: - case T_Aggref: - case T_ArrayRef: - case T_FieldSelect: - case T_RelabelType: - { - result = (Node *) expr; - break; - } - - default: - /* should not reach here */ - elog(ERROR, "transformExpr: does not know how to transform node %d" - " (internal error)", nodeTag(expr)); - break; - } - - expr_depth_counter--; - - return result; -} - -static Node * -transformIndirection(ParseState *pstate, Node *basenode, List *indirection) -{ - if (indirection == NIL) - return basenode; - return (Node *) transformArraySubscripts(pstate, - basenode, exprType(basenode), - indirection, false, NULL); -} - -static Node * -transformColumnRef(ParseState *pstate, ColumnRef *cref) -{ - int numnames = length(cref->fields); - Node *node; - RangeVar *rv; - - /*---------- - * The allowed syntaxes are: - * - * A First try to resolve as unqualified column name; - * if no luck, try to resolve as unqual. table name (A.*). - * A.B A is an unqual. table name; B is either a - * column or function name (trying column name first). - * A.B.C schema A, table B, col or func name C. - * A.B.C.D catalog A, schema B, table C, col or func D. - * A.* A is an unqual. table name; means whole-row value. - * A.B.* whole-row value of table B in schema A. - * A.B.C.* whole-row value of table C in schema B in catalog A. - * - * We do not need to cope with bare "*"; that will only be accepted by - * the grammar at the top level of a SELECT list, and transformTargetList - * will take care of it before it ever gets here. - * - * Currently, if a catalog name is given then it must equal the current - * database name; we check it here and then discard it. - * - * For whole-row references, the result is an untransformed RangeVar, - * which will work as the argument to a function call, but not in any - * other context at present. (We could instead coerce to a whole-row Var, - * but that will fail for subselect and join RTEs, because there is no - * pg_type entry for their rowtypes.) - *---------- - */ - switch (numnames) - { - case 1: - { - char *name = strVal(lfirst(cref->fields)); - - /* Try to identify as an unqualified column */ - node = colnameToVar(pstate, name); - if (node == NULL) - { - /* - * Not known as a column of any range-table entry, so - * try to find the name as a relation ... but not if - * subscripts appear. Note also that only relations - * already entered into the rangetable will be recognized. - */ - int levels_up; - - if (cref->indirection == NIL && - refnameRangeTblEntry(pstate, name, &levels_up) != NULL) - { - rv = makeNode(RangeVar); - rv->relname = name; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; - } - else - elog(ERROR, "Attribute \"%s\" not found", name); - } - break; - } - case 2: - { - char *name1 = strVal(lfirst(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); - - /* Whole-row reference? */ - if (strcmp(name2, "*") == 0) - { - rv = makeNode(RangeVar); - rv->relname = name1; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; - break; - } - - /* Try to identify as a once-qualified column */ - node = qualifiedNameToVar(pstate, name1, name2, true); - if (node == NULL) - { - /* - * Not known as a column of any range-table entry, so - * try it as a function call. Here, we will create an - * implicit RTE for tables not already entered. - */ - rv = makeNode(RangeVar); - rv->relname = name1; - rv->inhOpt = INH_DEFAULT; - node = ParseFuncOrColumn(pstate, - makeList1(makeString(name2)), - makeList1(rv), - false, false, true); - } - break; - } - case 3: - { - char *name1 = strVal(lfirst(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); - char *name3 = strVal(lfirst(lnext(lnext(cref->fields)))); - - /* Whole-row reference? */ - if (strcmp(name3, "*") == 0) - { - rv = makeNode(RangeVar); - rv->schemaname = name1; - rv->relname = name2; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; - break; - } - - /* Try to identify as a twice-qualified column */ - /* XXX do something with schema name here */ - node = qualifiedNameToVar(pstate, name2, name3, true); - if (node == NULL) - { - /* Try it as a function call */ - rv = makeNode(RangeVar); - rv->schemaname = name1; - rv->relname = name2; - rv->inhOpt = INH_DEFAULT; - node = ParseFuncOrColumn(pstate, - makeList1(makeString(name3)), - makeList1(rv), - false, false, true); - } - break; - } - case 4: - { - char *name1 = strVal(lfirst(cref->fields)); - char *name2 = strVal(lsecond(cref->fields)); - char *name3 = strVal(lfirst(lnext(lnext(cref->fields)))); - char *name4 = strVal(lfirst(lnext(lnext(lnext(cref->fields))))); - - /* - * We check the catalog name and then ignore it. - */ - if (strcmp(name1, DatabaseName) != 0) - elog(ERROR, "Cross-database references are not implemented"); - - /* Whole-row reference? */ - if (strcmp(name4, "*") == 0) - { - rv = makeNode(RangeVar); - rv->schemaname = name2; - rv->relname = name3; - rv->inhOpt = INH_DEFAULT; - node = (Node *) rv; - break; - } - - /* Try to identify as a twice-qualified column */ - /* XXX do something with schema name here */ - node = qualifiedNameToVar(pstate, name3, name4, true); - if (node == NULL) - { - /* Try it as a function call */ - rv = makeNode(RangeVar); - rv->schemaname = name2; - rv->relname = name3; - rv->inhOpt = INH_DEFAULT; - node = ParseFuncOrColumn(pstate, - makeList1(makeString(name4)), - makeList1(rv), - false, false, true); - } - break; - } - default: - elog(ERROR, "Invalid qualified name syntax (too many names)"); - node = NULL; /* keep compiler quiet */ - break; - } - - return transformIndirection(pstate, node, cref->indirection); -} - -/* - * exprType - - * returns the Oid of the type of the expression. (Used for typechecking.) - */ -Oid -exprType(Node *expr) -{ - Oid type = (Oid) InvalidOid; - - if (!expr) - return type; - - switch (nodeTag(expr)) - { - case T_Var: - type = ((Var *) expr)->vartype; - break; - case T_Expr: - type = ((Expr *) expr)->typeOid; - break; - case T_Const: - type = ((Const *) expr)->consttype; - break; - case T_ArrayRef: - type = ((ArrayRef *) expr)->refelemtype; - break; - case T_Aggref: - type = ((Aggref *) expr)->aggtype; - break; - case T_Param: - type = ((Param *) expr)->paramtype; - break; - case T_FieldSelect: - type = ((FieldSelect *) expr)->resulttype; - break; - case T_RelabelType: - type = ((RelabelType *) expr)->resulttype; - break; - case T_SubLink: - { - SubLink *sublink = (SubLink *) expr; - - if (sublink->subLinkType == EXPR_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 *) lfirst(qtree->targetList); - type = tent->resdom->restype; - } - else - { - /* for all other sublink types, result is boolean */ - type = BOOLOID; - } - } - break; - case T_CaseExpr: - type = ((CaseExpr *) expr)->casetype; - break; - case T_CaseWhen: - type = exprType(((CaseWhen *) expr)->result); - break; - case T_NullTest: - type = BOOLOID; - break; - case T_BooleanTest: - type = BOOLOID; - break; - default: - elog(ERROR, "Do not know how to get type for %d node", - nodeTag(expr)); - 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: - { - /* Be smart about string constants... */ - Const *con = (Const *) expr; - - switch (con->consttype) - { - case BPCHAROID: - if (!con->constisnull) - return VARSIZE(DatumGetPointer(con->constvalue)); - break; - default: - break; - } - } - break; - case T_Expr: - { - int32 coercedTypmod; - - /* Be smart about length-coercion functions... */ - if (exprIsLengthCoercion(expr, &coercedTypmod)) - return coercedTypmod; - } - break; - case T_FieldSelect: - return ((FieldSelect *) expr)->resulttypmod; - break; - case T_RelabelType: - return ((RelabelType *) expr)->resulttypmod; - break; - 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; - List *arg; - - if (!cexpr->defresult) - return -1; - if (exprType(cexpr->defresult) != casetype) - return -1; - typmod = exprTypmod(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(w->result) != casetype) - return -1; - if (exprTypmod(w->result) != typmod) - return -1; - } - return typmod; - } - break; - 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. - * - * We assume that a two-argument function named for a datatype, whose - * output and first argument types are that datatype, and whose second - * input is an int32 constant, represents a forced length coercion. - * - * XXX It'd be better if the parsetree retained some explicit indication - * of the coercion, so we didn't need these heuristics. - */ -bool -exprIsLengthCoercion(Node *expr, int32 *coercedTypmod) -{ - Func *func; - Const *second_arg; - HeapTuple procTuple; - HeapTuple typeTuple; - Form_pg_proc procStruct; - Form_pg_type typeStruct; - - if (coercedTypmod != NULL) - *coercedTypmod = -1; /* default result on failure */ - - /* Is it a function-call at all? */ - if (expr == NULL || - !IsA(expr, Expr) || - ((Expr *) expr)->opType != FUNC_EXPR) - return false; - func = (Func *) (((Expr *) expr)->oper); - Assert(IsA(func, Func)); - - /* - * If it's not a two-argument function with the second argument being - * an int4 constant, it can't have been created from a length - * coercion. - */ - if (length(((Expr *) expr)->args) != 2) - return false; - second_arg = (Const *) lsecond(((Expr *) expr)->args); - if (!IsA(second_arg, Const) || - second_arg->consttype != INT4OID || - second_arg->constisnull) - return false; - - /* - * Lookup the function in pg_proc - */ - procTuple = SearchSysCache(PROCOID, - ObjectIdGetDatum(func->funcid), - 0, 0, 0); - if (!HeapTupleIsValid(procTuple)) - elog(ERROR, "cache lookup for proc %u failed", func->funcid); - procStruct = (Form_pg_proc) GETSTRUCT(procTuple); - - /* - * It must be a function with two arguments where the first is of the - * same type as the return value and the second is an int4. Also, just - * to be sure, check return type agrees with expr node. - */ - if (procStruct->pronargs != 2 || - procStruct->prorettype != procStruct->proargtypes[0] || - procStruct->proargtypes[1] != INT4OID || - procStruct->prorettype != ((Expr *) expr)->typeOid) - { - ReleaseSysCache(procTuple); - return false; - } - - /* - * Furthermore, the name and namespace of the function must be the same - * as its result type's name/namespace (cf. find_coercion_function). - */ - typeTuple = SearchSysCache(TYPEOID, - ObjectIdGetDatum(procStruct->prorettype), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "cache lookup for type %u failed", - procStruct->prorettype); - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - if (strcmp(NameStr(procStruct->proname), - NameStr(typeStruct->typname)) != 0 || - procStruct->pronamespace != typeStruct->typnamespace) - { - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); - return false; - } - - /* - * OK, it is indeed a length-coercion function. - */ - if (coercedTypmod != NULL) - *coercedTypmod = DatumGetInt32(second_arg->constvalue); - - ReleaseSysCache(procTuple); - ReleaseSysCache(typeTuple); - return true; -} - -/* - * Produce an appropriate Const node from a constant value produced - * by the parser and an explicit type name to cast to. - */ -static Node * -parser_typecast_constant(Value *expr, TypeName *typename) -{ - Type tp; - Datum datum; - Const *con; - char *const_string = NULL; - bool string_palloced = false; - bool isNull = false; - - tp = typenameType(typename); - - switch (nodeTag(expr)) - { - case T_Integer: - const_string = DatumGetCString(DirectFunctionCall1(int4out, - Int32GetDatum(expr->val.ival))); - string_palloced = true; - break; - case T_Float: - case T_String: - case T_BitString: - const_string = expr->val.str; - break; - case T_Null: - isNull = true; - break; - default: - elog(ERROR, "Cannot cast this expression to type '%s'", - typeTypeName(tp)); - } - - if (isNull) - datum = (Datum) NULL; - else - datum = stringTypeDatum(tp, const_string, typename->typmod); - - con = makeConst(typeTypeId(tp), - typeLen(tp), - datum, - isNull, - typeByVal(tp), - false, /* not a set */ - true /* is cast */ ); - - if (string_palloced) - pfree(const_string); - - ReleaseSysCache(tp); - - return (Node *) con; -} - -/* - * Handle an explicit CAST applied to a non-constant expression. - * (Actually, this works for constants too, but gram.y won't generate - * a TypeCast node if the argument is just a constant.) - * - * The given expr has already been transformed, but we need to lookup - * the type name and then apply any necessary coercion function(s). - */ -static Node * -parser_typecast_expression(ParseState *pstate, - Node *expr, TypeName *typename) -{ - Oid inputType = exprType(expr); - Oid targetType; - - targetType = typenameTypeId(typename); - - if (inputType == InvalidOid) - return expr; /* do nothing if NULL input */ - - if (inputType != targetType) - { - expr = CoerceTargetExpr(pstate, expr, inputType, - targetType, typename->typmod, - true); /* explicit coercion */ - if (expr == NULL) - elog(ERROR, "Cannot cast type '%s' to '%s'", - format_type_be(inputType), - format_type_be(targetType)); - } - - /* - * If the target is a fixed-length type, it may need a length coercion - * as well as a type coercion. - */ - expr = coerce_type_typmod(pstate, expr, - targetType, typename->typmod); - - return expr; -} |