Commit ad48eacc authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

query2: Add <, >, <=, >= comparison operators



These can be used, for example, to get jobs submitted after a certain
timestamp.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarIustin Pop <iustin@google.com>
parent d8960502
......@@ -58,12 +58,16 @@ OP_TRUE = "?"
# operator-specific value
OP_EQUAL = "="
OP_NOT_EQUAL = "!="
OP_LT = "<"
OP_LE = "<="
OP_GT = ">"
OP_GE = ">="
OP_REGEXP = "=~"
OP_CONTAINS = "=[]"
#: Characters used for detecting user-written filters (see L{_CheckFilter})
FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\" + string.whitespace)
FILTER_DETECTION_CHARS = frozenset("()=/!~'\"\\<>" + string.whitespace)
#: Characters used to detect globbing filters (see L{_CheckGlobbing})
GLOB_DETECTION_CHARS = frozenset("*?")
......@@ -165,6 +169,10 @@ def BuildFilterParser():
binopstbl = {
"==": OP_EQUAL,
"!=": OP_NOT_EQUAL,
"<": OP_LT,
"<=": OP_LE,
">": OP_GT,
">=": OP_GE,
}
binary_cond = (field_name + pyp.oneOf(binopstbl.keys()) + rval)
......
......@@ -403,6 +403,18 @@ class _FilterCompilerHelper:
qlang.OP_NOT_EQUAL:
(_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
qlang.OP_LT: (_OPTYPE_BINARY, [
(None, operator.lt, None),
]),
qlang.OP_GT: (_OPTYPE_BINARY, [
(None, operator.gt, None),
]),
qlang.OP_LE: (_OPTYPE_BINARY, [
(None, operator.le, None),
]),
qlang.OP_GE: (_OPTYPE_BINARY, [
(None, operator.ge, None),
]),
qlang.OP_REGEXP: (_OPTYPE_BINARY, [
(None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
]),
......
......@@ -362,7 +362,7 @@ Syntax in pseudo-BNF::
<condition> ::=
{ /* Value comparison */
<field> { == | != } <value>
<field> { == | != | < | <= | >= | > } <value>
/* Collection membership */
| <value> [ not ] in <field>
......@@ -389,6 +389,14 @@ Operators:
Equality
*!=*
Inequality
*<*
Less than
*<=*
Less than or equal
*>*
Greater than
*>=*
Greater than or equal
*=~*
Pattern match using regular expression
*!~*
......
......@@ -147,6 +147,11 @@ class TestParseFilter(unittest.TestCase):
[qlang.OP_NOT, [qlang.OP_REGEXP, "field",
utils.DnsNameGlobPattern("*.example.*")]])
self._Test("ctime < 1234", [qlang.OP_LT, "ctime", 1234])
self._Test("ctime > 1234", [qlang.OP_GT, "ctime", 1234])
self._Test("mtime <= 9999", [qlang.OP_LE, "mtime", 9999])
self._Test("mtime >= 9999", [qlang.OP_GE, "mtime", 9999])
def testAllFields(self):
for name in frozenset(i for d in query.ALL_FIELD_LISTS for i in d.keys()):
self._Test("%s == \"value\"" % name, [qlang.OP_EQUAL, name, "value"])
......@@ -167,6 +172,11 @@ class TestParseFilter(unittest.TestCase):
# Non-matching regexp delimiters
tests.append("name =~ /foobarbaz#")
# Invalid operators
tests.append("name <> value")
tests.append("name => value")
tests.append("name =< value")
for qfilter in tests:
try:
qlang.ParseFilter(qfilter, parser=self.parser)
......
......@@ -1745,6 +1745,38 @@ class TestQueryFilter(unittest.TestCase):
self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
qfilter=["=~", "name", r"["])
def testFilterLessGreater(self):
fielddefs = query._PrepareFieldList([
(query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
None, 0, lambda ctx, item: item),
], [])
data = range(100)
q = query.Query(fielddefs, ["value"],
qfilter=["<", "value", 20])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data),
[[(constants.RS_NORMAL, i)] for i in range(20)])
q = query.Query(fielddefs, ["value"],
qfilter=["<=", "value", 30])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data),
[[(constants.RS_NORMAL, i)] for i in range(31)])
q = query.Query(fielddefs, ["value"],
qfilter=[">", "value", 40])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data),
[[(constants.RS_NORMAL, i)] for i in range(41, 100)])
q = query.Query(fielddefs, ["value"],
qfilter=[">=", "value", 50])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data),
[[(constants.RS_NORMAL, i)] for i in range(50, 100)])
if __name__ == "__main__":
testutils.GanetiTestProgram()
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