Commit 23d0a608 authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

query: Add implementation of regex match operator



So far this operator was not implemented. This patch adds an additional
value preparation function to the function table for binary operators,
used to compile the regular expression. Unittests are included.
Signed-off-by: default avatarMichael Hanselmann <hansmi@google.com>
Reviewed-by: default avatarRené Nussbaumer <rn@google.com>
parent a7761c12
......@@ -322,6 +322,16 @@ def _WrapNot(fn, lhs, rhs):
return not fn(lhs, rhs)
def _PrepareRegex(pattern):
"""Compiles a regular expression.
"""
try:
return re.compile(pattern)
except re.error, err:
raise errors.ParameterError("Invalid regex pattern (%s)" % err)
class _FilterCompilerHelper:
"""Converts a query filter to a callable usable for filtering.
......@@ -349,8 +359,9 @@ class _FilterCompilerHelper:
_EQUALITY_CHECKS = [
(QFF_HOSTNAME,
lambda lhs, rhs: utils.MatchNameComponent(rhs, [lhs],
case_sensitive=False)),
(None, operator.eq),
case_sensitive=False),
None),
(None, operator.eq, None),
]
"""Known operators
......@@ -377,12 +388,14 @@ class _FilterCompilerHelper:
# Binary operators
qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS),
qlang.OP_NOT_EQUAL:
(_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn))
for (flags, fn) in _EQUALITY_CHECKS]),
(_OPTYPE_BINARY, [(flags, compat.partial(_WrapNot, fn), valprepfn)
for (flags, fn, valprepfn) in _EQUALITY_CHECKS]),
qlang.OP_GLOB: (_OPTYPE_BINARY, NotImplemented),
qlang.OP_REGEXP: (_OPTYPE_BINARY, NotImplemented),
qlang.OP_REGEXP: (_OPTYPE_BINARY, [
(None, lambda lhs, rhs: rhs.search(lhs), _PrepareRegex),
]),
qlang.OP_CONTAINS: (_OPTYPE_BINARY, [
(None, operator.contains),
(None, operator.contains, None),
]),
}
......@@ -556,8 +569,12 @@ class _FilterCompilerHelper:
if hints_fn:
hints_fn(op, datakind, name, value)
for (fn_flags, fn) in op_data:
for (fn_flags, fn, valprepfn) in op_data:
if fn_flags is None or fn_flags & field_flags:
# Prepare value if necessary (e.g. compile regular expression)
if valprepfn:
value = valprepfn(value)
return compat.partial(_WrapBinaryOp, fn, retrieval_fn, value)
raise errors.ProgrammerError("Unable to find operator implementation"
......
......@@ -1537,6 +1537,64 @@ class TestQueryFilter(unittest.TestCase):
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
])
def testFilterRegex(self):
fielddefs = query._PrepareFieldList([
(query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
None, 0, lambda ctx, item: item["name"]),
], [])
data = [
{ "name": "node1.example.com", },
{ "name": "node2.site.example.com", },
{ "name": "node2.example.net", },
# Empty name
{ "name": "", },
]
q = query.Query(fielddefs, ["name"], namefield="name",
filter_=["=~", "name", "site"])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node2.site.example.com")],
])
q = query.Query(fielddefs, ["name"], namefield="name",
filter_=["=~", "name", "^node2"])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node2.example.net")],
[(constants.RS_NORMAL, "node2.site.example.com")],
])
q = query.Query(fielddefs, ["name"], namefield="name",
filter_=["=~", "name", r"(?i)\.COM$"])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1.example.com")],
[(constants.RS_NORMAL, "node2.site.example.com")],
])
q = query.Query(fielddefs, ["name"], namefield="name",
filter_=["=~", "name", r"."])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "node1.example.com")],
[(constants.RS_NORMAL, "node2.example.net")],
[(constants.RS_NORMAL, "node2.site.example.com")],
])
q = query.Query(fielddefs, ["name"], namefield="name",
filter_=["=~", "name", r"^$"])
self.assertTrue(q.RequestedNames() is None)
self.assertEqual(q.Query(data), [
[(constants.RS_NORMAL, "")],
])
# Invalid regular expression
self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
filter_=["=~", "name", r"["])
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