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