summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Stitcher <astitcher@apache.org>2013-03-15 17:29:33 +0000
committerAndrew Stitcher <astitcher@apache.org>2013-03-15 17:29:33 +0000
commitfea21d3bd5dfa9c5ac02619243af699621f20bdc (patch)
tree163ee4f84fd597b6c70222ee14b2e4ab4f8f3ae2
parent742052a4e3ca4693d2a991a0e21df8dde12c1b82 (diff)
downloadqpid-python-fea21d3bd5dfa9c5ac02619243af699621f20bdc.tar.gz
QPID-4623: Implemented parser and evaluator for BETWEEN comparisons
- The implementation uses a custom between operator rather than desugaring the syntax, because we can avoid repeated evaluation of the expression bing compared that way (this will also be true for the IN comparison operator). git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1457029 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--qpid/cpp/src/qpid/broker/SelectorExpression.cpp69
-rw-r--r--qpid/cpp/src/tests/Selector.cpp7
2 files changed, 57 insertions, 19 deletions
diff --git a/qpid/cpp/src/qpid/broker/SelectorExpression.cpp b/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
index 5b8621b518..62de067396 100644
--- a/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
+++ b/qpid/cpp/src/qpid/broker/SelectorExpression.cpp
@@ -67,8 +67,10 @@
*
* ComparisonExpression ::= PrimaryExpression "IS" "NULL" |
* PrimaryExpression "IS" "NOT" "NULL" |
- * PrimaryExpression "LIKE" LiteralString [ "ESCAPE" LiteralString ]
- * PrimaryExpression "NOT" "LIKE" LiteralString [ "ESCAPE" LiteralString ]
+ * PrimaryExpression "LIKE" LiteralString [ "ESCAPE" LiteralString ] |
+ * PrimaryExpression "NOT" "LIKE" LiteralString [ "ESCAPE" LiteralString ] |
+ * PrimaryExpression "BETWEEN" PrimaryExpression "AND" PrimaryExpression |
+ * PrimaryExpression "NOT" "BETWEEN" PrimaryExpression "AND" PrimaryExpression |
* PrimaryExpression ComparisonOpsOps PrimaryExpression |
* "NOT" ComparisonExpression |
* "(" OrExpression ")"
@@ -318,6 +320,34 @@ public:
}
};
+class BetweenExpression : public BoolExpression {
+ boost::scoped_ptr<Expression> e;
+ boost::scoped_ptr<Expression> l;
+ boost::scoped_ptr<Expression> u;
+
+public:
+ BetweenExpression(Expression* e_, Expression* l_, Expression* u_) :
+ e(e_),
+ l(l_),
+ u(u_)
+ {}
+
+ void repr(ostream& os) const {
+ os << *e << " BETWEEN " << *l << " AND " << *u;
+ }
+
+ BoolOrNone eval_bool(const SelectorEnv& env) const {
+ Value ve(e->eval(env));
+ if (unknown(ve)) return BN_UNKNOWN;
+ Value vl(l->eval(env));
+ if (unknown(vl)) return BN_UNKNOWN;
+ if (ve<vl) return BN_FALSE;
+ Value vu(u->eval(env));
+ if (unknown(vu)) return BN_UNKNOWN;
+ return BoolOrNone(ve<=vu);
+ }
+};
+
// Expression types...
class Literal : public Expression {
@@ -570,23 +600,25 @@ BoolExpression* parseSpecialComparisons(Tokeniser& tokeniser, std::auto_ptr<Expr
switch (tokeniser.nextToken().type) {
case T_LIKE: {
const Token t = tokeniser.nextToken();
- if ( t.type==T_STRING ) {
- // Check for "ESCAPE"
- if ( tokeniser.nextToken().type==T_ESCAPE ) {
- const Token e = tokeniser.nextToken();
- if ( e.type==T_STRING ) {
- return new LikeExpression(e1.release(), t.val, e.val);
- } else {
- return 0;
- }
- } else {
- tokeniser.returnTokens();
- return new LikeExpression(e1.release(), t.val);
- }
+ if ( t.type!=T_STRING ) return 0;
+ // Check for "ESCAPE"
+ if ( tokeniser.nextToken().type==T_ESCAPE ) {
+ const Token e = tokeniser.nextToken();
+ if ( e.type!=T_STRING ) return 0;
+ return new LikeExpression(e1.release(), t.val, e.val);
} else {
- return 0;
+ tokeniser.returnTokens();
+ return new LikeExpression(e1.release(), t.val);
}
}
+ case T_BETWEEN: {
+ std::auto_ptr<Expression> lower(parsePrimaryExpression(tokeniser));
+ if ( !lower.get() ) return 0;
+ if ( tokeniser.nextToken().type!=T_AND ) return 0;
+ std::auto_ptr<Expression> upper(parsePrimaryExpression(tokeniser));
+ if ( !upper.get() ) return 0;
+ return new BetweenExpression(e1.release(), lower.release(), upper.release());
+ }
default:
return 0;
}
@@ -628,11 +660,10 @@ BoolExpression* parseComparisonExpression(Tokeniser& tokeniser)
if (!e.get()) return 0;
return new UnaryBooleanExpression<BoolExpression>(&notOp, e.release());
}
+ case T_BETWEEN:
case T_LIKE: {
tokeniser.returnTokens();
- std::auto_ptr<BoolExpression> e(parseSpecialComparisons(tokeniser, e1));
- if (!e.get()) return 0;
- return e.release();
+ return parseSpecialComparisons(tokeniser, e1);
}
default:
break;
diff --git a/qpid/cpp/src/tests/Selector.cpp b/qpid/cpp/src/tests/Selector.cpp
index 7208f80e85..1b0eea3d7e 100644
--- a/qpid/cpp/src/tests/Selector.cpp
+++ b/qpid/cpp/src/tests/Selector.cpp
@@ -186,6 +186,9 @@ QPID_AUTO_TEST_CASE(parseStringFail)
BOOST_CHECK_THROW(qb::Selector e("A not 234 escape"), std::range_error);
BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape 'happy'"), std::range_error);
BOOST_CHECK_THROW(qb::Selector e("A not like 'eclecti_' escape happy"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A BETWEEN AND 'true'"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A NOT BETWEEN (X=Y) AND 3.9"), std::range_error);
+ BOOST_CHECK_THROW(qb::Selector e("A NOT BETWEEN 34 OR 3.9"), std::range_error);
}
class TestSelectorEnv : public qpid::broker::SelectorEnv {
@@ -240,6 +243,8 @@ QPID_AUTO_TEST_CASE(parseString)
BOOST_CHECK_NO_THROW(qb::Selector e("A LIKE 'excep%ional'"));
BOOST_CHECK_NO_THROW(qb::Selector e("B NOT LIKE 'excep%ional'"));
BOOST_CHECK_NO_THROW(qb::Selector e("A LIKE 'excep%ional' EScape '\'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A BETWEEN 13 AND 'true'"));
+ BOOST_CHECK_NO_THROW(qb::Selector e("A NOT BETWEEN 100 AND 3.9"));
}
QPID_AUTO_TEST_CASE(simpleEval)
@@ -291,6 +296,8 @@ QPID_AUTO_TEST_CASE(numericEval)
BOOST_CHECK(qb::Selector("B=39.0").eval(env));
BOOST_CHECK(qb::Selector("Not A=17 or B=5.6").eval(env));
BOOST_CHECK(!qb::Selector("A<>17 and B=5.6e17").eval(env));
+ BOOST_CHECK(qb::Selector("A BETWEEN B and 98.5").eval(env));
+ BOOST_CHECK(!qb::Selector("B NOT BETWEEN 35 AND 100").eval(env));
}
QPID_AUTO_TEST_CASE(comparisonEval)