aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_expr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/parser/parse_expr.c')
-rw-r--r--src/backend/parser/parse_expr.c219
1 files changed, 170 insertions, 49 deletions
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index cf520d53369..ece78b21820 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.187 2005/11/22 18:17:16 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.188 2005/11/28 04:35:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -47,6 +47,7 @@ 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);
@@ -64,9 +65,9 @@ static Node *transformIndirection(ParseState *pstate, Node *basenode,
static Node *typecast_expression(ParseState *pstate, Node *expr,
TypeName *typename);
static Node *make_row_op(ParseState *pstate, List *opname,
- Node *ltree, Node *rtree);
+ RowExpr *lrow, RowExpr *rrow);
static Node *make_row_distinct_op(ParseState *pstate, List *opname,
- Node *ltree, Node *rtree);
+ RowExpr *lrow, RowExpr *rrow);
static Expr *make_distinct_op(ParseState *pstate, List *opname,
Node *ltree, Node *rtree);
@@ -180,6 +181,9 @@ transformExpr(ParseState *pstate, Node *expr)
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);
}
@@ -603,7 +607,15 @@ transformAExprOp(ParseState *pstate, A_Expr *a)
rexpr && IsA(rexpr, RowExpr))
{
/* "row op row" */
- result = make_row_op(pstate, a->name, lexpr, rexpr);
+ lexpr = transformExpr(pstate, lexpr);
+ rexpr = transformExpr(pstate, rexpr);
+ Assert(IsA(lexpr, RowExpr));
+ Assert(IsA(rexpr, RowExpr));
+
+ result = make_row_op(pstate,
+ a->name,
+ (RowExpr *) lexpr,
+ (RowExpr *) rexpr);
}
else
{
@@ -686,22 +698,20 @@ transformAExprOpAll(ParseState *pstate, A_Expr *a)
static Node *
transformAExprDistinct(ParseState *pstate, A_Expr *a)
{
- Node *lexpr = a->lexpr;
- Node *rexpr = a->rexpr;
+ 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,
- lexpr, rexpr);
+ (RowExpr *) lexpr,
+ (RowExpr *) rexpr);
}
else
{
/* Ordinary scalar operator */
- lexpr = transformExpr(pstate, lexpr);
- rexpr = transformExpr(pstate, rexpr);
-
return (Node *) make_distinct_op(pstate,
a->name,
lexpr,
@@ -737,15 +747,14 @@ static Node *
transformAExprOf(ParseState *pstate, A_Expr *a)
{
/*
- * Checking an expression for match to type. Will result in a boolean
- * constant node.
+ * 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;
- A_Const *n;
Oid ltype,
rtype;
bool matched = false;
- Node *lexpr = transformExpr(pstate, a->lexpr);
ltype = exprType(lexpr);
foreach(telem, (List *) a->rexpr)
@@ -757,18 +766,145 @@ transformAExprOf(ParseState *pstate, A_Expr *a)
}
/*
- * Expect two forms: equals or not equals. Flip the sense of the result
+ * We have two forms: equals or not equals. Flip the sense of the result
* for not equals.
*/
- if (strcmp(strVal(linitial(a->name)), "!=") == 0)
+ if (strcmp(strVal(linitial(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");
+ 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));
+ }
- return transformExpr(pstate, (Node *) n);
+ /*
+ * 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);
+ }
+ }
+
+ /*
+ * 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")));
+ cmp = make_row_op(pstate,
+ a->name,
+ (RowExpr *) copyObject(lexpr),
+ (RowExpr *) rexpr);
+ }
+ else
+ cmp = (Node *) make_op(pstate,
+ a->name,
+ copyObject(lexpr),
+ rexpr);
+
+ 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 *
@@ -1818,32 +1954,25 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
/*
* Transform a "row op row" construct
+ *
+ * The input RowExprs are already transformed
*/
static Node *
-make_row_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
+make_row_op(ParseState *pstate, List *opname,
+ RowExpr *lrow, RowExpr *rrow)
{
Node *result = NULL;
- RowExpr *lrow,
- *rrow;
- List *largs,
- *rargs;
+ List *largs = lrow->args;
+ List *rargs = rrow->args;
ListCell *l,
*r;
char *oprname;
BoolExprType boolop;
- /* Inputs are untransformed RowExprs */
- lrow = (RowExpr *) transformExpr(pstate, ltree);
- rrow = (RowExpr *) transformExpr(pstate, rtree);
- Assert(IsA(lrow, RowExpr));
- Assert(IsA(rrow, RowExpr));
- largs = lrow->args;
- rargs = rrow->args;
-
if (list_length(largs) != list_length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unequal number of entries in row expression")));
+ errmsg("unequal number of entries in row expressions")));
/*
* XXX it's really wrong to generate a simple AND combination for < <= >
@@ -1898,31 +2027,23 @@ make_row_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree)
/*
* Transform a "row IS DISTINCT FROM row" construct
+ *
+ * The input RowExprs are already transformed
*/
static Node *
make_row_distinct_op(ParseState *pstate, List *opname,
- Node *ltree, Node *rtree)
+ RowExpr *lrow, RowExpr *rrow)
{
Node *result = NULL;
- RowExpr *lrow,
- *rrow;
- List *largs,
- *rargs;
+ List *largs = lrow->args;
+ List *rargs = rrow->args;
ListCell *l,
*r;
- /* Inputs are untransformed RowExprs */
- lrow = (RowExpr *) transformExpr(pstate, ltree);
- rrow = (RowExpr *) transformExpr(pstate, rtree);
- Assert(IsA(lrow, RowExpr));
- Assert(IsA(rrow, RowExpr));
- largs = lrow->args;
- rargs = rrow->args;
-
if (list_length(largs) != list_length(rargs))
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("unequal number of entries in row expression")));
+ errmsg("unequal number of entries in row expressions")));
forboth(l, largs, r, rargs)
{