Revision 3b877f08
b/lib/qlang.py | ||
---|---|---|
31 | 31 |
|
32 | 32 |
# Unary operators |
33 | 33 |
OP_NOT = "!" |
34 |
OP_TRUE = "?" |
|
34 | 35 |
|
35 | 36 |
|
36 | 37 |
# Binary operators |
b/lib/query.py | ||
---|---|---|
357 | 357 |
|
358 | 358 |
- C{_OPTYPE_LOGIC}: Callable taking any number of arguments; used by |
359 | 359 |
L{_HandleLogicOp} |
360 |
- C{_OPTYPE_UNARY}: Callable taking exactly one parameter; used by |
|
361 |
L{_HandleUnaryOp} |
|
360 |
- C{_OPTYPE_UNARY}: Always C{None}; details handled by L{_HandleUnaryOp} |
|
362 | 361 |
- C{_OPTYPE_BINARY}: Callable taking exactly two parameters, the left- and |
363 | 362 |
right-hand side of the operator, used by L{_HandleBinaryOp} |
364 | 363 |
|
... | ... | |
369 | 368 |
qlang.OP_AND: (_OPTYPE_LOGIC, compat.all), |
370 | 369 |
|
371 | 370 |
# Unary operators |
372 |
qlang.OP_NOT: (_OPTYPE_UNARY, operator.not_), |
|
371 |
qlang.OP_NOT: (_OPTYPE_UNARY, None), |
|
372 |
qlang.OP_TRUE: (_OPTYPE_UNARY, None), |
|
373 | 373 |
|
374 | 374 |
# Binary operators |
375 | 375 |
qlang.OP_EQUAL: (_OPTYPE_BINARY, _EQUALITY_CHECKS), |
... | ... | |
449 | 449 |
|
450 | 450 |
return handler(hints_cb, level, op, op_data, operands) |
451 | 451 |
|
452 |
def _LookupField(self, name): |
|
453 |
"""Returns a field definition by name. |
|
454 |
|
|
455 |
""" |
|
456 |
try: |
|
457 |
return self._fields[name] |
|
458 |
except KeyError: |
|
459 |
raise errors.ParameterError("Unknown field '%s'" % name) |
|
460 |
|
|
452 | 461 |
def _HandleLogicOp(self, hints_fn, level, op, op_fn, operands): |
453 | 462 |
"""Handles logic operators. |
454 | 463 |
|
... | ... | |
485 | 494 |
@param operands: List of operands |
486 | 495 |
|
487 | 496 |
""" |
497 |
assert op_fn is None |
|
498 |
|
|
488 | 499 |
if hints_fn: |
489 | 500 |
hints_fn(op) |
490 | 501 |
|
... | ... | |
492 | 503 |
raise errors.ParameterError("Unary operator '%s' expects exactly one" |
493 | 504 |
" operand" % op) |
494 | 505 |
|
495 |
return compat.partial(_WrapUnaryOp, op_fn, |
|
496 |
self._Compile(operands[0], level + 1)) |
|
506 |
if op == qlang.OP_TRUE: |
|
507 |
(_, _, _, retrieval_fn) = self._LookupField(operands[0]) |
|
508 |
|
|
509 |
op_fn = operator.truth |
|
510 |
arg = retrieval_fn |
|
511 |
elif op == qlang.OP_NOT: |
|
512 |
op_fn = operator.not_ |
|
513 |
arg = self._Compile(operands[0], level + 1) |
|
514 |
else: |
|
515 |
raise errors.ProgrammerError("Can't handle operator '%s'" % op) |
|
516 |
|
|
517 |
return compat.partial(_WrapUnaryOp, op_fn, arg) |
|
497 | 518 |
|
498 | 519 |
def _HandleBinaryOp(self, hints_fn, level, op, op_data, operands): |
499 | 520 |
"""Handles binary operators. |
... | ... | |
516 | 537 |
raise errors.ParameterError("Invalid binary operator, expected exactly" |
517 | 538 |
" two operands") |
518 | 539 |
|
519 |
try: |
|
520 |
(fdef, datakind, field_flags, retrieval_fn) = self._fields[name] |
|
521 |
except KeyError: |
|
522 |
raise errors.ParameterError("Unknown field '%s'" % name) |
|
540 |
(fdef, datakind, field_flags, retrieval_fn) = self._LookupField(name) |
|
523 | 541 |
|
524 | 542 |
assert fdef.kind != QFT_UNKNOWN |
525 | 543 |
|
b/test/ganeti.query_unittest.py | ||
---|---|---|
1317 | 1317 |
{ "name": "node2", "other": ["x", "y", "bar"], }, |
1318 | 1318 |
{ "name": "node3", "other": "Hello", }, |
1319 | 1319 |
{ "name": "node1", "other": ["a", "b", "foo"], }, |
1320 |
{ "name": "empty", "other": []}, |
|
1320 | 1321 |
] |
1321 | 1322 |
|
1322 | 1323 |
q = query.Query(fielddefs, ["name", "other"], namefield="name", |
... | ... | |
1343 | 1344 |
["node2", ["x", "y", "bar"]], |
1344 | 1345 |
]) |
1345 | 1346 |
|
1347 |
# Boolean test |
|
1348 |
q = query.Query(fielddefs, ["name", "other"], namefield="name", |
|
1349 |
filter_=["?", "other"]) |
|
1350 |
self.assertEqual(q.OldStyleQuery(data), [ |
|
1351 |
["node1", ["a", "b", "foo"]], |
|
1352 |
["node2", ["x", "y", "bar"]], |
|
1353 |
["node3", "Hello"], |
|
1354 |
]) |
|
1355 |
|
|
1356 |
q = query.Query(fielddefs, ["name", "other"], namefield="name", |
|
1357 |
filter_=["!", ["?", "other"]]) |
|
1358 |
self.assertEqual(q.OldStyleQuery(data), [ |
|
1359 |
["empty", []], |
|
1360 |
]) |
|
1361 |
|
|
1346 | 1362 |
def testFilterHostname(self): |
1347 | 1363 |
fielddefs = query._PrepareFieldList([ |
1348 | 1364 |
(query._MakeField("name", "Name", constants.QFT_TEXT, "Name"), |
... | ... | |
1402 | 1418 |
["node2.example.net"], |
1403 | 1419 |
]) |
1404 | 1420 |
|
1421 |
def testFilterBoolean(self): |
|
1422 |
fielddefs = query._PrepareFieldList([ |
|
1423 |
(query._MakeField("name", "Name", constants.QFT_TEXT, "Name"), |
|
1424 |
None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]), |
|
1425 |
(query._MakeField("value", "Value", constants.QFT_BOOL, "Value"), |
|
1426 |
None, 0, lambda ctx, item: item["value"]), |
|
1427 |
], []) |
|
1428 |
|
|
1429 |
data = [ |
|
1430 |
{ "name": "node1", "value": False, }, |
|
1431 |
{ "name": "node2", "value": True, }, |
|
1432 |
{ "name": "node3", "value": True, }, |
|
1433 |
] |
|
1434 |
|
|
1435 |
q = query.Query(fielddefs, ["name", "value"], |
|
1436 |
filter_=["|", ["=", "value", False], |
|
1437 |
["=", "value", True]]) |
|
1438 |
self.assertTrue(q.RequestedNames() is None) |
|
1439 |
self.assertEqual(q.Query(data), [ |
|
1440 |
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)], |
|
1441 |
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)], |
|
1442 |
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)], |
|
1443 |
]) |
|
1444 |
|
|
1445 |
q = query.Query(fielddefs, ["name", "value"], |
|
1446 |
filter_=["|", ["=", "value", False], |
|
1447 |
["!", ["=", "value", False]]]) |
|
1448 |
self.assertTrue(q.RequestedNames() is None) |
|
1449 |
self.assertEqual(q.Query(data), [ |
|
1450 |
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)], |
|
1451 |
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)], |
|
1452 |
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)], |
|
1453 |
]) |
|
1454 |
|
|
1455 |
# Comparing bool with string |
|
1456 |
for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]: |
|
1457 |
self.assertRaises(errors.ParameterError, query.Query, |
|
1458 |
fielddefs, ["name", "value"], |
|
1459 |
filter_=["=", "value", i]) |
|
1460 |
|
|
1461 |
# Truth filter |
|
1462 |
q = query.Query(fielddefs, ["name", "value"], filter_=["?", "value"]) |
|
1463 |
self.assertTrue(q.RequestedNames() is None) |
|
1464 |
self.assertEqual(q.Query(data), [ |
|
1465 |
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)], |
|
1466 |
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)], |
|
1467 |
]) |
|
1468 |
|
|
1469 |
# Negative bool filter |
|
1470 |
q = query.Query(fielddefs, ["name", "value"], filter_=["!", ["?", "value"]]) |
|
1471 |
self.assertTrue(q.RequestedNames() is None) |
|
1472 |
self.assertEqual(q.Query(data), [ |
|
1473 |
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)], |
|
1474 |
]) |
|
1475 |
|
|
1476 |
# Complex truth filter |
|
1477 |
q = query.Query(fielddefs, ["name", "value"], |
|
1478 |
filter_=["|", ["&", ["=", "name", "node1"], |
|
1479 |
["!", ["?", "value"]]], |
|
1480 |
["?", "value"]]) |
|
1481 |
self.assertTrue(q.RequestedNames() is None) |
|
1482 |
self.assertEqual(q.Query(data), [ |
|
1483 |
[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)], |
|
1484 |
[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)], |
|
1485 |
[(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)], |
|
1486 |
]) |
|
1487 |
|
|
1405 | 1488 |
|
1406 | 1489 |
if __name__ == "__main__": |
1407 | 1490 |
testutils.GanetiTestProgram() |
Also available in: Unified diff