Commit 34af082f authored by Tom Lane's avatar Tom Lane

Represent BETWEEN as a special node type in raw parse trees.

Previously, gram.y itself converted BETWEEN into AND (or AND/OR) nests of
expression comparisons.  This was always as bogus as could be, but fixing
it hasn't risen to the top of the to-do list.  The present patch invents an
A_Expr representation for BETWEEN expressions, and does the expansion to
comparison trees in parse_expr.c which is at least a slightly saner place
to be doing semantic conversions.  There should be no change in the post-
parse-analysis results.

This does nothing for the semantic issues with BETWEEN (dubious connection
to btree-opclass semantics, and multiple evaluation of possibly volatile
subexpressions) ... but it's a necessary preliminary step before we could
fix any of that.  The main immediate benefit is that preserving BETWEEN as
an identifiable raw-parse-tree construct will enable better error messages.

While at it, fix the code so that multiply-referenced subexpressions are
physically duplicated before being passed through transformExpr().  This
gets rid of one of the principal reasons why transformExpr() has
historically had to allow already-processed input.
parent 74811c40
...@@ -2512,6 +2512,22 @@ _outAExpr(StringInfo str, const A_Expr *node) ...@@ -2512,6 +2512,22 @@ _outAExpr(StringInfo str, const A_Expr *node)
appendStringInfoString(str, " IN "); appendStringInfoString(str, " IN ");
WRITE_NODE_FIELD(name); WRITE_NODE_FIELD(name);
break; break;
case AEXPR_BETWEEN:
appendStringInfoString(str, " BETWEEN ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_NOT_BETWEEN:
appendStringInfoString(str, " NOT_BETWEEN ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_BETWEEN_SYM:
appendStringInfoString(str, " BETWEEN_SYM ");
WRITE_NODE_FIELD(name);
break;
case AEXPR_NOT_BETWEEN_SYM:
appendStringInfoString(str, " NOT_BETWEEN_SYM ");
WRITE_NODE_FIELD(name);
break;
default: default:
appendStringInfoString(str, " ??"); appendStringInfoString(str, " ??");
break; break;
......
...@@ -11178,7 +11178,7 @@ a_expr: c_expr { $$ = $1; } ...@@ -11178,7 +11178,7 @@ a_expr: c_expr { $$ = $1; }
* below; and all those operators will have the same precedence. * below; and all those operators will have the same precedence.
* *
* If you add more explicitly-known operators, be sure to add them * If you add more explicitly-known operators, be sure to add them
* also to b_expr and to the MathOp list above. * also to b_expr and to the MathOp list below.
*/ */
| '+' a_expr %prec UMINUS | '+' a_expr %prec UMINUS
{ $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); } { $$ = (Node *) makeSimpleA_Expr(AEXPR_OP, "+", NULL, $2, @1); }
...@@ -11396,50 +11396,36 @@ a_expr: c_expr { $$ = $1; } ...@@ -11396,50 +11396,36 @@ a_expr: c_expr { $$ = $1; }
{ {
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2); $$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "<>", $1, (Node *) $6, @2);
} }
/*
* Ideally we would not use hard-wired operators below but
* instead use opclasses. However, mixed data types and other
* issues make this difficult:
* http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
*/
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN | a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
{ {
$$ = makeAndExpr( $$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN,
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2), "BETWEEN",
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2), $1,
(Node *) list_make2($4, $6),
@2); @2);
} }
| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN | a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
{ {
$$ = makeOrExpr( $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_BETWEEN,
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2), "NOT BETWEEN",
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2), $1,
(Node *) list_make2($5, $7),
@2); @2);
} }
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN | a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
{ {
$$ = makeOrExpr( $$ = (Node *) makeSimpleA_Expr(AEXPR_BETWEEN_SYM,
makeAndExpr( "BETWEEN SYMMETRIC",
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4, @2), $1,
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6, @2), (Node *) list_make2($4, $6),
@2),
makeAndExpr(
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $6, @2),
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $4, @2),
@2),
@2); @2);
} }
| a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN | a_expr NOT BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
{ {
$$ = makeAndExpr( $$ = (Node *) makeSimpleA_Expr(AEXPR_NOT_BETWEEN_SYM,
makeOrExpr( "NOT BETWEEN SYMMETRIC",
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5, @2), $1,
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7, @2), (Node *) list_make2($5, $7),
@2),
makeOrExpr(
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7, @2),
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5, @2),
@2),
@2); @2);
} }
| a_expr IN_P in_expr | a_expr IN_P in_expr
......
...@@ -48,6 +48,7 @@ static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a); ...@@ -48,6 +48,7 @@ static Node *transformAExprDistinct(ParseState *pstate, A_Expr *a);
static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a); static Node *transformAExprNullIf(ParseState *pstate, A_Expr *a);
static Node *transformAExprOf(ParseState *pstate, A_Expr *a); static Node *transformAExprOf(ParseState *pstate, A_Expr *a);
static Node *transformAExprIn(ParseState *pstate, A_Expr *a); static Node *transformAExprIn(ParseState *pstate, A_Expr *a);
static Node *transformAExprBetween(ParseState *pstate, A_Expr *a);
static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a); static Node *transformBoolExpr(ParseState *pstate, BoolExpr *a);
static Node *transformFuncCall(ParseState *pstate, FuncCall *fn); static Node *transformFuncCall(ParseState *pstate, FuncCall *fn);
static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref); static Node *transformMultiAssignRef(ParseState *pstate, MultiAssignRef *maref);
...@@ -241,6 +242,12 @@ transformExprRecurse(ParseState *pstate, Node *expr) ...@@ -241,6 +242,12 @@ transformExprRecurse(ParseState *pstate, Node *expr)
case AEXPR_IN: case AEXPR_IN:
result = transformAExprIn(pstate, a); result = transformAExprIn(pstate, a);
break; break;
case AEXPR_BETWEEN:
case AEXPR_NOT_BETWEEN:
case AEXPR_BETWEEN_SYM:
case AEXPR_NOT_BETWEEN_SYM:
result = transformAExprBetween(pstate, a);
break;
default: default:
elog(ERROR, "unrecognized A_Expr kind: %d", a->kind); elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
result = NULL; /* keep compiler quiet */ result = NULL; /* keep compiler quiet */
...@@ -1195,6 +1202,101 @@ transformAExprIn(ParseState *pstate, A_Expr *a) ...@@ -1195,6 +1202,101 @@ transformAExprIn(ParseState *pstate, A_Expr *a)
return result; return result;
} }
static Node *
transformAExprBetween(ParseState *pstate, A_Expr *a)
{
Node *aexpr;
Node *bexpr;
Node *cexpr;
Node *result;
Node *sub1;
Node *sub2;
List *args;
/* Deconstruct A_Expr into three subexprs */
aexpr = a->lexpr;
Assert(IsA(a->rexpr, List));
args = (List *) a->rexpr;
Assert(list_length(args) == 2);
bexpr = (Node *) linitial(args);
cexpr = (Node *) lsecond(args);
/*
* Build the equivalent comparison expression. Make copies of
* multiply-referenced subexpressions for safety. (XXX this is really
* wrong since it results in multiple runtime evaluations of what may be
* volatile expressions ...)
*
* Ideally we would not use hard-wired operators here but instead use
* opclasses. However, mixed data types and other issues make this
* difficult:
* http://archives.postgresql.org/pgsql-hackers/2008-08/msg01142.php
*/
switch (a->kind)
{
case AEXPR_BETWEEN:
args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
aexpr, bexpr,
a->location),
makeSimpleA_Expr(AEXPR_OP, "<=",
copyObject(aexpr), cexpr,
a->location));
result = (Node *) makeBoolExpr(AND_EXPR, args, a->location);
break;
case AEXPR_NOT_BETWEEN:
args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
aexpr, bexpr,
a->location),
makeSimpleA_Expr(AEXPR_OP, ">",
copyObject(aexpr), cexpr,
a->location));
result = (Node *) makeBoolExpr(OR_EXPR, args, a->location);
break;
case AEXPR_BETWEEN_SYM:
args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
aexpr, bexpr,
a->location),
makeSimpleA_Expr(AEXPR_OP, "<=",
copyObject(aexpr), cexpr,
a->location));
sub1 = (Node *) makeBoolExpr(AND_EXPR, args, a->location);
args = list_make2(makeSimpleA_Expr(AEXPR_OP, ">=",
copyObject(aexpr), copyObject(cexpr),
a->location),
makeSimpleA_Expr(AEXPR_OP, "<=",
copyObject(aexpr), copyObject(bexpr),
a->location));
sub2 = (Node *) makeBoolExpr(AND_EXPR, args, a->location);
args = list_make2(sub1, sub2);
result = (Node *) makeBoolExpr(OR_EXPR, args, a->location);
break;
case AEXPR_NOT_BETWEEN_SYM:
args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
aexpr, bexpr,
a->location),
makeSimpleA_Expr(AEXPR_OP, ">",
copyObject(aexpr), cexpr,
a->location));
sub1 = (Node *) makeBoolExpr(OR_EXPR, args, a->location);
args = list_make2(makeSimpleA_Expr(AEXPR_OP, "<",
copyObject(aexpr), copyObject(cexpr),
a->location),
makeSimpleA_Expr(AEXPR_OP, ">",
copyObject(aexpr), copyObject(bexpr),
a->location));
sub2 = (Node *) makeBoolExpr(OR_EXPR, args, a->location);
args = list_make2(sub1, sub2);
result = (Node *) makeBoolExpr(AND_EXPR, args, a->location);
break;
default:
elog(ERROR, "unrecognized A_Expr kind: %d", a->kind);
result = NULL; /* keep compiler quiet */
break;
}
return transformExprRecurse(pstate, result);
}
static Node * static Node *
transformBoolExpr(ParseState *pstate, BoolExpr *a) transformBoolExpr(ParseState *pstate, BoolExpr *a)
{ {
......
...@@ -232,7 +232,11 @@ typedef enum A_Expr_Kind ...@@ -232,7 +232,11 @@ typedef enum A_Expr_Kind
AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */ AEXPR_DISTINCT, /* IS DISTINCT FROM - name must be "=" */
AEXPR_NULLIF, /* NULLIF - name must be "=" */ AEXPR_NULLIF, /* NULLIF - name must be "=" */
AEXPR_OF, /* IS [NOT] OF - name must be "=" or "<>" */ AEXPR_OF, /* IS [NOT] OF - name must be "=" or "<>" */
AEXPR_IN /* [NOT] IN - name must be "=" or "<>" */ AEXPR_IN, /* [NOT] IN - name must be "=" or "<>" */
AEXPR_BETWEEN, /* name must be "BETWEEN" */
AEXPR_NOT_BETWEEN, /* name must be "NOT BETWEEN" */
AEXPR_BETWEEN_SYM, /* name must be "BETWEEN SYMMETRIC" */
AEXPR_NOT_BETWEEN_SYM /* name must be "NOT BETWEEN SYMMETRIC" */
} A_Expr_Kind; } A_Expr_Kind;
typedef struct A_Expr typedef struct A_Expr
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment