Revision fbc263a9

b/lib/cmdlib.py
464 464
    self.requested_data = self.query.RequestedData()
465 465
    self.names = self.query.RequestedNames()
466 466

  
467
    # Sort only if no names were requested
468
    self.sort_by_name = not self.names
469

  
467 470
    self.do_locking = None
468 471
    self.wanted = None
469 472

  
......
530 533
    """Collect data and execute query.
531 534

  
532 535
    """
533
    return query.GetQueryResponse(self.query, self._GetQueryData(lu))
536
    return query.GetQueryResponse(self.query, self._GetQueryData(lu),
537
                                  sort_by_name=self.sort_by_name)
534 538

  
535 539
  def OldStyleQuery(self, lu):
536 540
    """Collect data and execute query.
537 541

  
538 542
    """
539
    return self.query.OldStyleQuery(self._GetQueryData(lu))
543
    return self.query.OldStyleQuery(self._GetQueryData(lu),
544
                                    sort_by_name=self.sort_by_name)
540 545

  
541 546

  
542 547
def _GetWantedNodes(lu, nodes):
b/lib/query.py
629 629
    """
630 630
    return GetAllFields(self._fields)
631 631

  
632
  def Query(self, ctx):
632
  def Query(self, ctx, sort_by_name=True):
633 633
    """Execute a query.
634 634

  
635 635
    @param ctx: Data container passed to field retrieval functions, must
636 636
      support iteration using C{__iter__}
637
    @type sort_by_name: boolean
638
    @param sort_by_name: Whether to sort by name or keep the input data's
639
      ordering
637 640

  
638 641
    """
642
    sort = (self._name_fn and sort_by_name)
643

  
639 644
    result = []
640 645

  
641 646
    for idx, item in enumerate(ctx):
......
648 653
      if __debug__:
649 654
        _VerifyResultRow(self._fields, row)
650 655

  
651
      if self._name_fn:
656
      if sort:
652 657
        (status, name) = _ProcessResult(self._name_fn(ctx, item))
653 658
        assert status == constants.RS_NORMAL
654 659
        # TODO: Are there cases where we wouldn't want to use NiceSort?
655
        sortname = utils.NiceSortKey(name)
660
        result.append((utils.NiceSortKey(name), idx, row))
656 661
      else:
657
        sortname = None
662
        result.append(row)
658 663

  
659
      result.append((sortname, idx, row))
664
    if not sort:
665
      return result
660 666

  
661 667
    # TODO: Would "heapq" be more efficient than sorting?
662 668

  
......
667 673

  
668 674
    return map(operator.itemgetter(2), result)
669 675

  
670
  def OldStyleQuery(self, ctx):
676
  def OldStyleQuery(self, ctx, sort_by_name=True):
671 677
    """Query with "old" query result format.
672 678

  
673 679
    See L{Query.Query} for arguments.
......
681 687
                                 errors.ECODE_INVAL)
682 688

  
683 689
    return [[value for (_, value) in row]
684
            for row in self.Query(ctx)]
690
            for row in self.Query(ctx, sort_by_name=sort_by_name)]
685 691

  
686 692

  
687 693
def _ProcessResult(value):
......
776 782
  return result
777 783

  
778 784

  
779
def GetQueryResponse(query, ctx):
785
def GetQueryResponse(query, ctx, sort_by_name=True):
780 786
  """Prepares the response for a query.
781 787

  
782 788
  @type query: L{Query}
783 789
  @param ctx: Data container, see L{Query.Query}
790
  @type sort_by_name: boolean
791
  @param sort_by_name: Whether to sort by name or keep the input data's
792
    ordering
784 793

  
785 794
  """
786
  return objects.QueryResponse(data=query.Query(ctx),
795
  return objects.QueryResponse(data=query.Query(ctx, sort_by_name=sort_by_name),
787 796
                               fields=query.GetFields()).ToDict()
788 797

  
789 798

  
b/test/ganeti.query_unittest.py
1118 1118
      ["node1", "node44"],
1119 1119
      ])
1120 1120

  
1121
    # Name field, but no sorting, result must be in incoming order
1122
    q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1123
    self.assertFalse(q.RequestedData())
1124
    self.assertEqual(q.Query(data, sort_by_name=False),
1125
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1126
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1127
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1128
       [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1129
    self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1130
      ["node1", "node44"],
1131
      ["node30", "node90"],
1132
      ["node25", "node1"],
1133
      ["node20", "node1"],
1134
      ])
1135
    self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1136
      [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1137
       [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1138
       [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1139
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1140
    self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1141
      ["node20", "node1"],
1142
      ["node25", "node1"],
1143
      ["node30", "node90"],
1144
      ["node1", "node44"],
1145
      ])
1146

  
1147
  def testEqualNamesOrder(self):
1148
    fielddefs = query._PrepareFieldList([
1149
      (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1150
       None, 0, lambda ctx, item: item["pnode"]),
1151
      (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1152
       None, 0, lambda ctx, item: item["num"]),
1153
      ], [])
1154

  
1155
    data = [
1156
      { "pnode": "node1", "num": 100, },
1157
      { "pnode": "node1", "num": 25, },
1158
      { "pnode": "node2", "num": 90, },
1159
      { "pnode": "node2", "num": 30, },
1160
      ]
1161

  
1162
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1163
                    filter_=["|", ["=", "pnode", "node1"],
1164
                                  ["=", "pnode", "node2"],
1165
                                  ["=", "pnode", "node1"]])
1166
    self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1167
                     msg="Did not return unique names")
1168
    self.assertFalse(q.RequestedData())
1169
    self.assertEqual(q.Query(data),
1170
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1171
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1172
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1173
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1174
    self.assertEqual(q.Query(data, sort_by_name=False),
1175
      [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1176
       [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1177
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1178
       [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1179

  
1180
    data = [
1181
      { "pnode": "nodeX", "num": 50, },
1182
      { "pnode": "nodeY", "num": 40, },
1183
      { "pnode": "nodeX", "num": 30, },
1184
      { "pnode": "nodeX", "num": 20, },
1185
      { "pnode": "nodeM", "num": 10, },
1186
      ]
1187

  
1188
    q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1189
                    filter_=["|", ["=", "pnode", "nodeX"],
1190
                                  ["=", "pnode", "nodeY"],
1191
                                  ["=", "pnode", "nodeY"],
1192
                                  ["=", "pnode", "nodeY"],
1193
                                  ["=", "pnode", "nodeM"]])
1194
    self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1195
                     msg="Did not return unique names")
1196
    self.assertFalse(q.RequestedData())
1197

  
1198
    # First sorted by name, then input order
1199
    self.assertEqual(q.Query(data, sort_by_name=True),
1200
      [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1201
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1202
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1203
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1204
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1205

  
1206
    # Input order
1207
    self.assertEqual(q.Query(data, sort_by_name=False),
1208
      [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1209
       [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1210
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1211
       [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1212
       [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1213

  
1121 1214
  def testFilter(self):
1122 1215
    (DK_A, DK_B) = range(1000, 1002)
1123 1216

  

Also available in: Unified diff