mirror of
https://github.com/postgres/postgres.git
synced 2025-06-04 12:42:24 +03:00
Finished the Between patch Christopher started.
Implements between (symmetric / asymmetric) as a node. Executes the left or right expression once, makes a Const out of the resulting Datum and executes the >=, <= portions out of the Const sets. Of course, the parser does a fair amount of preparatory work for this to happen. Rod Taylor
This commit is contained in:
parent
7ea5f1d7f1
commit
3e22406ec6
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.97 2002/07/06 20:16:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.98 2002/07/18 04:41:44 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -38,6 +38,9 @@
|
||||
#include "executor/execdebug.h"
|
||||
#include "executor/functions.h"
|
||||
#include "executor/nodeSubplan.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
#include "parser/parse.h"
|
||||
#include "parser/parse_expr.h"
|
||||
#include "utils/array.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/fcache.h"
|
||||
@ -62,6 +65,8 @@ static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
|
||||
static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
|
||||
static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalBetweenExpr(BetweenExpr *btest, ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
|
||||
bool *isNull, ExprDoneCond *isDone);
|
||||
static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
|
||||
@ -1188,6 +1193,104 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
|
||||
return (Datum) 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEvalBetweenExpr
|
||||
*
|
||||
* Evaluate a BetweenExpr node. Result is
|
||||
* a boolean. If any of the three expression
|
||||
* parameters are NULL, result is NULL.
|
||||
* ----------------------------------------------------------------
|
||||
*/
|
||||
static Datum
|
||||
ExecEvalBetweenExpr(BetweenExpr *btest,
|
||||
ExprContext *econtext,
|
||||
bool *isNull,
|
||||
ExprDoneCond *isDone)
|
||||
{
|
||||
Datum expr_result;
|
||||
Datum lexpr_result;
|
||||
Datum rexpr_result;
|
||||
bool result = FALSE;
|
||||
Node *expr_const;
|
||||
Node *lexpr_const;
|
||||
Node *rexpr_const;
|
||||
|
||||
/* Evaluate subexpressons and Auto-return if we find a NULL */
|
||||
expr_result = ExecEvalExpr(btest->expr, econtext, isNull, isDone);
|
||||
if (*isNull)
|
||||
return (Datum) 0;
|
||||
|
||||
lexpr_result = ExecEvalExpr(btest->lexpr, econtext, isNull, isDone);
|
||||
if (*isNull)
|
||||
return (Datum) 0;
|
||||
|
||||
rexpr_result = ExecEvalExpr(btest->rexpr, econtext, isNull, isDone);
|
||||
if (*isNull)
|
||||
return (Datum) 0;
|
||||
|
||||
/*
|
||||
* Make a Constant out of our newly found Datums
|
||||
* Types were coerced during transformExpr to be common
|
||||
*/
|
||||
expr_const = (Node *) makeConst(btest->typeId, btest->typeLen,
|
||||
expr_result, false,
|
||||
btest->typeByVal, false, true);
|
||||
|
||||
lexpr_const = (Node *) makeConst(btest->typeId, btest->typeLen,
|
||||
lexpr_result, false,
|
||||
btest->typeByVal, false, true);
|
||||
|
||||
rexpr_const = (Node *) makeConst(btest->typeId, btest->typeLen,
|
||||
rexpr_result, false,
|
||||
btest->typeByVal, false, true);
|
||||
|
||||
/*
|
||||
* Test the between case which for the straight forward method.
|
||||
* expr >= lexpr and expr <= rexpr
|
||||
*
|
||||
* Of course, can't use makeA_Expr here without requiring another
|
||||
* transform, so we've already prepared a gthan and lthan operator
|
||||
* set in the parsing stage.
|
||||
*/
|
||||
btest->gthan->args = makeList2(expr_const, lexpr_const);
|
||||
if (DatumGetBool(ExecEvalExpr((Node *) btest->gthan,
|
||||
econtext,
|
||||
isNull, isDone)))
|
||||
{
|
||||
btest->lthan->args = makeList2(expr_const, rexpr_const);
|
||||
result = DatumGetBool(ExecEvalExpr((Node *) btest->lthan,
|
||||
econtext,
|
||||
isNull, isDone));
|
||||
}
|
||||
|
||||
/*
|
||||
* If this is a symmetric BETWEEN, we win a second try with the operators
|
||||
* reversed. (a >= min(b,c) and a <= max(b,c))
|
||||
*/
|
||||
if (!result && btest->symmetric)
|
||||
{
|
||||
btest->gthan->args = makeList2(expr_const, rexpr_const);
|
||||
if (DatumGetBool(ExecEvalExpr((Node *) btest->gthan,
|
||||
econtext,
|
||||
isNull, isDone)))
|
||||
{
|
||||
btest->lthan->args = makeList2(expr_const, lexpr_const);
|
||||
result = DatumGetBool(ExecEvalExpr((Node *) btest->lthan,
|
||||
econtext,
|
||||
isNull, isDone));
|
||||
}
|
||||
}
|
||||
|
||||
/* Apply NOT as necessary */
|
||||
if (btest->not)
|
||||
result = !result;
|
||||
|
||||
/* We're not returning a null */
|
||||
*isNull = false;
|
||||
|
||||
return (BoolGetDatum(result));
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------
|
||||
* ExecEvalNullTest
|
||||
*
|
||||
@ -1524,6 +1627,12 @@ ExecEvalExpr(Node *expression,
|
||||
isNull,
|
||||
isDone);
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
retDatum = ExecEvalBetweenExpr((BetweenExpr *) expression,
|
||||
econtext,
|
||||
isNull,
|
||||
isDone);
|
||||
break;
|
||||
case T_NullTest:
|
||||
retDatum = ExecEvalNullTest((NullTest *) expression,
|
||||
econtext,
|
||||
@ -1536,7 +1645,6 @@ ExecEvalExpr(Node *expression,
|
||||
isNull,
|
||||
isDone);
|
||||
break;
|
||||
|
||||
default:
|
||||
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
|
||||
nodeTag(expression));
|
||||
|
@ -15,7 +15,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.194 2002/07/16 22:12:19 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.195 2002/07/18 04:41:44 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -999,6 +999,32 @@ _copyCaseExpr(CaseExpr *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _copyBetweenExpr
|
||||
* ----------------
|
||||
*/
|
||||
static BetweenExpr *
|
||||
_copyBetweenExpr(BetweenExpr *from)
|
||||
{
|
||||
BetweenExpr *newnode = makeNode(BetweenExpr);
|
||||
|
||||
/*
|
||||
* copy remainder of node
|
||||
*/
|
||||
Node_Copy(from, newnode, expr);
|
||||
Node_Copy(from, newnode, lexpr);
|
||||
Node_Copy(from, newnode, rexpr);
|
||||
Node_Copy(from, newnode, lthan);
|
||||
Node_Copy(from, newnode, gthan);
|
||||
newnode->symmetric = from->symmetric;
|
||||
newnode->not = from->not;
|
||||
newnode->typeId = from->typeId;
|
||||
newnode->typeLen = from->typeLen;
|
||||
newnode->typeByVal = from->typeByVal;
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _copyCaseWhen
|
||||
* ----------------
|
||||
@ -3052,6 +3078,9 @@ copyObject(void *from)
|
||||
case T_CaseExpr:
|
||||
retval = _copyCaseExpr(from);
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
retval = _copyBetweenExpr(from);
|
||||
break;
|
||||
case T_CaseWhen:
|
||||
retval = _copyCaseWhen(from);
|
||||
break;
|
||||
|
@ -20,7 +20,7 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.141 2002/07/16 22:12:19 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.142 2002/07/18 04:41:44 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -1769,6 +1769,33 @@ _equalCaseExpr(CaseExpr *a, CaseExpr *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalBetweenExpr(BetweenExpr *a, BetweenExpr *b)
|
||||
{
|
||||
if (!equal(a->expr, b->expr))
|
||||
return false;
|
||||
if (!equal(a->lexpr, b->lexpr))
|
||||
return false;
|
||||
if (!equal(a->rexpr, b->rexpr))
|
||||
return false;
|
||||
if (!equal(a->lthan, b->lthan))
|
||||
return false;
|
||||
if (!equal(a->gthan, b->gthan))
|
||||
return false;
|
||||
if (a->symmetric != b->symmetric)
|
||||
return false;
|
||||
if (a->not != b->not)
|
||||
return false;
|
||||
if (a->typeId != b->typeId)
|
||||
return false;
|
||||
if (a->typeLen != b->typeLen)
|
||||
return false;
|
||||
if (a->typeByVal != b->typeByVal)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalCaseWhen(CaseWhen *a, CaseWhen *b)
|
||||
{
|
||||
@ -2217,6 +2244,9 @@ equal(void *a, void *b)
|
||||
case T_CaseExpr:
|
||||
retval = _equalCaseExpr(a, b);
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
retval = _equalBetweenExpr(a, b);
|
||||
break;
|
||||
case T_CaseWhen:
|
||||
retval = _equalCaseWhen(a, b);
|
||||
break;
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.163 2002/07/16 22:12:19 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.164 2002/07/18 04:41:44 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Every (plan) node in POSTGRES has an associated "out" routine which
|
||||
@ -1483,6 +1483,38 @@ _outCaseWhen(StringInfo str, CaseWhen *node)
|
||||
_outNode(str, node->result);
|
||||
}
|
||||
|
||||
/*
|
||||
* BetweenExpr
|
||||
*/
|
||||
static void
|
||||
_outBetweenExpr(StringInfo str, BetweenExpr *node)
|
||||
{
|
||||
appendStringInfo(str, " BETWEENEXPR :expr ");
|
||||
_outNode(str, node->expr);
|
||||
|
||||
appendStringInfo(str, " :not %s",
|
||||
booltostr(node->not));
|
||||
|
||||
appendStringInfo(str, " :symmetric %s",
|
||||
booltostr(node->symmetric));
|
||||
|
||||
appendStringInfo(str, " :lexpr ");
|
||||
_outNode(str, node->lexpr);
|
||||
|
||||
appendStringInfo(str, " :rexpr ");
|
||||
_outNode(str, node->rexpr);
|
||||
|
||||
appendStringInfo(str, " :gthan ");
|
||||
_outNode(str, node->gthan);
|
||||
|
||||
appendStringInfo(str, " :lthan ");
|
||||
_outNode(str, node->lthan);
|
||||
|
||||
appendStringInfo(str, " :typeid %u :typelen %d :typebyval %s",
|
||||
node->typeId, node->typeLen,
|
||||
booltostr(node->typeByVal));
|
||||
}
|
||||
|
||||
/*
|
||||
* NullTest
|
||||
*/
|
||||
@ -1767,6 +1799,9 @@ _outNode(StringInfo str, void *obj)
|
||||
case T_CaseExpr:
|
||||
_outCaseExpr(str, obj);
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
_outBetweenExpr(str, obj);
|
||||
break;
|
||||
case T_CaseWhen:
|
||||
_outCaseWhen(str, obj);
|
||||
break;
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.124 2002/07/04 15:23:54 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.125 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
* NOTES
|
||||
* Most of the read functions for plan nodes are tested. (In fact, they
|
||||
@ -881,6 +881,53 @@ _readCaseWhen(void)
|
||||
return local_node;
|
||||
}
|
||||
|
||||
static BetweenExpr *
|
||||
_readBetweenExpr(void)
|
||||
{
|
||||
BetweenExpr *local_node;
|
||||
char *token;
|
||||
int length;
|
||||
|
||||
local_node = makeNode(BetweenExpr);
|
||||
|
||||
token = pg_strtok(&length); /* eat :expr */
|
||||
local_node->expr = nodeRead(true);
|
||||
|
||||
token = pg_strtok(&length); /* eat :not */
|
||||
token = pg_strtok(&length); /* get not */
|
||||
local_node->not = strtobool(token);
|
||||
|
||||
token = pg_strtok(&length); /* eat :symmetric */
|
||||
token = pg_strtok(&length); /* get symmetric */
|
||||
local_node->symmetric = strtobool(token);
|
||||
|
||||
token = pg_strtok(&length); /* eat :lexpr */
|
||||
local_node->lexpr = nodeRead(true);
|
||||
|
||||
token = pg_strtok(&length); /* eat :rexpr */
|
||||
local_node->rexpr = nodeRead(true);
|
||||
|
||||
token = pg_strtok(&length); /* eat :gthan */
|
||||
local_node->gthan = nodeRead(true);
|
||||
|
||||
token = pg_strtok(&length); /* eat :lthan */
|
||||
local_node->lthan = nodeRead(true);
|
||||
|
||||
token = pg_strtok(&length); /* eat :typeid */
|
||||
token = pg_strtok(&length); /* get typeid */
|
||||
local_node->typeId = atooid(token);
|
||||
|
||||
token = pg_strtok(&length); /* eat :typelen */
|
||||
token = pg_strtok(&length); /* get typelen */
|
||||
local_node->typeLen = atoui(token);
|
||||
|
||||
token = pg_strtok(&length); /* eat :typebyval */
|
||||
token = pg_strtok(&length); /* get typebyval */
|
||||
local_node->typeByVal = strtobool(token);
|
||||
|
||||
return local_node;
|
||||
}
|
||||
|
||||
/* ----------------
|
||||
* _readNullTest
|
||||
*
|
||||
@ -2132,6 +2179,8 @@ parsePlanString(void)
|
||||
return_value = _readNullTest();
|
||||
else if (length == 11 && strncmp(token, "BOOLEANTEST", length) == 0)
|
||||
return_value = _readBooleanTest();
|
||||
else if (length == 11 && strncmp(token, "BETWEENEXPR", length) == 0)
|
||||
return_value = _readBetweenExpr();
|
||||
else
|
||||
elog(ERROR, "badly formatted planstring \"%.10s\"...", token);
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.103 2002/07/06 20:16:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.104 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -1817,8 +1817,6 @@ expression_tree_walker(Node *node,
|
||||
case T_Var:
|
||||
case T_Param:
|
||||
case T_RangeTblRef:
|
||||
/* primitive node types with no subnodes */
|
||||
break;
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
@ -1965,6 +1963,18 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
{
|
||||
BetweenExpr *betwn = (BetweenExpr *) node;
|
||||
|
||||
if (walker(betwn->expr, context))
|
||||
return true;
|
||||
if (walker(betwn->lexpr, context))
|
||||
return true;
|
||||
if (walker(betwn->rexpr, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "expression_tree_walker: Unexpected node type %d",
|
||||
nodeTag(node));
|
||||
@ -2123,8 +2133,6 @@ expression_tree_mutator(Node *node,
|
||||
case T_Var:
|
||||
case T_Param:
|
||||
case T_RangeTblRef:
|
||||
/* primitive node types with no subnodes */
|
||||
return (Node *) copyObject(node);
|
||||
case T_Expr:
|
||||
{
|
||||
Expr *expr = (Expr *) node;
|
||||
@ -2272,6 +2280,18 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
{
|
||||
BetweenExpr *bexpr = (BetweenExpr *) node;
|
||||
BetweenExpr *newnode;
|
||||
|
||||
FLATCOPY(newnode, bexpr, BetweenExpr);
|
||||
MUTATE(newnode->expr, bexpr->expr, Node *);
|
||||
MUTATE(newnode->lexpr, bexpr->lexpr, Node *);
|
||||
MUTATE(newnode->rexpr, bexpr->rexpr, Node *);
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_SubLink:
|
||||
{
|
||||
/*
|
||||
|
@ -11,7 +11,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.342 2002/07/18 02:02:30 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.343 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
* HISTORY
|
||||
* AUTHOR DATE MAJOR EVENT
|
||||
@ -235,7 +235,7 @@ static void doNegateFloat(Value *v);
|
||||
|
||||
%type <list> extract_list, overlay_list, position_list
|
||||
%type <list> substr_list, trim_list
|
||||
%type <ival> opt_interval
|
||||
%type <ival> opt_interval, opt_symmetry
|
||||
%type <node> overlay_placing, substr_from, substr_for
|
||||
|
||||
%type <boolean> opt_instead, opt_cursor
|
||||
@ -320,7 +320,7 @@ static void doNegateFloat(Value *v);
|
||||
/* ordinary key words in alphabetical order */
|
||||
%token <keyword> ABORT_TRANS, ABSOLUTE, ACCESS, ACTION, ADD, AFTER,
|
||||
AGGREGATE, ALL, ALTER, ANALYSE, ANALYZE, AND, ANY, AS, ASC,
|
||||
ASSERTION, ASSIGNMENT, AT, AUTHORIZATION,
|
||||
ASSERTION, ASSIGNMENT, ASYMMETRIC, AT, AUTHORIZATION,
|
||||
|
||||
BACKWARD, BEFORE, BEGIN_TRANS, BETWEEN, BIGINT, BINARY, BIT, BOTH,
|
||||
BOOLEAN, BY,
|
||||
@ -379,7 +379,7 @@ static void doNegateFloat(Value *v);
|
||||
SERIALIZABLE, SESSION, SESSION_USER, SET, SETOF, SHARE,
|
||||
SHOW, SIMILAR, SIMPLE, SMALLINT, SOME, STABLE, START, STATEMENT,
|
||||
STATISTICS, STDIN, STDOUT, STORAGE, STRICT, SUBSTRING,
|
||||
SYSID,
|
||||
SYMMETRIC, SYSID,
|
||||
|
||||
TABLE, TEMP, TEMPLATE, TEMPORARY, THEN, TIME, TIMESTAMP,
|
||||
TO, TOAST, TRAILING, TRANSACTION, TREAT, TRIGGER, TRIM, TRUE_P,
|
||||
@ -5500,17 +5500,25 @@ a_expr: c_expr { $$ = $1; }
|
||||
}
|
||||
| a_expr IS DISTINCT FROM a_expr %prec DISTINCT
|
||||
{ $$ = (Node *) makeSimpleA_Expr(DISTINCT, "=", $1, $5); }
|
||||
| a_expr BETWEEN b_expr AND b_expr %prec BETWEEN
|
||||
| a_expr BETWEEN opt_symmetry b_expr AND b_expr %prec BETWEEN
|
||||
{
|
||||
$$ = (Node *) makeA_Expr(AND, NIL,
|
||||
(Node *) makeSimpleA_Expr(OP, ">=", $1, $3),
|
||||
(Node *) makeSimpleA_Expr(OP, "<=", $1, $5));
|
||||
BetweenExpr *n = makeNode(BetweenExpr);
|
||||
n->expr = $1;
|
||||
n->symmetric = $3;
|
||||
n->lexpr = $4;
|
||||
n->rexpr = $6;
|
||||
n->not = false;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| a_expr NOT BETWEEN b_expr AND b_expr %prec BETWEEN
|
||||
| a_expr NOT BETWEEN opt_symmetry b_expr AND b_expr %prec BETWEEN
|
||||
{
|
||||
$$ = (Node *) makeA_Expr(OR, NIL,
|
||||
(Node *) makeSimpleA_Expr(OP, "<", $1, $4),
|
||||
(Node *) makeSimpleA_Expr(OP, ">", $1, $6));
|
||||
BetweenExpr *n = makeNode(BetweenExpr);
|
||||
n->expr = $1;
|
||||
n->symmetric = $4;
|
||||
n->lexpr = $5;
|
||||
n->rexpr = $7;
|
||||
n->not = true;
|
||||
$$ = (Node *)n;
|
||||
}
|
||||
| a_expr IN_P in_expr
|
||||
{
|
||||
@ -5582,6 +5590,11 @@ a_expr: c_expr { $$ = $1; }
|
||||
{ $$ = $1; }
|
||||
;
|
||||
|
||||
opt_symmetry: SYMMETRIC { $$ = TRUE; }
|
||||
| ASYMMETRIC { $$ = FALSE; }
|
||||
| /* EMPTY */ { $$ = FALSE; }
|
||||
;
|
||||
|
||||
/*
|
||||
* Restricted expressions
|
||||
*
|
||||
@ -6919,6 +6932,7 @@ reserved_keyword:
|
||||
| ANY
|
||||
| AS
|
||||
| ASC
|
||||
| ASYMMETRIC
|
||||
| BOTH
|
||||
| CASE
|
||||
| CAST
|
||||
@ -6969,6 +6983,7 @@ reserved_keyword:
|
||||
| SELECT
|
||||
| SESSION_USER
|
||||
| SOME
|
||||
| SYMMETRIC
|
||||
| TABLE
|
||||
| THEN
|
||||
| TO
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.119 2002/07/11 07:39:26 ishii Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.120 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -45,6 +45,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"asc", ASC},
|
||||
{"assertion", ASSERTION},
|
||||
{"assignment", ASSIGNMENT},
|
||||
{"asymmetric", ASYMMETRIC},
|
||||
{"at", AT},
|
||||
{"authorization", AUTHORIZATION},
|
||||
{"backward", BACKWARD},
|
||||
@ -272,6 +273,7 @@ static const ScanKeyword ScanKeywords[] = {
|
||||
{"storage", STORAGE},
|
||||
{"strict", STRICT},
|
||||
{"substring", SUBSTRING},
|
||||
{"symmetric", SYMMETRIC},
|
||||
{"sysid", SYSID},
|
||||
{"table", TABLE},
|
||||
{"temp", TEMP},
|
||||
|
@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.121 2002/07/06 20:16:36 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.122 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -69,11 +69,8 @@ parse_expr_init(void)
|
||||
* here.
|
||||
*
|
||||
* NOTE: there are various cases in which this routine will get applied to
|
||||
* an already-transformed expression. Some examples:
|
||||
* 1. At least one construct (BETWEEN/AND) puts the same nodes
|
||||
* into two branches of the parse tree; hence, some nodes
|
||||
* are transformed twice.
|
||||
* 2. Another way it can happen is that coercion of an operator or
|
||||
* an already-transformed expression. An examples:
|
||||
* - Another way it can happen is that coercion of an operator or
|
||||
* function argument to the required type (via coerce_type())
|
||||
* can apply transformExpr to an already-transformed subexpression.
|
||||
* An example here is "SELECT count(*) + 1.0 FROM table".
|
||||
@ -588,6 +585,82 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
result = expr;
|
||||
break;
|
||||
}
|
||||
case T_BetweenExpr:
|
||||
{
|
||||
BetweenExpr *b = (BetweenExpr *) expr;
|
||||
List *typeIds = NIL;
|
||||
HeapTuple tup;
|
||||
Form_pg_operator opform;
|
||||
|
||||
/* Transform the expressions */
|
||||
b->expr = transformExpr(pstate, b->expr);
|
||||
b->lexpr = transformExpr(pstate, b->lexpr);
|
||||
b->rexpr = transformExpr(pstate, b->rexpr);
|
||||
|
||||
/* Find coercion type for all 3 entities */
|
||||
typeIds = lappendi(typeIds, exprType(b->expr));
|
||||
typeIds = lappendi(typeIds, exprType(b->lexpr));
|
||||
typeIds = lappendi(typeIds, exprType(b->rexpr));
|
||||
b->typeId = select_common_type(typeIds, "TransformExpr");
|
||||
|
||||
/* Additional type information */
|
||||
b->typeLen = get_typlen(b->typeId);
|
||||
b->typeByVal = get_typbyval(b->typeId);
|
||||
|
||||
/* Coerce the three expressions to the type */
|
||||
b->expr = coerce_to_common_type(pstate, b->expr,
|
||||
b->typeId,
|
||||
"TransformExpr");
|
||||
|
||||
b->lexpr = coerce_to_common_type(pstate, b->lexpr,
|
||||
b->typeId,
|
||||
"TransformExpr");
|
||||
|
||||
b->rexpr = coerce_to_common_type(pstate, b->rexpr,
|
||||
b->typeId,
|
||||
"TransformExpr");
|
||||
|
||||
/* Build the >= operator */
|
||||
tup = oper(makeList1(makeString(">=")),
|
||||
b->typeId, b->typeId, false);
|
||||
opform = (Form_pg_operator) GETSTRUCT(tup);
|
||||
|
||||
/* Triple check our types */
|
||||
if (b->typeId != opform->oprright || b->typeId != opform->oprleft)
|
||||
elog(ERROR, "transformExpr: Unable to find appropriate"
|
||||
" operator for between operation");
|
||||
|
||||
b->gthan = makeNode(Expr);
|
||||
b->gthan->typeOid = opform->oprresult;
|
||||
b->gthan->opType = OP_EXPR;
|
||||
b->gthan->oper = (Node *) makeOper(oprid(tup), /* opno */
|
||||
oprfuncid(tup), /* opid */
|
||||
opform->oprresult, /* opresulttype */
|
||||
get_func_retset(opform->oprcode));/* opretset */
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
/* Build the equation for <= operator */
|
||||
tup = oper(makeList1(makeString("<=")),
|
||||
b->typeId, b->typeId, false);
|
||||
opform = (Form_pg_operator) GETSTRUCT(tup);
|
||||
|
||||
/* Triple check the types */
|
||||
if (b->typeId != opform->oprright || b->typeId != opform->oprleft)
|
||||
elog(ERROR, "transformExpr: Unable to find appropriate"
|
||||
" operator for between operation");
|
||||
|
||||
b->lthan = makeNode(Expr);
|
||||
b->lthan->typeOid = opform->oprresult;
|
||||
b->lthan->opType = OP_EXPR;
|
||||
b->lthan->oper = (Node *) makeOper(oprid(tup), /* opno */
|
||||
oprfuncid(tup), /* opid */
|
||||
opform->oprresult, /* opresulttype */
|
||||
get_func_retset(opform->oprcode));/* opretset */
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
result = expr;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Quietly accept node types that may be presented when we are
|
||||
@ -609,7 +682,6 @@ transformExpr(ParseState *pstate, Node *expr)
|
||||
result = (Node *) expr;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
/* should not reach here */
|
||||
elog(ERROR, "transformExpr: does not know how to transform node %d"
|
||||
@ -890,6 +962,9 @@ exprType(Node *expr)
|
||||
case T_BooleanTest:
|
||||
type = BOOLOID;
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
type = BOOLOID;
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "exprType: Do not know how to get type for %d node",
|
||||
nodeTag(expr));
|
||||
|
@ -3,7 +3,7 @@
|
||||
* back to source text
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.109 2002/07/04 15:24:07 thomas Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.110 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
* This software is copyrighted by Jan Wieck - Hamburg.
|
||||
*
|
||||
@ -1879,7 +1879,32 @@ get_rule_expr(Node *node, deparse_context *context)
|
||||
}
|
||||
}
|
||||
break;
|
||||
case T_BetweenExpr:
|
||||
{
|
||||
BetweenExpr *btwn = (BetweenExpr *) node;
|
||||
|
||||
get_rule_expr(btwn->expr, context);
|
||||
|
||||
if (btwn->not)
|
||||
appendStringInfo(buf, " NOT");
|
||||
|
||||
appendStringInfo(buf, " BETWEEN");
|
||||
|
||||
/*
|
||||
* Output both symmetric and asymmetric, even though
|
||||
* asymmetric is default
|
||||
*/
|
||||
if (btwn->symmetric)
|
||||
appendStringInfo(buf, " SYMMETRIC ");
|
||||
else
|
||||
appendStringInfo(buf, " ASYMMETRIC ");
|
||||
|
||||
get_rule_expr(btwn->lexpr, context);
|
||||
|
||||
appendStringInfo(buf, " AND ");
|
||||
get_rule_expr(btwn->rexpr, context);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
elog(ERROR, "get_rule_expr: unknown node type %d", nodeTag(node));
|
||||
break;
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: makefuncs.h,v 1.37 2002/06/20 20:29:49 momjian Exp $
|
||||
* $Id: makefuncs.h,v 1.38 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -57,5 +57,6 @@ extern RelabelType *makeRelabelType(Node *arg, Oid rtype, int32 rtypmod);
|
||||
extern RangeVar *makeRangeVar(char *schemaname, char *relname);
|
||||
|
||||
extern TypeName *makeTypeName(char *typnam);
|
||||
extern TypeName *makeQualifiedTypeName(List *lst);
|
||||
|
||||
#endif /* MAKEFUNC_H */
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: nodes.h,v 1.110 2002/07/11 07:39:27 ishii Exp $
|
||||
* $Id: nodes.h,v 1.111 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -226,6 +226,7 @@ typedef enum NodeTag
|
||||
T_GroupClause,
|
||||
T_NullTest,
|
||||
T_BooleanTest,
|
||||
T_BetweenExpr,
|
||||
T_CaseExpr,
|
||||
T_CaseWhen,
|
||||
T_FkConstraint,
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parsenodes.h,v 1.187 2002/07/16 22:12:20 tgl Exp $
|
||||
* $Id: parsenodes.h,v 1.188 2002/07/18 04:41:45 momjian Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -173,6 +173,25 @@ typedef struct A_Const
|
||||
TypeName *typename; /* typecast */
|
||||
} A_Const;
|
||||
|
||||
/*
|
||||
* BetweenExpr - an SQL99 BETWEEN expression
|
||||
*/
|
||||
|
||||
typedef struct BetweenExpr
|
||||
{
|
||||
NodeTag type;
|
||||
Node *expr; /* Expression to check */
|
||||
Node *lexpr; /* First bound */
|
||||
Node *rexpr; /* Second bound */
|
||||
bool not; /* Do we want inverse? */
|
||||
bool symmetric; /* True if SYMMETRIC, false if ASYMMETRIC */
|
||||
Oid typeId; /* Information abotu common type */
|
||||
int16 typeLen;
|
||||
bool typeByVal;
|
||||
Expr *gthan;
|
||||
Expr *lthan;
|
||||
} BetweenExpr;
|
||||
|
||||
/*
|
||||
* TypeCast - a CAST expression
|
||||
*
|
||||
|
@ -430,3 +430,150 @@ SELECT p.name, p.age FROM person* p ORDER BY age using >, name;
|
||||
mary | 8
|
||||
(58 rows)
|
||||
|
||||
--
|
||||
-- Test between syntax
|
||||
--
|
||||
SELECT 2 BETWEEN 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 BETWEEN 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT 2 BETWEEN ASYMMETRIC 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 BETWEEN ASYMMETRIC 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT 2 BETWEEN SYMMETRIC 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 BETWEEN SYMMETRIC 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN ASYMMETRIC 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN ASYMMETRIC 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN SYMMETRIC 1 AND 3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT 2 NOT BETWEEN SYMMETRIC 3 AND 1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN ASYMMETRIC -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN ASYMMETRIC -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN SYMMETRIC -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 BETWEEN SYMMETRIC -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
f
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN ASYMMETRIC -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN ASYMMETRIC -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN SYMMETRIC -1 AND -3;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
SELECT -4 NOT BETWEEN SYMMETRIC -3 AND -1;
|
||||
?column?
|
||||
----------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
|
@ -103,3 +103,31 @@ SELECT p.name, p.age FROM person* p;
|
||||
--
|
||||
SELECT p.name, p.age FROM person* p ORDER BY age using >, name;
|
||||
|
||||
--
|
||||
-- Test between syntax
|
||||
--
|
||||
SELECT 2 BETWEEN 1 AND 3;
|
||||
SELECT 2 BETWEEN 3 AND 1;
|
||||
SELECT 2 BETWEEN ASYMMETRIC 1 AND 3;
|
||||
SELECT 2 BETWEEN ASYMMETRIC 3 AND 1;
|
||||
SELECT 2 BETWEEN SYMMETRIC 1 AND 3;
|
||||
SELECT 2 BETWEEN SYMMETRIC 3 AND 1;
|
||||
SELECT 2 NOT BETWEEN 1 AND 3;
|
||||
SELECT 2 NOT BETWEEN 3 AND 1;
|
||||
SELECT 2 NOT BETWEEN ASYMMETRIC 1 AND 3;
|
||||
SELECT 2 NOT BETWEEN ASYMMETRIC 3 AND 1;
|
||||
SELECT 2 NOT BETWEEN SYMMETRIC 1 AND 3;
|
||||
SELECT 2 NOT BETWEEN SYMMETRIC 3 AND 1;
|
||||
SELECT -4 BETWEEN -1 AND -3;
|
||||
SELECT -4 BETWEEN -3 AND -1;
|
||||
SELECT -4 BETWEEN ASYMMETRIC -1 AND -3;
|
||||
SELECT -4 BETWEEN ASYMMETRIC -3 AND -1;
|
||||
SELECT -4 BETWEEN SYMMETRIC -1 AND -3;
|
||||
SELECT -4 BETWEEN SYMMETRIC -3 AND -1;
|
||||
SELECT -4 NOT BETWEEN -1 AND -3;
|
||||
SELECT -4 NOT BETWEEN -3 AND -1;
|
||||
SELECT -4 NOT BETWEEN ASYMMETRIC -1 AND -3;
|
||||
SELECT -4 NOT BETWEEN ASYMMETRIC -3 AND -1;
|
||||
SELECT -4 NOT BETWEEN SYMMETRIC -1 AND -3;
|
||||
SELECT -4 NOT BETWEEN SYMMETRIC -3 AND -1;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user