mirror of
https://github.com/postgres/postgres.git
synced 2025-06-14 18:42:34 +03:00
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.
This commit is contained in:
@ -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; }
|
|||||||
* 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; }
|
|||||||
{
|
{
|
||||||
$$ = (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);
|
|||||||
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)
|
|||||||
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)
|
|||||||
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
|
|||||||
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
|
||||||
|
Reference in New Issue
Block a user