* parse_expr.c
* handle expressions in parser
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2007, 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.138 2002/12/27 20:06:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.217 2007/04/27 22:05:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
-#include "catalog/pg_operator.h"
-#include "catalog/pg_proc.h"
+#include "catalog/pg_type.h"
+#include "commands/dbcommands.h"
+#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
-#include "nodes/params.h"
+#include "nodes/plannodes.h"
+#include "optimizer/clauses.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"
+#include "utils/xml.h"
-int max_expr_depth = DEFAULT_MAX_EXPR_DEPTH;
-static int expr_depth_counter = 0;
-
bool Transform_null_equals = false;
-static Node *typecast_expression(Node *expr, TypeName *typename);
+static Node *transformParamRef(ParseState *pstate, ParamRef *pref);
+static Node *transformAExprOp(ParseState *pstate, A_Expr *a);
+static Node *transformAExprAnd(ParseState *pstate, A_Expr *a);
+static Node *transformAExprOr(ParseState *pstate, A_Expr *a);
+static Node *transformAExprNot(ParseState *pstate, A_Expr *a);
+static Node *transformAExprOpAny(ParseState *pstate, A_Expr *a);
+static Node *transformAExprOpAll(ParseState *pstate, A_Expr *a);
+static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
+static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
+static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
+static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
+static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
+static Node *transformCaseExpr(ParseState *pstate, CaseExpr *c);
+static Node *transformSubLink(ParseState *pstate, SubLink *sublink);
+static Node *transformArrayExpr(ParseState *pstate, ArrayExpr *a);
+static Node *transformRowExpr(ParseState *pstate, RowExpr *r);
+static Node *transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c);
+static Node *transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m);
+static Node *transformXmlExpr(ParseState *pstate, XmlExpr *x);
+static Node *transformXmlSerialize(ParseState *pstate, XmlSerialize *xs);
+static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
static Node *transformColumnRef(ParseState *pstate, ColumnRef *cref);
+static Node *transformWholeRowRef(ParseState *pstate, char *schemaname,
+ char *relname, int location);
+static Node *transformBooleanTest(ParseState *pstate, BooleanTest *b);
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;
-}
+static Node *typecast_expression(ParseState *pstate, Node *expr,
+ TypeName *typename);
+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,
+ RowExpr *lrow, RowExpr *rrow, int location);
+static Expr *make_distinct_op(ParseState *pstate, List *opname,
+ Node *ltree, Node *rtree, int location);
/*
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);
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
switch (nodeTag(expr))
{
case T_ColumnRef:
- {
- result = transformColumnRef(pstate, (ColumnRef *) expr);
- break;
- }
+ 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->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;
- }
+ result = transformParamRef(pstate, (ParamRef *) expr);
+ break;
+
case T_A_Const:
{
A_Const *con = (A_Const *) expr;
result = (Node *) make_const(val);
if (con->typename != NULL)
- result = typecast_expression(result, con->typename);
+ result = typecast_expression(pstate, result,
+ con->typename);
break;
}
- case T_ExprFieldSelect:
+
+ case T_A_Indirection:
{
- ExprFieldSelect *efs = (ExprFieldSelect *) expr;
- List *fields;
+ A_Indirection *ind = (A_Indirection *) expr;
- 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 = transformExpr(pstate, ind->arg);
result = transformIndirection(pstate, result,
- efs->indirection);
+ ind->indirection);
break;
}
+
case T_TypeCast:
{
TypeCast *tc = (TypeCast *) expr;
Node *arg = transformExpr(pstate, tc->arg);
- result = typecast_expression(arg, tc->typename);
+ result = typecast_expression(pstate, arg, tc->typename);
break;
}
+
case T_A_Expr:
{
A_Expr *a = (A_Expr *) expr;
- switch (a->oper)
+ switch (a->kind)
{
- 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 = (Expr *) a->rexpr;
- else
- n->arg = (Expr *) 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);
- }
- }
+ case AEXPR_OP:
+ result = transformAExprOp(pstate, a);
break;
- case AND:
- {
- Node *lexpr = transformExpr(pstate,
- a->lexpr);
- Node *rexpr = transformExpr(pstate,
- a->rexpr);
-
- lexpr = coerce_to_boolean(lexpr, "AND");
- rexpr = coerce_to_boolean(rexpr, "AND");
-
- result = (Node *) makeBoolExpr(AND_EXPR,
- makeList2(lexpr,
- rexpr));
- }
+ case AEXPR_AND:
+ result = transformAExprAnd(pstate, a);
break;
- case OR:
- {
- Node *lexpr = transformExpr(pstate,
- a->lexpr);
- Node *rexpr = transformExpr(pstate,
- a->rexpr);
-
- lexpr = coerce_to_boolean(lexpr, "OR");
- rexpr = coerce_to_boolean(rexpr, "OR");
-
- result = (Node *) makeBoolExpr(OR_EXPR,
- makeList2(lexpr,
- rexpr));
- }
+ case AEXPR_OR:
+ result = transformAExprOr(pstate, a);
break;
- case NOT:
- {
- Node *rexpr = transformExpr(pstate,
- a->rexpr);
-
- rexpr = coerce_to_boolean(rexpr, "NOT");
-
- result = (Node *) makeBoolExpr(NOT_EXPR,
- makeList1(rexpr));
- }
+ case AEXPR_NOT:
+ result = transformAExprNot(pstate, a);
+ break;
+ case AEXPR_OP_ANY:
+ result = transformAExprOpAny(pstate, a);
+ break;
+ case AEXPR_OP_ALL:
+ result = transformAExprOpAll(pstate, a);
break;
- case DISTINCT:
- {
- Node *lexpr = transformExpr(pstate,
- a->lexpr);
- Node *rexpr = transformExpr(pstate,
- a->rexpr);
-
- result = (Node *) make_op(a->name,
- lexpr,
- rexpr);
- if (((OpExpr *) result)->opresulttype != BOOLOID)
- elog(ERROR, "IS DISTINCT FROM requires = operator to yield boolean");
- /*
- * We rely on DistinctExpr and OpExpr being same struct
- */
- NodeSetTag(result, T_DistinctExpr);
- }
+ case AEXPR_DISTINCT:
+ result = transformAExprDistinct(pstate, a);
break;
- case OF:
- {
- List *telem;
- A_Const *n;
- Oid ltype,
- rtype;
- bool matched = FALSE;
-
- /*
- * Checking an expression for match to type.
- * Will result in a boolean constant node.
- */
- Node *lexpr = transformExpr(pstate,
- a->lexpr);
-
- ltype = exprType(lexpr);
- foreach(telem, (List *) a->rexpr)
- {
- rtype = LookupTypeName(lfirst(telem));
- matched = (rtype == ltype);
- if (matched)
- break;
- }
-
- /*
- * Expect two forms: equals or not equals.
- * Flip the sense of the result for not
- * equals.
- */
- if (strcmp(strVal(lfirst(a->name)), "!=") == 0)
- matched = (!matched);
-
- n = makeNode(A_Const);
- n->val.type = T_String;
- n->val.val.str = (matched ? "t" : "f");
- n->typename = SystemTypeName("bool");
-
- result = transformExpr(pstate, (Node *) n);
- }
+ case AEXPR_NULLIF:
+ result = transformAExprNullIf(pstate, a);
break;
+ case AEXPR_OF:
+ result = transformAExprOf(pstate, a);
+ break;
+ case AEXPR_IN:
+ result = transformAExprIn(pstate, a);
+ break;
+ default:
+ elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
}
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);
+ case T_FuncCall:
+ result = transformFuncCall(pstate, (FuncCall *) expr);
+ break;
- /*
- * 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;
- OpExpr *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((Node *) 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 = makeNode(OpExpr);
- newop->opno = oprid(optup);
- newop->opfuncid = InvalidOid;
- newop->opresulttype = opform->oprresult;
- newop->opretset = false;
- newop->args = NIL; /* for now */
-
- 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_SubLink:
+ result = transformSubLink(pstate, (SubLink *) 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 = (Node *) w->expr;
- if (c->arg != NULL)
- {
- /* shorthand form was specified, so expand... */
- warg = (Node *) makeSimpleA_Expr(OP, "=",
- (Node *) c->arg,
- warg);
- }
- neww->expr = (Expr *) transformExpr(pstate, warg);
-
- neww->expr = (Expr *) coerce_to_boolean((Node *) neww->expr,
- "CASE/WHEN");
-
- /*
- * result is NULL for NULLIF() construct - thomas
- * 1998-11-11
- */
- warg = (Node *) w->result;
- if (warg == NULL)
- {
- A_Const *n = makeNode(A_Const);
-
- n->val.type = T_Null;
- warg = (Node *) n;
- }
- neww->result = (Expr *) transformExpr(pstate, warg);
-
- newargs = lappend(newargs, neww);
- typeids = lappendi(typeids, exprType((Node *) 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 = (Node *) c->defresult;
- if (defresult == NULL)
- {
- A_Const *n = makeNode(A_Const);
-
- n->val.type = T_Null;
- defresult = (Node *) n;
- }
- newc->defresult = (Expr *) transformExpr(pstate, defresult);
+ result = transformCaseExpr(pstate, (CaseExpr *) expr);
+ break;
- /*
- * 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((Node *) newc->defresult), typeids);
+ case T_ArrayExpr:
+ result = transformArrayExpr(pstate, (ArrayExpr *) expr);
+ break;
- ptype = select_common_type(typeids, "CASE");
- newc->casetype = ptype;
+ case T_RowExpr:
+ result = transformRowExpr(pstate, (RowExpr *) expr);
+ break;
- /* Convert default result clause, if necessary */
- newc->defresult = (Expr *)
- coerce_to_common_type((Node *) newc->defresult,
- ptype,
- "CASE/ELSE");
+ case T_CoalesceExpr:
+ result = transformCoalesceExpr(pstate, (CoalesceExpr *) expr);
+ break;
- /* Convert when-clause results, if necessary */
- foreach(args, newc->args)
- {
- CaseWhen *w = (CaseWhen *) lfirst(args);
+ case T_MinMaxExpr:
+ result = transformMinMaxExpr(pstate, (MinMaxExpr *) expr);
+ break;
- w->result = (Expr *)
- coerce_to_common_type((Node *) w->result,
- ptype,
- "CASE/WHEN");
- }
+ case T_XmlExpr:
+ result = transformXmlExpr(pstate, (XmlExpr *) expr);
+ break;
- result = (Node *) newc;
- break;
- }
+ case T_XmlSerialize:
+ result = transformXmlSerialize(pstate, (XmlSerialize *) expr);
+ break;
case T_NullTest:
{
}
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 = (Expr *) transformExpr(pstate, (Node *) b->arg);
-
- b->arg = (Expr *) coerce_to_boolean((Node *) b->arg, clausename);
-
- result = expr;
- break;
- }
+ result = transformBooleanTest(pstate, (BooleanTest *) expr);
+ break;
/*********************************************
* Quietly accept node types that may be presented when 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_FuncExpr:
+ case T_OpExpr:
+ case T_DistinctExpr:
+ case T_ScalarArrayOpExpr:
+ case T_NullIfExpr:
+ case T_BoolExpr:
case T_FieldSelect:
+ case T_FieldStore:
case T_RelabelType:
- case T_ConstraintTest:
- case T_ConstraintTestValue:
+ case T_ArrayCoerceExpr:
+ case T_ConvertRowtypeExpr:
+ case T_CaseTestExpr:
+ case T_CoerceToDomain:
+ case T_CoerceToDomainValue:
+ case T_SetToDefault:
{
result = (Node *) expr;
break;
default:
/* should not reach here */
- elog(ERROR, "transformExpr: does not know how to transform node %d"
- " (internal error)", nodeTag(expr));
+ elog(ERROR, "unrecognized node type: %d", (int) 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),
- exprTypmod(basenode),
- indirection,
- false,
- NULL);
+ Node *result = basenode;
+ List *subscripts = NIL;
+ ListCell *i;
+
+ /*
+ * We have to split any field-selection operations apart from
+ * subscripting. Adjacent A_Indices nodes have to be treated as a single
+ * multidimensional subscript operation.
+ */
+ foreach(i, indirection)
+ {
+ Node *n = lfirst(i);
+
+ if (IsA(n, A_Indices))
+ subscripts = lappend(subscripts, n);
+ else
+ {
+ Assert(IsA(n, String));
+
+ /* process subscripts before this field selection */
+ if (subscripts)
+ result = (Node *) transformArraySubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
+ subscripts = NIL;
+
+ result = ParseFuncOrColumn(pstate,
+ list_make1(n),
+ list_make1(result),
+ false, false, true,
+ -1);
+ }
+ }
+ /* process trailing subscripts, if any */
+ if (subscripts)
+ result = (Node *) transformArraySubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
+
+ return result;
}
static Node *
transformColumnRef(ParseState *pstate, ColumnRef *cref)
{
- int numnames = length(cref->fields);
+ int numnames = list_length(cref->fields);
Node *node;
- RangeVar *rv;
int levels_up;
/*----------
* 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
+ * if no luck, try to resolve as unqualified table name (A.*).
+ * A.B A is an unqualified 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.* A is an unqualified 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.
+ * will take care of it before it ever gets here. Also, "A.*" etc will
+ * be expanded by transformTargetList if they appear at SELECT top level,
+ * so here we are only going to see them as function or operator inputs.
*
* 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));
+ char *name = strVal(linitial(cref->fields));
/* Try to identify as an unqualified column */
- node = colnameToVar(pstate, name);
+ node = colNameToVar(pstate, name, false, cref->location);
if (node == NULL)
{
}
/*
- * 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
+ * Try to find the name as a relation. Note that only
+ * relations already entered into the rangetable will be
* recognized.
*
* This is a hack for backwards compatibility with
- * PostQUEL-inspired syntax. The preferred form now
- * is "rel.*".
+ * PostQUEL-inspired syntax. The preferred form now is
+ * "rel.*".
*/
- if (cref->indirection == NIL &&
- refnameRangeTblEntry(pstate, NULL, name,
+ if (refnameRangeTblEntry(pstate, NULL, name,
&levels_up) != NULL)
- {
- rv = makeNode(RangeVar);
- rv->relname = name;
- rv->inhOpt = INH_DEFAULT;
- node = (Node *) rv;
- }
+ node = transformWholeRowRef(pstate, NULL, name,
+ cref->location);
else
- elog(ERROR, "Attribute \"%s\" not found", name);
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" does not exist",
+ name),
+ parser_errposition(pstate, cref->location)));
}
break;
}
case 2:
{
- char *name1 = strVal(lfirst(cref->fields));
+ char *name1 = strVal(linitial(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;
+ node = transformWholeRowRef(pstate, NULL, name1,
+ cref->location);
break;
}
/* Try to identify as a once-qualified column */
- node = qualifiedNameToVar(pstate, NULL, name1, name2, true);
+ node = qualifiedNameToVar(pstate, NULL, name1, name2, true,
+ cref->location);
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
+ * 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 = transformWholeRowRef(pstate, NULL, name1,
+ cref->location);
node = ParseFuncOrColumn(pstate,
- makeList1(makeString(name2)),
- makeList1(rv),
- false, false, true);
+ list_make1(makeString(name2)),
+ list_make1(node),
+ false, false, true,
+ cref->location);
}
break;
}
case 3:
{
- char *name1 = strVal(lfirst(cref->fields));
+ char *name1 = strVal(linitial(cref->fields));
char *name2 = strVal(lsecond(cref->fields));
- char *name3 = strVal(lfirst(lnext(lnext(cref->fields))));
+ char *name3 = strVal(lthird(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;
+ node = transformWholeRowRef(pstate, name1, name2,
+ cref->location);
break;
}
/* Try to identify as a twice-qualified column */
- node = qualifiedNameToVar(pstate, name1, name2, name3, true);
+ node = qualifiedNameToVar(pstate, name1, name2, name3, true,
+ cref->location);
if (node == NULL)
{
/* Try it as a function call */
- rv = makeNode(RangeVar);
- rv->schemaname = name1;
- rv->relname = name2;
- rv->inhOpt = INH_DEFAULT;
+ node = transformWholeRowRef(pstate, name1, name2,
+ cref->location);
node = ParseFuncOrColumn(pstate,
- makeList1(makeString(name3)),
- makeList1(rv),
- false, false, true);
+ list_make1(makeString(name3)),
+ list_make1(node),
+ false, false, true,
+ cref->location);
}
break;
}
case 4:
{
- char *name1 = strVal(lfirst(cref->fields));
+ char *name1 = strVal(linitial(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)))));
+ char *name3 = strVal(lthird(cref->fields));
+ char *name4 = strVal(lfourth(cref->fields));
/*
* We check the catalog name and then ignore it.
*/
- if (strcmp(name1, DatabaseName) != 0)
- elog(ERROR, "Cross-database references are not implemented");
+ if (strcmp(name1, get_database_name(MyDatabaseId)) != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cross-database references are not implemented: %s",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
/* Whole-row reference? */
if (strcmp(name4, "*") == 0)
{
- rv = makeNode(RangeVar);
- rv->schemaname = name2;
- rv->relname = name3;
- rv->inhOpt = INH_DEFAULT;
- node = (Node *) rv;
+ node = transformWholeRowRef(pstate, name2, name3,
+ cref->location);
break;
}
/* Try to identify as a twice-qualified column */
- node = qualifiedNameToVar(pstate, name2, name3, name4, true);
+ node = qualifiedNameToVar(pstate, name2, name3, name4, true,
+ cref->location);
if (node == NULL)
{
/* Try it as a function call */
- rv = makeNode(RangeVar);
- rv->schemaname = name2;
- rv->relname = name3;
- rv->inhOpt = INH_DEFAULT;
+ node = transformWholeRowRef(pstate, name2, name3,
+ cref->location);
node = ParseFuncOrColumn(pstate,
- makeList1(makeString(name4)),
- makeList1(rv),
- false, false, true);
+ list_make1(makeString(name4)),
+ list_make1(node),
+ false, false, true,
+ cref->location);
}
break;
}
default:
- elog(ERROR, "Invalid qualified name syntax (too many names)");
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("improper qualified name (too many dotted names): %s",
+ NameListToString(cref->fields)),
+ parser_errposition(pstate, cref->location)));
node = NULL; /* keep compiler quiet */
break;
}
- return transformIndirection(pstate, node, cref->indirection);
+ return node;
}
-/*
- * exprType -
- * returns the Oid of the type of the expression. (Used for typechecking.)
- */
-Oid
-exprType(Node *expr)
+static Node *
+transformParamRef(ParseState *pstate, ParamRef *pref)
{
- Oid type;
-
- if (!expr)
- return InvalidOid;
+ int paramno = pref->number;
+ ParseState *toppstate;
+ Param *param;
- switch (nodeTag(expr))
+ /*
+ * Find topmost ParseState, which is where paramtype info lives.
+ */
+ toppstate = pstate;
+ while (toppstate->parentParseState != NULL)
+ toppstate = toppstate->parentParseState;
+
+ /* Check parameter number is in range */
+ if (paramno <= 0) /* probably can't happen? */
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PARAMETER),
+ errmsg("there is no parameter $%d", paramno)));
+ if (paramno > toppstate->p_numparams)
{
- 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:
- type = ((ArrayRef *) expr)->refrestype;
- 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_BoolExpr:
- type = BOOLOID;
- break;
- case T_SubLink:
- {
- SubLink *sublink = (SubLink *) expr;
+ if (!toppstate->p_variableparams)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_PARAMETER),
+ errmsg("there is no parameter $%d",
+ paramno)));
+ /* Okay to enlarge param array */
+ if (toppstate->p_paramtypes)
+ toppstate->p_paramtypes =
+ (Oid *) repalloc(toppstate->p_paramtypes,
+ paramno * sizeof(Oid));
+ else
+ toppstate->p_paramtypes =
+ (Oid *) palloc(paramno * sizeof(Oid));
+ /* Zero out the previously-unreferenced slots */
+ MemSet(toppstate->p_paramtypes + toppstate->p_numparams,
+ 0,
+ (paramno - toppstate->p_numparams) * sizeof(Oid));
+ toppstate->p_numparams = paramno;
+ }
+ if (toppstate->p_variableparams)
+ {
+ /* If not seen before, initialize to UNKNOWN type */
+ if (toppstate->p_paramtypes[paramno - 1] == InvalidOid)
+ toppstate->p_paramtypes[paramno - 1] = UNKNOWNOID;
+ }
- if (sublink->subLinkType == EXPR_SUBLINK)
- {
- /* get the type of the subselect's first target column */
- Query *qtree = (Query *) sublink->subselect;
- TargetEntry *tent;
+ param = makeNode(Param);
+ param->paramkind = PARAM_EXTERN;
+ param->paramid = paramno;
+ param->paramtype = toppstate->p_paramtypes[paramno - 1];
+ param->paramtypmod = -1;
- if (!qtree || !IsA(qtree, Query))
- elog(ERROR, "exprType: Cannot get type for untransformed sublink");
- tent = (TargetEntry *) lfirst(qtree->targetList);
- Assert(IsA(tent, TargetEntry));
- type = tent->resdom->restype;
- }
- else
- {
- /* for all other sublink types, result is boolean */
- type = BOOLOID;
- }
- }
- break;
- case T_FieldSelect:
- type = ((FieldSelect *) expr)->resulttype;
- break;
- case T_RelabelType:
- type = ((RelabelType *) expr)->resulttype;
- break;
- case T_CaseExpr:
- type = ((CaseExpr *) expr)->casetype;
- break;
- case T_CaseWhen:
- type = exprType((Node *) ((CaseWhen *) expr)->result);
- break;
- case T_NullTest:
- type = BOOLOID;
- break;
- case T_BooleanTest:
- type = BOOLOID;
- break;
- case T_ConstraintTest:
- type = exprType((Node *) ((ConstraintTest *) expr)->arg);
- break;
- case T_ConstraintTestValue:
- type = ((ConstraintTestValue *) expr)->typeId;
- break;
- case T_RangeVar:
- /*
- * If someone uses a bare relation name in an expression,
- * we will likely first notice a problem here (see comments in
- * transformColumnRef()). Issue an appropriate error message.
- */
- elog(ERROR, "Relation reference \"%s\" cannot be used in an expression",
- ((RangeVar *) expr)->relname);
- type = InvalidOid; /* keep compiler quiet */
- break;
- default:
- elog(ERROR, "exprType: Do not know how to get type for %d node",
- nodeTag(expr));
- type = InvalidOid; /* keep compiler quiet */
- break;
- }
- return type;
+ return (Node *) param;
}
-/*
- * 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)
+static Node *
+transformAExprOp(ParseState *pstate, A_Expr *a)
{
- if (!expr)
- return -1;
+ Node *lexpr = a->lexpr;
+ Node *rexpr = a->rexpr;
+ Node *result;
- switch (nodeTag(expr))
+ /*
+ * 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 &&
+ list_length(a->name) == 1 &&
+ strcmp(strVal(linitial(a->name)), "=") == 0 &&
+ (exprIsNullConstant(lexpr) || exprIsNullConstant(rexpr)))
{
- case T_Var:
- return ((Var *) expr)->vartypmod;
- case T_Const:
- {
- /* Be smart about string constants... */
- Const *con = (Const *) expr;
+ NullTest *n = makeNode(NullTest);
- switch (con->consttype)
- {
- case BPCHAROID:
- if (!con->constisnull)
- return VARSIZE(DatumGetPointer(con->constvalue));
- break;
- default:
- break;
- }
- }
- break;
- case T_FuncExpr:
- {
+ n->nulltesttype = IS_NULL;
+
+ if (exprIsNullConstant(lexpr))
+ n->arg = (Expr *) rexpr;
+ else
+ n->arg = (Expr *) lexpr;
+
+ result = transformExpr(pstate, (Node *) n);
+ }
+ else if (lexpr && IsA(lexpr, RowExpr) &&
+ rexpr && IsA(rexpr, SubLink) &&
+ ((SubLink *) rexpr)->subLinkType == EXPR_SUBLINK)
+ {
+ /*
+ * Convert "row op subselect" into a ROWCOMPARE sublink. Formerly the
+ * grammar did this, but now that a row construct is allowed anywhere
+ * in expressions, it's easier to do it here.
+ */
+ SubLink *s = (SubLink *) rexpr;
+
+ s->subLinkType = ROWCOMPARE_SUBLINK;
+ s->testexpr = lexpr;
+ s->operName = a->name;
+ result = transformExpr(pstate, (Node *) s);
+ }
+ else if (lexpr && IsA(lexpr, RowExpr) &&
+ rexpr && IsA(rexpr, RowExpr))
+ {
+ /* "row op row" */
+ lexpr = transformExpr(pstate, lexpr);
+ rexpr = transformExpr(pstate, rexpr);
+ Assert(IsA(lexpr, RowExpr));
+ Assert(IsA(rexpr, RowExpr));
+
+ result = make_row_comparison_op(pstate,
+ a->name,
+ ((RowExpr *) lexpr)->args,
+ ((RowExpr *) rexpr)->args,
+ a->location);
+ }
+ else
+ {
+ /* Ordinary scalar operator */
+ lexpr = transformExpr(pstate, lexpr);
+ rexpr = transformExpr(pstate, rexpr);
+
+ result = (Node *) make_op(pstate,
+ a->name,
+ lexpr,
+ rexpr,
+ a->location);
+ }
+
+ return result;
+}
+
+static Node *
+transformAExprAnd(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ lexpr = coerce_to_boolean(pstate, lexpr, "AND");
+ rexpr = coerce_to_boolean(pstate, rexpr, "AND");
+
+ return (Node *) makeBoolExpr(AND_EXPR,
+ list_make2(lexpr, rexpr));
+}
+
+static Node *
+transformAExprOr(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ lexpr = coerce_to_boolean(pstate, lexpr, "OR");
+ rexpr = coerce_to_boolean(pstate, rexpr, "OR");
+
+ return (Node *) makeBoolExpr(OR_EXPR,
+ list_make2(lexpr, rexpr));
+}
+
+static Node *
+transformAExprNot(ParseState *pstate, A_Expr *a)
+{
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ rexpr = coerce_to_boolean(pstate, rexpr, "NOT");
+
+ return (Node *) makeBoolExpr(NOT_EXPR,
+ list_make1(rexpr));
+}
+
+static Node *
+transformAExprOpAny(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ return (Node *) make_scalar_array_op(pstate,
+ a->name,
+ true,
+ lexpr,
+ rexpr,
+ a->location);
+}
+
+static Node *
+transformAExprOpAll(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ return (Node *) make_scalar_array_op(pstate,
+ a->name,
+ false,
+ lexpr,
+ rexpr,
+ a->location);
+}
+
+static Node *
+transformAExprDistinct(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+
+ if (lexpr && IsA(lexpr, RowExpr) &&
+ rexpr && IsA(rexpr, RowExpr))
+ {
+ /* "row op row" */
+ return make_row_distinct_op(pstate, a->name,
+ (RowExpr *) lexpr,
+ (RowExpr *) rexpr,
+ a->location);
+ }
+ else
+ {
+ /* Ordinary scalar operator */
+ return (Node *) make_distinct_op(pstate,
+ a->name,
+ lexpr,
+ rexpr,
+ a->location);
+ }
+}
+
+static Node *
+transformAExprNullIf(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ Node *rexpr = transformExpr(pstate, a->rexpr);
+ Node *result;
+
+ result = (Node *) make_op(pstate,
+ a->name,
+ lexpr,
+ rexpr,
+ a->location);
+ if (((OpExpr *) result)->opresulttype != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("NULLIF requires = operator to yield boolean"),
+ parser_errposition(pstate, a->location)));
+
+ /*
+ * We rely on NullIfExpr and OpExpr being the same struct
+ */
+ NodeSetTag(result, T_NullIfExpr);
+
+ return result;
+}
+
+static Node *
+transformAExprOf(ParseState *pstate, A_Expr *a)
+{
+ /*
+ * Checking an expression for match to a list of type names. Will result
+ * in a boolean constant node.
+ */
+ Node *lexpr = transformExpr(pstate, a->lexpr);
+ ListCell *telem;
+ Oid ltype,
+ rtype;
+ bool matched = false;
+
+ ltype = exprType(lexpr);
+ foreach(telem, (List *) a->rexpr)
+ {
+ rtype = typenameTypeId(pstate, lfirst(telem));
+ matched = (rtype == ltype);
+ if (matched)
+ break;
+ }
+
+ /*
+ * We have two forms: equals or not equals. Flip the sense of the result
+ * for not equals.
+ */
+ if (strcmp(strVal(linitial(a->name)), "<>") == 0)
+ matched = (!matched);
+
+ return makeBoolConst(matched, false);
+}
+
+static Node *
+transformAExprIn(ParseState *pstate, A_Expr *a)
+{
+ Node *lexpr;
+ List *rexprs;
+ List *typeids;
+ bool useOr;
+ bool haveRowExpr;
+ Node *result;
+ ListCell *l;
+
+ /*
+ * If the operator is <>, combine with AND not OR.
+ */
+ if (strcmp(strVal(linitial(a->name)), "<>") == 0)
+ useOr = false;
+ else
+ useOr = true;
+
+ /*
+ * We try to generate a ScalarArrayOpExpr from IN/NOT IN, but this is only
+ * possible if the inputs are all scalars (no RowExprs) and there is a
+ * suitable array type available. If not, we fall back to a boolean
+ * condition tree with multiple copies of the lefthand expression.
+ *
+ * First step: transform all the inputs, and detect whether any are
+ * RowExprs.
+ */
+ lexpr = transformExpr(pstate, a->lexpr);
+ haveRowExpr = (lexpr && IsA(lexpr, RowExpr));
+ typeids = list_make1_oid(exprType(lexpr));
+ rexprs = NIL;
+ foreach(l, (List *) a->rexpr)
+ {
+ Node *rexpr = transformExpr(pstate, lfirst(l));
+
+ haveRowExpr |= (rexpr && IsA(rexpr, RowExpr));
+ rexprs = lappend(rexprs, rexpr);
+ typeids = lappend_oid(typeids, exprType(rexpr));
+ }
+
+ /*
+ * If not forced by presence of RowExpr, try to resolve a common scalar
+ * type for all the expressions, and see if it has an array type. (But if
+ * there's only one righthand expression, we may as well just fall through
+ * and generate a simple = comparison.)
+ */
+ if (!haveRowExpr && list_length(rexprs) != 1)
+ {
+ Oid scalar_type;
+ Oid array_type;
+
+ /*
+ * Select a common type for the array elements. Note that since the
+ * LHS' type is first in the list, it will be preferred when there is
+ * doubt (eg, when all the RHS items are unknown literals).
+ */
+ scalar_type = select_common_type(typeids, "IN");
+
+ /* Do we have an array type to use? */
+ array_type = get_array_type(scalar_type);
+ if (array_type != InvalidOid)
+ {
+ /*
+ * OK: coerce all the right-hand inputs to the common type and
+ * build an ArrayExpr for them.
+ */
+ List *aexprs;
+ ArrayExpr *newa;
+
+ aexprs = NIL;
+ foreach(l, rexprs)
+ {
+ Node *rexpr = (Node *) lfirst(l);
+
+ rexpr = coerce_to_common_type(pstate, rexpr,
+ scalar_type,
+ "IN");
+ aexprs = lappend(aexprs, rexpr);
+ }
+ newa = makeNode(ArrayExpr);
+ newa->array_typeid = array_type;
+ newa->element_typeid = scalar_type;
+ newa->elements = aexprs;
+ newa->multidims = false;
+
+ return (Node *) make_scalar_array_op(pstate,
+ a->name,
+ useOr,
+ lexpr,
+ (Node *) newa,
+ a->location);
+ }
+ }
+
+ /*
+ * Must do it the hard way, ie, with a boolean expression tree.
+ */
+ result = NULL;
+ foreach(l, rexprs)
+ {
+ Node *rexpr = (Node *) lfirst(l);
+ Node *cmp;
+
+ if (haveRowExpr)
+ {
+ if (!IsA(lexpr, RowExpr) ||
+ !IsA(rexpr, RowExpr))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("arguments of row IN must all be row expressions"),
+ parser_errposition(pstate, a->location)));
+ cmp = make_row_comparison_op(pstate,
+ a->name,
+ (List *) copyObject(((RowExpr *) lexpr)->args),
+ ((RowExpr *) rexpr)->args,
+ a->location);
+ }
+ else
+ cmp = (Node *) make_op(pstate,
+ a->name,
+ copyObject(lexpr),
+ rexpr,
+ a->location);
+
+ cmp = coerce_to_boolean(pstate, cmp, "IN");
+ if (result == NULL)
+ result = cmp;
+ else
+ result = (Node *) makeBoolExpr(useOr ? OR_EXPR : AND_EXPR,
+ list_make2(result, cmp));
+ }
+
+ return result;
+}
+
+static Node *
+transformFuncCall(ParseState *pstate, FuncCall *fn)
+{
+ List *targs;
+ ListCell *args;
+
+ /*
+ * Transform the list of arguments. We use a shallow list copy and then
+ * transform-in-place to avoid O(N^2) behavior from repeated lappend's.
+ *
+ * XXX: repeated lappend() would no longer result in O(n^2) behavior;
+ * worth reconsidering this design?
+ */
+ targs = list_copy(fn->args);
+ foreach(args, targs)
+ {
+ lfirst(args) = transformExpr(pstate,
+ (Node *) lfirst(args));
+ }
+
+ return ParseFuncOrColumn(pstate,
+ fn->funcname,
+ targs,
+ fn->agg_star,
+ fn->agg_distinct,
+ false,
+ fn->location);
+}
+
+static Node *
+transformCaseExpr(ParseState *pstate, CaseExpr *c)
+{
+ CaseExpr *newc;
+ Node *arg;
+ CaseTestExpr *placeholder;
+ List *newargs;
+ List *typeids;
+ ListCell *l;
+ Node *defresult;
+ Oid ptype;
+
+ /* If we already transformed this node, do nothing */
+ if (OidIsValid(c->casetype))
+ return (Node *) c;
+
+ newc = makeNode(CaseExpr);
+
+ /* transform the test expression, if any */
+ arg = transformExpr(pstate, (Node *) c->arg);
+
+ /* generate placeholder for test expression */
+ if (arg)
+ {
+ /*
+ * If test expression is an untyped literal, force it to text. We have
+ * to do something now because we won't be able to do this coercion on
+ * the placeholder. This is not as flexible as what was done in 7.4
+ * and before, but it's good enough to handle the sort of silly coding
+ * commonly seen.
+ */
+ if (exprType(arg) == UNKNOWNOID)
+ arg = coerce_to_common_type(pstate, arg, TEXTOID, "CASE");
+
+ placeholder = makeNode(CaseTestExpr);
+ placeholder->typeId = exprType(arg);
+ placeholder->typeMod = exprTypmod(arg);
+ }
+ else
+ placeholder = NULL;
+
+ newc->arg = (Expr *) arg;
+
+ /* transform the list of arguments */
+ newargs = NIL;
+ typeids = NIL;
+ foreach(l, c->args)
+ {
+ CaseWhen *w = (CaseWhen *) lfirst(l);
+ CaseWhen *neww = makeNode(CaseWhen);
+ Node *warg;
+
+ Assert(IsA(w, CaseWhen));
+
+ warg = (Node *) w->expr;
+ if (placeholder)
+ {
+ /* shorthand form was specified, so expand... */
+ warg = (Node *) makeSimpleA_Expr(AEXPR_OP, "=",
+ (Node *) placeholder,
+ warg,
+ -1);
+ }
+ neww->expr = (Expr *) transformExpr(pstate, warg);
+
+ neww->expr = (Expr *) coerce_to_boolean(pstate,
+ (Node *) neww->expr,
+ "CASE/WHEN");
+
+ warg = (Node *) w->result;
+ neww->result = (Expr *) transformExpr(pstate, warg);
+
+ newargs = lappend(newargs, neww);
+ typeids = lappend_oid(typeids, exprType((Node *) neww->result));
+ }
+
+ newc->args = newargs;
+
+ /* transform the default clause */
+ defresult = (Node *) c->defresult;
+ if (defresult == NULL)
+ {
+ A_Const *n = makeNode(A_Const);
+
+ n->val.type = T_Null;
+ defresult = (Node *) n;
+ }
+ newc->defresult = (Expr *) 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 = lcons_oid(exprType((Node *) newc->defresult), typeids);
+
+ ptype = select_common_type(typeids, "CASE");
+ Assert(OidIsValid(ptype));
+ newc->casetype = ptype;
+
+ /* Convert default result clause, if necessary */
+ newc->defresult = (Expr *)
+ coerce_to_common_type(pstate,
+ (Node *) newc->defresult,
+ ptype,
+ "CASE/ELSE");
+
+ /* Convert when-clause results, if necessary */
+ foreach(l, newc->args)
+ {
+ CaseWhen *w = (CaseWhen *) lfirst(l);
+
+ w->result = (Expr *)
+ coerce_to_common_type(pstate,
+ (Node *) w->result,
+ ptype,
+ "CASE/WHEN");
+ }
+
+ return (Node *) newc;
+}
+
+static Node *
+transformSubLink(ParseState *pstate, SubLink *sublink)
+{
+ List *qtrees;
+ Query *qtree;
+ Node *result = (Node *) sublink;
+
+ /* If we already transformed this node, do nothing */
+ if (IsA(sublink->subselect, Query))
+ return result;
+
+ pstate->p_hasSubLinks = true;
+ qtrees = parse_sub_analyze(sublink->subselect, pstate);
+ if (list_length(qtrees) != 1)
+ elog(ERROR, "bad query in sub-select");
+ qtree = (Query *) linitial(qtrees);
+ if (qtree->commandType != CMD_SELECT ||
+ qtree->utilityStmt != NULL ||
+ qtree->intoClause != NULL)
+ elog(ERROR, "bad query in sub-select");
+ sublink->subselect = (Node *) qtree;
+
+ if (sublink->subLinkType == EXISTS_SUBLINK)
+ {
+ /*
+ * EXISTS needs no test expression or combining operator. These fields
+ * should be null already, but make sure.
+ */
+ sublink->testexpr = NULL;
+ sublink->operName = NIL;
+ }
+ else if (sublink->subLinkType == EXPR_SUBLINK ||
+ sublink->subLinkType == ARRAY_SUBLINK)
+ {
+ ListCell *tlist_item = list_head(qtree->targetList);
+
+ /*
+ * Make sure the subselect delivers a single column (ignoring resjunk
+ * targets).
+ */
+ if (tlist_item == NULL ||
+ ((TargetEntry *) lfirst(tlist_item))->resjunk)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("subquery must return a column")));
+ while ((tlist_item = lnext(tlist_item)) != NULL)
+ {
+ if (!((TargetEntry *) lfirst(tlist_item))->resjunk)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("subquery must return only one column")));
+ }
+
+ /*
+ * EXPR and ARRAY need no test expression or combining operator. These
+ * fields should be null already, but make sure.
+ */
+ sublink->testexpr = NULL;
+ sublink->operName = NIL;
+ }
+ else
+ {
+ /* ALL, ANY, or ROWCOMPARE: generate row-comparing expression */
+ Node *lefthand;
+ List *left_list;
+ List *right_list;
+ ListCell *l;
+
+ /*
+ * Transform lefthand expression, and convert to a list
+ */
+ lefthand = transformExpr(pstate, sublink->testexpr);
+ if (lefthand && IsA(lefthand, RowExpr))
+ left_list = ((RowExpr *) lefthand)->args;
+ else
+ left_list = list_make1(lefthand);
+
+ /*
+ * Build a list of PARAM_SUBLINK nodes representing the output columns
+ * of the subquery.
+ */
+ right_list = NIL;
+ foreach(l, qtree->targetList)
+ {
+ TargetEntry *tent = (TargetEntry *) lfirst(l);
+ Param *param;
+
+ if (tent->resjunk)
+ continue;
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_SUBLINK;
+ param->paramid = tent->resno;
+ param->paramtype = exprType((Node *) tent->expr);
+ param->paramtypmod = exprTypmod((Node *) tent->expr);
+
+ right_list = lappend(right_list, param);
+ }
+
+ /*
+ * We could rely on make_row_comparison_op to complain if the list
+ * lengths differ, but we prefer to generate a more specific error
+ * message.
+ */
+ if (list_length(left_list) < list_length(right_list))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("subquery has too many columns")));
+ if (list_length(left_list) > list_length(right_list))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("subquery has too few columns")));
+
+ /*
+ * Identify the combining operator(s) and generate a suitable
+ * row-comparison expression.
+ */
+ sublink->testexpr = make_row_comparison_op(pstate,
+ sublink->operName,
+ left_list,
+ right_list,
+ -1);
+ }
+
+ return result;
+}
+
+static Node *
+transformArrayExpr(ParseState *pstate, ArrayExpr *a)
+{
+ ArrayExpr *newa = makeNode(ArrayExpr);
+ List *newelems = NIL;
+ List *newcoercedelems = NIL;
+ List *typeids = NIL;
+ ListCell *element;
+ Oid array_type;
+ Oid element_type;
+
+ /* Transform the element expressions */
+ foreach(element, a->elements)
+ {
+ Node *e = (Node *) lfirst(element);
+ Node *newe;
+
+ newe = transformExpr(pstate, e);
+ newelems = lappend(newelems, newe);
+ typeids = lappend_oid(typeids, exprType(newe));
+ }
+
+ /* Select a common type for the elements */
+ element_type = select_common_type(typeids, "ARRAY");
+
+ /* Coerce arguments to common type if necessary */
+ foreach(element, newelems)
+ {
+ Node *e = (Node *) lfirst(element);
+ Node *newe;
+
+ newe = coerce_to_common_type(pstate, e,
+ element_type,
+ "ARRAY");
+ newcoercedelems = lappend(newcoercedelems, newe);
+ }
+
+ /* Do we have an array type to use? */
+ array_type = get_array_type(element_type);
+ if (array_type != InvalidOid)
+ {
+ /* Elements are presumably of scalar type */
+ newa->multidims = false;
+ }
+ else
+ {
+ /* Must be nested array expressions */
+ newa->multidims = true;
+
+ array_type = element_type;
+ element_type = get_element_type(array_type);
+ if (!OidIsValid(element_type))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(array_type))));
+ }
+
+ newa->array_typeid = array_type;
+ newa->element_typeid = element_type;
+ newa->elements = newcoercedelems;
+
+ return (Node *) newa;
+}
+
+static Node *
+transformRowExpr(ParseState *pstate, RowExpr *r)
+{
+ RowExpr *newr = makeNode(RowExpr);
+
+ /* Transform the field expressions */
+ newr->args = transformExpressionList(pstate, r->args);
+
+ /* Barring later casting, we consider the type RECORD */
+ newr->row_typeid = RECORDOID;
+ newr->row_format = COERCE_IMPLICIT_CAST;
+
+ return (Node *) newr;
+}
+
+static Node *
+transformCoalesceExpr(ParseState *pstate, CoalesceExpr *c)
+{
+ CoalesceExpr *newc = makeNode(CoalesceExpr);
+ List *newargs = NIL;
+ List *newcoercedargs = NIL;
+ List *typeids = NIL;
+ ListCell *args;
+
+ foreach(args, c->args)
+ {
+ Node *e = (Node *) lfirst(args);
+ Node *newe;
+
+ newe = transformExpr(pstate, e);
+ newargs = lappend(newargs, newe);
+ typeids = lappend_oid(typeids, exprType(newe));
+ }
+
+ newc->coalescetype = select_common_type(typeids, "COALESCE");
+
+ /* Convert arguments if necessary */
+ foreach(args, newargs)
+ {
+ Node *e = (Node *) lfirst(args);
+ Node *newe;
+
+ newe = coerce_to_common_type(pstate, e,
+ newc->coalescetype,
+ "COALESCE");
+ newcoercedargs = lappend(newcoercedargs, newe);
+ }
+
+ newc->args = newcoercedargs;
+ return (Node *) newc;
+}
+
+static Node *
+transformMinMaxExpr(ParseState *pstate, MinMaxExpr *m)
+{
+ MinMaxExpr *newm = makeNode(MinMaxExpr);
+ List *newargs = NIL;
+ List *newcoercedargs = NIL;
+ List *typeids = NIL;
+ ListCell *args;
+
+ newm->op = m->op;
+ foreach(args, m->args)
+ {
+ Node *e = (Node *) lfirst(args);
+ Node *newe;
+
+ newe = transformExpr(pstate, e);
+ newargs = lappend(newargs, newe);
+ typeids = lappend_oid(typeids, exprType(newe));
+ }
+
+ newm->minmaxtype = select_common_type(typeids, "GREATEST/LEAST");
+
+ /* Convert arguments if necessary */
+ foreach(args, newargs)
+ {
+ Node *e = (Node *) lfirst(args);
+ Node *newe;
+
+ newe = coerce_to_common_type(pstate, e,
+ newm->minmaxtype,
+ "GREATEST/LEAST");
+ newcoercedargs = lappend(newcoercedargs, newe);
+ }
+
+ newm->args = newcoercedargs;
+ return (Node *) newm;
+}
+
+static Node *
+transformXmlExpr(ParseState *pstate, XmlExpr *x)
+{
+ XmlExpr *newx = makeNode(XmlExpr);
+ ListCell *lc;
+ int i;
+
+ newx->op = x->op;
+ if (x->name)
+ newx->name = map_sql_identifier_to_xml_name(x->name, false, false);
+ else
+ newx->name = NULL;
+
+ /*
+ * gram.y built the named args as a list of ResTarget. Transform each,
+ * and break the names out as a separate list.
+ */
+ newx->named_args = NIL;
+ newx->arg_names = NIL;
+
+ foreach(lc, x->named_args)
+ {
+ ResTarget *r = (ResTarget *) lfirst(lc);
+ Node *expr;
+ char *argname;
+
+ Assert(IsA(r, ResTarget));
+
+ expr = transformExpr(pstate, r->val);
+
+ if (r->name)
+ argname = map_sql_identifier_to_xml_name(r->name, false, false);
+ else if (IsA(r->val, ColumnRef))
+ argname = map_sql_identifier_to_xml_name(FigureColname(r->val),
+ true, false);
+ else
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ x->op == IS_XMLELEMENT
+ ? errmsg("unnamed XML attribute value must be a column reference")
+ : errmsg("unnamed XML element value must be a column reference")));
+ argname = NULL; /* keep compiler quiet */
+ }
+
+ newx->named_args = lappend(newx->named_args, expr);
+ newx->arg_names = lappend(newx->arg_names, makeString(argname));
+ }
+
+ newx->xmloption = x->xmloption;
+
+ if (x->op == IS_XMLELEMENT)
+ {
+ foreach(lc, newx->arg_names)
+ {
+ ListCell *lc2;
+
+ for_each_cell(lc2, lnext(lc))
+ {
+ if (strcmp(strVal(lfirst(lc)), strVal(lfirst(lc2))) == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("XML attribute name \"%s\" appears more than once", strVal(lfirst(lc)))));
+ }
+ }
+ }
+
+ /* The other arguments are of varying types depending on the function */
+ newx->args = NIL;
+ i = 0;
+ foreach(lc, x->args)
+ {
+ Node *e = (Node *) lfirst(lc);
+ Node *newe;
+
+ newe = transformExpr(pstate, e);
+ switch (x->op)
+ {
+ case IS_XMLCONCAT:
+ newe = coerce_to_specific_type(pstate, newe, XMLOID,
+ "XMLCONCAT");
+ break;
+ case IS_XMLELEMENT:
+ /* no coercion necessary */
+ break;
+ case IS_XMLFOREST:
+ newe = coerce_to_specific_type(pstate, newe, XMLOID,
+ "XMLFOREST");
+ break;
+ case IS_XMLPARSE:
+ if (i == 0)
+ newe = coerce_to_specific_type(pstate, newe, TEXTOID,
+ "XMLPARSE");
+ else
+ newe = coerce_to_boolean(pstate, newe, "XMLPARSE");
+ break;
+ case IS_XMLPI:
+ newe = coerce_to_specific_type(pstate, newe, TEXTOID,
+ "XMLPI");
+ break;
+ case IS_XMLROOT:
+ if (i == 0)
+ newe = coerce_to_specific_type(pstate, newe, XMLOID,
+ "XMLROOT");
+ else if (i == 1)
+ newe = coerce_to_specific_type(pstate, newe, TEXTOID,
+ "XMLROOT");
+ else
+ newe = coerce_to_specific_type(pstate, newe, INT4OID,
+ "XMLROOT");
+ break;
+ case IS_XMLSERIALIZE:
+ /* not handled here */
+ break;
+ case IS_DOCUMENT:
+ newe = coerce_to_specific_type(pstate, newe, XMLOID,
+ "IS DOCUMENT");
+ break;
+ }
+ newx->args = lappend(newx->args, newe);
+ i++;
+ }
+
+ return (Node *) newx;
+}
+
+static Node *
+transformXmlSerialize(ParseState *pstate, XmlSerialize *xs)
+{
+ Oid targetType;
+ int32 targetTypmod;
+ XmlExpr *xexpr;
+
+ xexpr = makeNode(XmlExpr);
+ xexpr->op = IS_XMLSERIALIZE;
+ xexpr->args = list_make1(coerce_to_specific_type(pstate,
+ transformExpr(pstate, xs->expr),
+ XMLOID,
+ "XMLSERIALIZE"));
+
+ targetType = typenameTypeId(pstate, xs->typename);
+ targetTypmod = typenameTypeMod(pstate, xs->typename, targetType);
+
+ xexpr->xmloption = xs->xmloption;
+ /* We actually only need these to be able to parse back the expression. */
+ xexpr->type = targetType;
+ xexpr->typmod = targetTypmod;
+
+ /*
+ * The actual target type is determined this way. SQL allows char
+ * and varchar as target types. We allow anything that can be
+ * cast implicitly from text. This way, user-defined text-like
+ * data types automatically fit in.
+ */
+ return (Node *) coerce_to_target_type(pstate, (Node *) xexpr, TEXTOID, targetType, targetTypmod,
+ COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
+}
+
+static Node *
+transformBooleanTest(ParseState *pstate, BooleanTest *b)
+{
+ 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, "unrecognized booltesttype: %d",
+ (int) b->booltesttype);
+ clausename = NULL; /* keep compiler quiet */
+ }
+
+ b->arg = (Expr *) transformExpr(pstate, (Node *) b->arg);
+
+ b->arg = (Expr *) coerce_to_boolean(pstate,
+ (Node *) b->arg,
+ clausename);
+
+ return (Node *) b;
+}
+
+/*
+ * 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, char *schemaname, char *relname,
+ int location)
+{
+ Node *result;
+ RangeTblEntry *rte;
+ int vnum;
+ int sublevels_up;
+ Oid toid;
+
+ /* Look up the referenced RTE, creating it if needed */
+
+ rte = refnameRangeTblEntry(pstate, schemaname, relname,
+ &sublevels_up);
+
+ if (rte == NULL)
+ rte = addImplicitRTE(pstate, makeRangeVar(schemaname, relname),
+ location);
+
+ vnum = RTERangeTablePosn(pstate, rte, &sublevels_up);
+
+ /* Build the appropriate referencing node */
+
+ 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 = (Node *) 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 = (Node *) 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 = (Node *) makeVar(vnum,
+ 1,
+ toid,
+ -1,
+ sublevels_up);
+ }
+ break;
+ case RTE_VALUES:
+ toid = RECORDOID;
+ /* returns composite; same as relation case */
+ result = (Node *) 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 = (Node *) makeVar(vnum,
+ InvalidAttrNumber,
+ RECORDOID,
+ -1,
+ sublevels_up);
+ break;
+ }
+
+ 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_FieldSelect:
+ type = ((FieldSelect *) expr)->resulttype;
+ break;
+ case T_FieldStore:
+ type = ((FieldStore *) expr)->resulttype;
+ break;
+ case T_RelabelType:
+ type = ((RelabelType *) 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;
+ 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... */
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
+ * 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;
+ ListCell *arg;
if (!cexpr->defresult)
return -1;
return typmod;
}
break;
- case T_ConstraintTest:
- return exprTypmod((Node *) ((ConstraintTest *) expr)->arg);
- case T_ConstraintTestValue:
- return ((ConstraintTestValue *) expr)->typeMod;
+ 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;
}
*
* 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)
{
- FuncExpr *func;
- int nargs;
- Const *second_arg;
-
if (coercedTypmod != NULL)
*coercedTypmod = -1; /* default result on failure */
- /* Is it a function-call at all? */
- if (expr == NULL || !IsA(expr, FuncExpr))
- return false;
- func = (FuncExpr *) expr;
-
/*
- * If it didn't come from a coercion context, reject.
+ * Scalar-type length coercions are FuncExprs, array-type length
+ * coercions are ArrayCoerceExprs
*/
- if (func->funcformat != COERCE_EXPLICIT_CAST &&
- func->funcformat != COERCE_IMPLICIT_CAST)
- return false;
+ 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 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 = length(func->args);
- if (nargs < 2 || nargs > 3)
- return false;
+ if (expr && IsA(expr, ArrayCoerceExpr))
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) expr;
- second_arg = (Const *) lsecond(func->args);
- if (!IsA(second_arg, Const) ||
- second_arg->consttype != INT4OID ||
- second_arg->constisnull)
- return false;
+ /* 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 function.
- */
- if (coercedTypmod != NULL)
- *coercedTypmod = DatumGetInt32(second_arg->constvalue);
+ /*
+ * OK, it is indeed a length-coercion expression.
+ */
+ if (coercedTypmod != NULL)
+ *coercedTypmod = acoerce->resulttypmod;
- return true;
+ return true;
+ }
+
+ return false;
}
/*
* the type name and then apply any necessary coercion function(s).
*/
static Node *
-typecast_expression(Node *expr, TypeName *typename)
+typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
{
Oid inputType = exprType(expr);
Oid targetType;
+ int32 targetTypmod;
- targetType = typenameTypeId(typename);
+ targetType = typenameTypeId(pstate, typename);
+ targetTypmod = typenameTypeMod(pstate, typename, targetType);
if (inputType == InvalidOid)
return expr; /* do nothing if NULL input */
- expr = coerce_to_target_type(expr, inputType,
- targetType, typename->typmod,
+ expr = coerce_to_target_type(pstate, expr, inputType,
+ targetType, targetTypmod,
COERCION_EXPLICIT,
COERCE_EXPLICIT_CAST);
if (expr == NULL)
- elog(ERROR, "Cannot cast type %s to %s",
- format_type_be(inputType),
- format_type_be(targetType));
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(inputType),
+ format_type_be(targetType)),
+ parser_errposition(pstate, typename->location)));
return expr;
}
+
+/*
+ * Transform a "row compare-op row" construct
+ *
+ * The inputs are lists of already-transformed expressions.
+ * As with coerce_type, pstate may be NULL if no special unknown-Param
+ * processing is wanted.
+ *
+ * The output may be a single OpExpr, an AND or OR combination of OpExprs,
+ * or a RowCompareExpr. In all cases it is guaranteed to return boolean.
+ * The AND, OR, and RowCompareExpr cases further imply things about the
+ * behavior of the operators (ie, they behave as =, <>, or < <= > >=).
+ */
+static Node *
+make_row_comparison_op(ParseState *pstate, List *opname,
+ List *largs, List *rargs, int location)
+{
+ RowCompareExpr *rcexpr;
+ RowCompareType rctype;
+ List *opexprs;
+ List *opnos;
+ List *opfamilies;
+ ListCell *l,
+ *r;
+ List **opfamily_lists;
+ List **opstrat_lists;
+ Bitmapset *strats;
+ int nopers;
+ int i;
+
+ nopers = list_length(largs);
+ if (nopers != list_length(rargs))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unequal number of entries in row expressions"),
+ parser_errposition(pstate, location)));
+
+ /*
+ * We can't compare zero-length rows because there is no principled basis
+ * for figuring out what the operator is.
+ */
+ if (nopers == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot compare rows of zero length"),
+ parser_errposition(pstate, location)));
+
+ /*
+ * Identify all the pairwise operators, using make_op so that behavior is
+ * the same as in the simple scalar case.
+ */
+ opexprs = NIL;
+ forboth(l, largs, r, rargs)
+ {
+ Node *larg = (Node *) lfirst(l);
+ Node *rarg = (Node *) lfirst(r);
+ OpExpr *cmp;
+
+ cmp = (OpExpr *) make_op(pstate, opname, larg, rarg, location);
+ Assert(IsA(cmp, OpExpr));
+
+ /*
+ * We don't use coerce_to_boolean here because we insist on the
+ * operator yielding boolean directly, not via coercion. If it
+ * doesn't yield bool it won't be in any index opfamilies...
+ */
+ if (cmp->opresulttype != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("row comparison operator must yield type boolean, "
+ "not type %s",
+ format_type_be(cmp->opresulttype)),
+ parser_errposition(pstate, location)));
+ if (expression_returns_set((Node *) cmp))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("row comparison operator must not return a set"),
+ parser_errposition(pstate, location)));
+ opexprs = lappend(opexprs, cmp);
+ }
+
+ /*
+ * If rows are length 1, just return the single operator. In this case we
+ * don't insist on identifying btree semantics for the operator (but we
+ * still require it to return boolean).
+ */
+ if (nopers == 1)
+ return (Node *) linitial(opexprs);
+
+ /*
+ * Now we must determine which row comparison semantics (= <> < <= > >=)
+ * apply to this set of operators. We look for btree opfamilies containing
+ * the operators, and see which interpretations (strategy numbers) exist
+ * for each operator.
+ */
+ opfamily_lists = (List **) palloc(nopers * sizeof(List *));
+ opstrat_lists = (List **) palloc(nopers * sizeof(List *));
+ strats = NULL;
+ i = 0;
+ foreach(l, opexprs)
+ {
+ Oid opno = ((OpExpr *) lfirst(l))->opno;
+ Bitmapset *this_strats;
+ ListCell *j;
+
+ get_op_btree_interpretation(opno,
+ &opfamily_lists[i], &opstrat_lists[i]);
+
+ /*
+ * convert strategy number list to a Bitmapset to make the
+ * intersection calculation easy.
+ */
+ this_strats = NULL;
+ foreach(j, opstrat_lists[i])
+ {
+ this_strats = bms_add_member(this_strats, lfirst_int(j));
+ }
+ if (i == 0)
+ strats = this_strats;
+ else
+ strats = bms_int_members(strats, this_strats);
+ i++;
+ }
+
+ /*
+ * If there are multiple common interpretations, we may use any one of
+ * them ... this coding arbitrarily picks the lowest btree strategy
+ * number.
+ */
+ i = bms_first_member(strats);
+ if (i < 0)
+ {
+ /* No common interpretation, so fail */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not determine interpretation of row comparison operator %s",
+ strVal(llast(opname))),
+ errhint("Row comparison operators must be associated with btree operator families."),
+ parser_errposition(pstate, location)));
+ }
+ rctype = (RowCompareType) i;
+
+ /*
+ * For = and <> cases, we just combine the pairwise operators with AND or
+ * OR respectively.
+ *
+ * Note: this is presently the only place where the parser generates
+ * BoolExpr with more than two arguments. Should be OK since the rest of
+ * the system thinks BoolExpr is N-argument anyway.
+ */
+ if (rctype == ROWCOMPARE_EQ)
+ return (Node *) makeBoolExpr(AND_EXPR, opexprs);
+ if (rctype == ROWCOMPARE_NE)
+ return (Node *) makeBoolExpr(OR_EXPR, opexprs);
+
+ /*
+ * Otherwise we need to choose exactly which opfamily to associate with
+ * each operator.
+ */
+ opfamilies = NIL;
+ for (i = 0; i < nopers; i++)
+ {
+ Oid opfamily = InvalidOid;
+
+ forboth(l, opfamily_lists[i], r, opstrat_lists[i])
+ {
+ int opstrat = lfirst_int(r);
+
+ if (opstrat == rctype)
+ {
+ opfamily = lfirst_oid(l);
+ break;
+ }
+ }
+ if (OidIsValid(opfamily))
+ opfamilies = lappend_oid(opfamilies, opfamily);
+ else /* should not happen */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("could not determine interpretation of row comparison operator %s",
+ strVal(llast(opname))),
+ errdetail("There are multiple equally-plausible candidates."),
+ parser_errposition(pstate, location)));
+ }
+
+ /*
+ * Now deconstruct the OpExprs and create a RowCompareExpr.
+ *
+ * Note: can't just reuse the passed largs/rargs lists, because of
+ * possibility that make_op inserted coercion operations.
+ */
+ opnos = NIL;
+ largs = NIL;
+ rargs = NIL;
+ foreach(l, opexprs)
+ {
+ OpExpr *cmp = (OpExpr *) lfirst(l);
+
+ opnos = lappend_oid(opnos, cmp->opno);
+ largs = lappend(largs, linitial(cmp->args));
+ rargs = lappend(rargs, lsecond(cmp->args));
+ }
+
+ rcexpr = makeNode(RowCompareExpr);
+ rcexpr->rctype = rctype;
+ rcexpr->opnos = opnos;
+ rcexpr->opfamilies = opfamilies;
+ rcexpr->largs = largs;
+ rcexpr->rargs = rargs;
+
+ return (Node *) rcexpr;
+}
+
+/*
+ * Transform a "row IS DISTINCT FROM row" construct
+ *
+ * The input RowExprs are already transformed
+ */
+static Node *
+make_row_distinct_op(ParseState *pstate, List *opname,
+ RowExpr *lrow, RowExpr *rrow,
+ int location)
+{
+ Node *result = NULL;
+ List *largs = lrow->args;
+ List *rargs = rrow->args;
+ ListCell *l,
+ *r;
+
+ if (list_length(largs) != list_length(rargs))
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("unequal number of entries in row expressions"),
+ parser_errposition(pstate, location)));
+
+ forboth(l, largs, r, rargs)
+ {
+ Node *larg = (Node *) lfirst(l);
+ Node *rarg = (Node *) lfirst(r);
+ Node *cmp;
+
+ cmp = (Node *) make_distinct_op(pstate, opname, larg, rarg, location);
+ if (result == NULL)
+ result = cmp;
+ else
+ result = (Node *) makeBoolExpr(OR_EXPR,
+ list_make2(result, cmp));
+ }
+
+ if (result == NULL)
+ {
+ /* zero-length rows? Generate constant FALSE */
+ result = makeBoolConst(false, false);
+ }
+
+ return result;
+}
+
+/*
+ * make the node for an IS DISTINCT FROM operator
+ */
+static Expr *
+make_distinct_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
+ int location)
+{
+ Expr *result;
+
+ result = make_op(pstate, opname, ltree, rtree, location);
+ if (((OpExpr *) result)->opresulttype != BOOLOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("IS DISTINCT FROM requires = operator to yield boolean"),
+ parser_errposition(pstate, location)));
+
+ /*
+ * We rely on DistinctExpr and OpExpr being same struct
+ */
+ NodeSetTag(result, T_DistinctExpr);
+
+ return result;
+}