~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/index.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2008-06-05 04:05:05 UTC
  • mfrom: (3473.1.1 ianc-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20080605040505-i9kqxg2fps2qjdi0
Add the 'alias' command (Tim Penhey)

Show diffs side-by-side

added added

removed removed

Lines of Context:
27
27
from bisect import bisect_right
28
28
from cStringIO import StringIO
29
29
import re
30
 
import sys
31
30
 
32
31
from bzrlib.lazy_import import lazy_import
33
32
lazy_import(globals(), """
81
80
        """
82
81
        self.reference_lists = reference_lists
83
82
        self._keys = set()
84
 
        # A dict of {key: (absent, ref_lists, value)}
85
83
        self._nodes = {}
86
 
        self._nodes_by_key = None
 
84
        self._nodes_by_key = {}
87
85
        self._key_length = key_elements
88
 
        self._optimize_for_size = False
89
86
 
90
87
    def _check_key(self, key):
91
88
        """Raise BadIndexKey if key is not a valid key for this index."""
97
94
            if not element or _whitespace_re.search(element) is not None:
98
95
                raise errors.BadIndexKey(element)
99
96
 
100
 
    def _get_nodes_by_key(self):
101
 
        if self._nodes_by_key is None:
102
 
            nodes_by_key = {}
103
 
            if self.reference_lists:
104
 
                for key, (absent, references, value) in self._nodes.iteritems():
105
 
                    if absent:
106
 
                        continue
107
 
                    key_dict = nodes_by_key
108
 
                    for subkey in key[:-1]:
109
 
                        key_dict = key_dict.setdefault(subkey, {})
110
 
                    key_dict[key[-1]] = key, value, references
111
 
            else:
112
 
                for key, (absent, references, value) in self._nodes.iteritems():
113
 
                    if absent:
114
 
                        continue
115
 
                    key_dict = nodes_by_key
116
 
                    for subkey in key[:-1]:
117
 
                        key_dict = key_dict.setdefault(subkey, {})
118
 
                    key_dict[key[-1]] = key, value
119
 
            self._nodes_by_key = nodes_by_key
120
 
        return self._nodes_by_key
121
 
 
122
 
    def _update_nodes_by_key(self, key, value, node_refs):
123
 
        """Update the _nodes_by_key dict with a new key.
124
 
 
125
 
        For a key of (foo, bar, baz) create
126
 
        _nodes_by_key[foo][bar][baz] = key_value
127
 
        """
128
 
        if self._nodes_by_key is None:
129
 
            return
130
 
        key_dict = self._nodes_by_key
131
 
        if self.reference_lists:
132
 
            key_value = key, value, node_refs
133
 
        else:
134
 
            key_value = key, value
135
 
        for subkey in key[:-1]:
136
 
            key_dict = key_dict.setdefault(subkey, {})
137
 
        key_dict[key[-1]] = key_value
138
 
 
139
 
    def _check_key_ref_value(self, key, references, value):
140
 
        """Check that 'key' and 'references' are all valid.
141
 
 
142
 
        :param key: A key tuple. Must conform to the key interface (be a tuple,
143
 
            be of the right length, not have any whitespace or nulls in any key
144
 
            element.)
145
 
        :param references: An iterable of reference lists. Something like
146
 
            [[(ref, key)], [(ref, key), (other, key)]]
147
 
        :param value: The value associate with this key. Must not contain
148
 
            newlines or null characters.
149
 
        :return: (node_refs, absent_references)
150
 
            node_refs   basically a packed form of 'references' where all
151
 
                        iterables are tuples
152
 
            absent_references   reference keys that are not in self._nodes.
153
 
                                This may contain duplicates if the same key is
154
 
                                referenced in multiple lists.
 
97
    def add_node(self, key, value, references=()):
 
98
        """Add a node to the index.
 
99
 
 
100
        :param key: The key. keys are non-empty tuples containing
 
101
            as many whitespace-free utf8 bytestrings as the key length
 
102
            defined for this index.
 
103
        :param references: An iterable of iterables of keys. Each is a
 
104
            reference to another key.
 
105
        :param value: The value to associate with the key. It may be any
 
106
            bytes as long as it does not contain \0 or \n.
155
107
        """
156
108
        self._check_key(key)
157
109
        if _newline_null_re.search(value) is not None:
159
111
        if len(references) != self.reference_lists:
160
112
            raise errors.BadIndexValue(references)
161
113
        node_refs = []
162
 
        absent_references = []
163
114
        for reference_list in references:
164
115
            for reference in reference_list:
165
 
                # If reference *is* in self._nodes, then we know it has already
166
 
                # been checked.
 
116
                self._check_key(reference)
167
117
                if reference not in self._nodes:
168
 
                    self._check_key(reference)
169
 
                    absent_references.append(reference)
 
118
                    self._nodes[reference] = ('a', (), '')
170
119
            node_refs.append(tuple(reference_list))
171
 
        return tuple(node_refs), absent_references
172
 
 
173
 
    def add_node(self, key, value, references=()):
174
 
        """Add a node to the index.
175
 
 
176
 
        :param key: The key. keys are non-empty tuples containing
177
 
            as many whitespace-free utf8 bytestrings as the key length
178
 
            defined for this index.
179
 
        :param references: An iterable of iterables of keys. Each is a
180
 
            reference to another key.
181
 
        :param value: The value to associate with the key. It may be any
182
 
            bytes as long as it does not contain \0 or \n.
183
 
        """
184
 
        (node_refs,
185
 
         absent_references) = self._check_key_ref_value(key, references, value)
186
 
        if key in self._nodes and self._nodes[key][0] != 'a':
 
120
        if key in self._nodes and self._nodes[key][0] == '':
187
121
            raise errors.BadIndexDuplicateKey(key, self)
188
 
        for reference in absent_references:
189
 
            # There may be duplicates, but I don't think it is worth worrying
190
 
            # about
191
 
            self._nodes[reference] = ('a', (), '')
192
 
        self._nodes[key] = ('', node_refs, value)
 
122
        self._nodes[key] = ('', tuple(node_refs), value)
193
123
        self._keys.add(key)
194
 
        if self._nodes_by_key is not None and self._key_length > 1:
195
 
            self._update_nodes_by_key(key, value, node_refs)
 
124
        if self._key_length > 1:
 
125
            key_dict = self._nodes_by_key
 
126
            if self.reference_lists:
 
127
                key_value = key, value, tuple(node_refs)
 
128
            else:
 
129
                key_value = key, value
 
130
            # possibly should do this on-demand, but it seems likely it is 
 
131
            # always wanted
 
132
            # For a key of (foo, bar, baz) create
 
133
            # _nodes_by_key[foo][bar][baz] = key_value
 
134
            for subkey in key[:-1]:
 
135
                key_dict = key_dict.setdefault(subkey, {})
 
136
            key_dict[key[-1]] = key_value
196
137
 
197
138
    def finish(self):
198
139
        lines = [_SIGNATURE]
201
142
        lines.append(_OPTION_LEN + str(len(self._keys)) + '\n')
202
143
        prefix_length = sum(len(x) for x in lines)
203
144
        # references are byte offsets. To avoid having to do nasty
204
 
        # polynomial work to resolve offsets (references to later in the
 
145
        # polynomial work to resolve offsets (references to later in the 
205
146
        # file cannot be determined until all the inbetween references have
206
147
        # been calculated too) we pad the offsets with 0's to make them be
207
148
        # of consistent length. Using binary offsets would break the trivial
278
219
            raise errors.BzrError('Failed index creation. Internal error:'
279
220
                ' mismatched output length and expected length: %d %d' %
280
221
                (len(result.getvalue()), expected_bytes))
281
 
        return result
282
 
 
283
 
    def set_optimize(self, for_size=True):
284
 
        """Change how the builder tries to optimize the result.
285
 
 
286
 
        :param for_size: Tell the builder to try and make the index as small as
287
 
            possible.
288
 
        :return: None
289
 
        """
290
 
        # GraphIndexBuilder itself doesn't pay attention to the flag yet, but
291
 
        # other builders do.
292
 
        self._optimize_for_size = for_size
 
222
        return StringIO(''.join(lines))
293
223
 
294
224
 
295
225
class GraphIndex(object):
342
272
        self._keys_by_offset = None
343
273
        self._nodes_by_key = None
344
274
        self._size = size
345
 
        # The number of bytes we've read so far in trying to process this file
346
 
        self._bytes_read = 0
347
275
 
348
276
    def __eq__(self, other):
349
277
        """Equal when self and other were created with the same parameters."""
356
284
    def __ne__(self, other):
357
285
        return not self.__eq__(other)
358
286
 
359
 
    def __repr__(self):
360
 
        return "%s(%r)" % (self.__class__.__name__,
361
 
            self._transport.abspath(self._name))
362
 
 
363
 
    def _buffer_all(self, stream=None):
 
287
    def _buffer_all(self):
364
288
        """Buffer all the index data.
365
289
 
366
290
        Mutates self._nodes and self.keys_by_offset.
367
291
        """
368
 
        if self._nodes is not None:
369
 
            # We already did this
370
 
            return
371
292
        if 'index' in debug.debug_flags:
372
293
            mutter('Reading entire index %s', self._transport.abspath(self._name))
373
 
        if stream is None:
374
 
            stream = self._transport.get(self._name)
 
294
        stream = self._transport.get(self._name)
375
295
        self._read_prefix(stream)
376
296
        self._expected_elements = 3 + self._key_length
377
297
        line_count = 0
379
299
        self._keys_by_offset = {}
380
300
        # ready-to-return key:value or key:value, node_ref_lists
381
301
        self._nodes = {}
382
 
        self._nodes_by_key = None
 
302
        self._nodes_by_key = {}
383
303
        trailers = 0
384
304
        pos = stream.tell()
385
305
        lines = stream.read().split('\n')
394
314
            else:
395
315
                node_value = value
396
316
            self._nodes[key] = node_value
 
317
            if self._key_length > 1:
 
318
                subkey = list(reversed(key[:-1]))
 
319
                key_dict = self._nodes_by_key
 
320
                if self.node_ref_lists:
 
321
                    key_value = key, node_value[0], node_value[1]
 
322
                else:
 
323
                    key_value = key, node_value
 
324
                # possibly should do this on-demand, but it seems likely it is 
 
325
                # always wanted
 
326
                # For a key of (foo, bar, baz) create
 
327
                # _nodes_by_key[foo][bar][baz] = key_value
 
328
                for subkey in key[:-1]:
 
329
                    key_dict = key_dict.setdefault(subkey, {})
 
330
                key_dict[key[-1]] = key_value
397
331
        # cache the keys for quick set intersections
398
332
        self._keys = set(self._nodes)
399
333
        if trailers != 1:
400
334
            # there must be one line - the empty trailer line.
401
335
            raise errors.BadIndexData(self)
402
336
 
403
 
    def _get_nodes_by_key(self):
404
 
        if self._nodes_by_key is None:
405
 
            nodes_by_key = {}
406
 
            if self.node_ref_lists:
407
 
                for key, (value, references) in self._nodes.iteritems():
408
 
                    key_dict = nodes_by_key
409
 
                    for subkey in key[:-1]:
410
 
                        key_dict = key_dict.setdefault(subkey, {})
411
 
                    key_dict[key[-1]] = key, value, references
412
 
            else:
413
 
                for key, value in self._nodes.iteritems():
414
 
                    key_dict = nodes_by_key
415
 
                    for subkey in key[:-1]:
416
 
                        key_dict = key_dict.setdefault(subkey, {})
417
 
                    key_dict[key[-1]] = key, value
418
 
            self._nodes_by_key = nodes_by_key
419
 
        return self._nodes_by_key
420
 
 
421
337
    def iter_all_entries(self):
422
338
        """Iterate over all keys within the index.
423
339
 
548
464
            keys supplied. No additional keys will be returned, and every
549
465
            key supplied that is in the index will be returned.
550
466
        """
 
467
        # PERFORMANCE TODO: parse and bisect all remaining data at some
 
468
        # threshold of total-index processing/get calling layers that expect to
 
469
        # read the entire index to use the iter_all_entries  method instead.
551
470
        keys = set(keys)
552
471
        if not keys:
553
472
            return []
554
473
        if self._size is None and self._nodes is None:
555
474
            self._buffer_all()
556
 
 
557
 
        # We fit about 20 keys per minimum-read (4K), so if we are looking for
558
 
        # more than 1/20th of the index its likely (assuming homogenous key
559
 
        # spread) that we'll read the entire index. If we're going to do that,
560
 
        # buffer the whole thing. A better analysis might take key spread into
561
 
        # account - but B+Tree indices are better anyway.
562
 
        # We could look at all data read, and use a threshold there, which will
563
 
        # trigger on ancestry walks, but that is not yet fully mapped out.
564
 
        if self._nodes is None and len(keys) * 20 > self.key_count():
565
 
            self._buffer_all()
566
475
        if self._nodes is not None:
567
476
            return self._iter_entries_from_total_buffer(keys)
568
477
        else:
610
519
                else:
611
520
                    yield self, key, self._nodes[key]
612
521
            return
613
 
        nodes_by_key = self._get_nodes_by_key()
614
522
        for key in keys:
615
523
            # sanity check
616
524
            if key[0] is None:
618
526
            if len(key) != self._key_length:
619
527
                raise errors.BadIndexKey(key)
620
528
            # find what it refers to:
621
 
            key_dict = nodes_by_key
 
529
            key_dict = self._nodes_by_key
622
530
            elements = list(key)
623
531
            # find the subdict whose contents should be returned.
624
532
            try:
711
619
        if self._bisect_nodes is None:
712
620
            readv_ranges.append(_HEADER_READV)
713
621
        self._read_and_parse(readv_ranges)
714
 
        result = []
715
 
        if self._nodes is not None:
716
 
            # _read_and_parse triggered a _buffer_all because we requested the
717
 
            # whole data range
718
 
            for location, key in location_keys:
719
 
                if key not in self._nodes: # not present
720
 
                    result.append(((location, key), False))
721
 
                elif self.node_ref_lists:
722
 
                    value, refs = self._nodes[key]
723
 
                    result.append(((location, key),
724
 
                        (self, key, value, refs)))
725
 
                else:
726
 
                    result.append(((location, key),
727
 
                        (self, key, self._nodes[key])))
728
 
            return result
729
622
        # generate results:
730
623
        #  - figure out <, >, missing, present
731
624
        #  - result present references so we can return them.
 
625
        result = []
732
626
        # keys that we cannot answer until we resolve references
733
627
        pending_references = []
734
628
        pending_locations = set()
784
678
            if length > 0:
785
679
                readv_ranges.append((location, length))
786
680
        self._read_and_parse(readv_ranges)
787
 
        if self._nodes is not None:
788
 
            # The _read_and_parse triggered a _buffer_all, grab the data and
789
 
            # return it
790
 
            for location, key in pending_references:
791
 
                value, refs = self._nodes[key]
792
 
                result.append(((location, key), (self, key, value, refs)))
793
 
            return result
794
681
        for location, key in pending_references:
795
682
            # answer key references we had to look-up-late.
 
683
            index = self._parsed_key_index(key)
796
684
            value, refs = self._bisect_nodes[key]
797
685
            result.append(((location, key), (self, key,
798
686
                value, self._resolve_references(refs))))
989
877
            elements = line.split('\0')
990
878
            if len(elements) != self._expected_elements:
991
879
                raise errors.BadIndexData(self)
992
 
            # keys are tuples. Each element is a string that may occur many
993
 
            # times, so we intern them to save space. AB, RC, 200807
994
 
            key = tuple([intern(element) for element in elements[:self._key_length]])
 
880
            # keys are tuples
 
881
            key = tuple(elements[:self._key_length])
995
882
            if first_key is None:
996
883
                first_key = key
997
884
            absent, references, value = elements[-3:]
1068
955
 
1069
956
        :param readv_ranges: A prepared readv range list.
1070
957
        """
1071
 
        if not readv_ranges:
1072
 
            return
1073
 
        if self._nodes is None and self._bytes_read * 2 >= self._size:
1074
 
            # We've already read more than 50% of the file and we are about to
1075
 
            # request more data, just _buffer_all() and be done
1076
 
            self._buffer_all()
1077
 
            return
1078
 
 
1079
 
        readv_data = self._transport.readv(self._name, readv_ranges, True,
1080
 
            self._size)
1081
 
        # parse
1082
 
        for offset, data in readv_data:
1083
 
            self._bytes_read += len(data)
1084
 
            if offset == 0 and len(data) == self._size:
1085
 
                # We read the whole range, most likely because the
1086
 
                # Transport upcast our readv ranges into one long request
1087
 
                # for enough total data to grab the whole index.
1088
 
                self._buffer_all(StringIO(data))
1089
 
                return
1090
 
            if self._bisect_nodes is None:
1091
 
                # this must be the start
1092
 
                if not (offset == 0):
1093
 
                    raise AssertionError()
1094
 
                offset, data = self._parse_header_from_bytes(data)
1095
 
            # print readv_ranges, "[%d:%d]" % (offset, offset + len(data))
1096
 
            self._parse_region(offset, data)
 
958
        if readv_ranges:
 
959
            readv_data = self._transport.readv(self._name, readv_ranges, True,
 
960
                self._size)
 
961
            # parse
 
962
            for offset, data in readv_data:
 
963
                if self._bisect_nodes is None:
 
964
                    # this must be the start
 
965
                    if not (offset == 0):
 
966
                        raise AssertionError()
 
967
                    offset, data = self._parse_header_from_bytes(data)
 
968
                # print readv_ranges, "[%d:%d]" % (offset, offset + len(data))
 
969
                self._parse_region(offset, data)
1097
970
 
1098
971
    def _signature(self):
1099
972
        """The file signature for this index type."""
1119
992
    in the index list.
1120
993
    """
1121
994
 
1122
 
    def __init__(self, indices, reload_func=None):
 
995
    def __init__(self, indices):
1123
996
        """Create a CombinedGraphIndex backed by indices.
1124
997
 
1125
998
        :param indices: An ordered list of indices to query for data.
1126
 
        :param reload_func: A function to call if we find we are missing an
1127
 
            index. Should have the form reload_func() => True/False to indicate
1128
 
            if reloading actually changed anything.
1129
999
        """
1130
1000
        self._indices = indices
1131
 
        self._reload_func = reload_func
1132
1001
 
1133
1002
    def __repr__(self):
1134
1003
        return "%s(%s)" % (
1186
1055
            the most efficient order for the index.
1187
1056
        """
1188
1057
        seen_keys = set()
1189
 
        while True:
1190
 
            try:
1191
 
                for index in self._indices:
1192
 
                    for node in index.iter_all_entries():
1193
 
                        if node[1] not in seen_keys:
1194
 
                            yield node
1195
 
                            seen_keys.add(node[1])
1196
 
                return
1197
 
            except errors.NoSuchFile:
1198
 
                self._reload_or_raise()
 
1058
        for index in self._indices:
 
1059
            for node in index.iter_all_entries():
 
1060
                if node[1] not in seen_keys:
 
1061
                    yield node
 
1062
                    seen_keys.add(node[1])
1199
1063
 
1200
1064
    def iter_entries(self, keys):
1201
1065
        """Iterate over keys within the index.
1209
1073
            efficient order for the index.
1210
1074
        """
1211
1075
        keys = set(keys)
1212
 
        while True:
1213
 
            try:
1214
 
                for index in self._indices:
1215
 
                    if not keys:
1216
 
                        return
1217
 
                    for node in index.iter_entries(keys):
1218
 
                        keys.remove(node[1])
1219
 
                        yield node
 
1076
        for index in self._indices:
 
1077
            if not keys:
1220
1078
                return
1221
 
            except errors.NoSuchFile:
1222
 
                self._reload_or_raise()
 
1079
            for node in index.iter_entries(keys):
 
1080
                keys.remove(node[1])
 
1081
                yield node
1223
1082
 
1224
1083
    def iter_entries_prefix(self, keys):
1225
1084
        """Iterate over keys within the index using prefix matching.
1245
1104
        if not keys:
1246
1105
            return
1247
1106
        seen_keys = set()
1248
 
        while True:
1249
 
            try:
1250
 
                for index in self._indices:
1251
 
                    for node in index.iter_entries_prefix(keys):
1252
 
                        if node[1] in seen_keys:
1253
 
                            continue
1254
 
                        seen_keys.add(node[1])
1255
 
                        yield node
1256
 
                return
1257
 
            except errors.NoSuchFile:
1258
 
                self._reload_or_raise()
 
1107
        for index in self._indices:
 
1108
            for node in index.iter_entries_prefix(keys):
 
1109
                if node[1] in seen_keys:
 
1110
                    continue
 
1111
                seen_keys.add(node[1])
 
1112
                yield node
1259
1113
 
1260
1114
    def key_count(self):
1261
1115
        """Return an estimate of the number of keys in this index.
1262
 
 
 
1116
        
1263
1117
        For CombinedGraphIndex this is approximated by the sum of the keys of
1264
1118
        the child indices. As child indices may have duplicate keys this can
1265
1119
        have a maximum error of the number of child indices * largest number of
1266
1120
        keys in any index.
1267
1121
        """
1268
 
        while True:
1269
 
            try:
1270
 
                return sum((index.key_count() for index in self._indices), 0)
1271
 
            except errors.NoSuchFile:
1272
 
                self._reload_or_raise()
1273
 
 
1274
 
    def _reload_or_raise(self):
1275
 
        """We just got a NoSuchFile exception.
1276
 
 
1277
 
        Try to reload the indices, if it fails, just raise the current
1278
 
        exception.
1279
 
        """
1280
 
        if self._reload_func is None:
1281
 
            raise
1282
 
        exc_type, exc_value, exc_traceback = sys.exc_info()
1283
 
        trace.mutter('Trying to reload after getting exception: %s',
1284
 
                     exc_value)
1285
 
        if not self._reload_func():
1286
 
            # We tried to reload, but nothing changed, so we fail anyway
1287
 
            trace.mutter('_reload_func indicated nothing has changed.'
1288
 
                         ' Raising original exception.')
1289
 
            raise exc_type, exc_value, exc_traceback
 
1122
        return sum((index.key_count() for index in self._indices), 0)
1290
1123
 
1291
1124
    def validate(self):
1292
1125
        """Validate that everything in the index can be accessed."""
1293
 
        while True:
1294
 
            try:
1295
 
                for index in self._indices:
1296
 
                    index.validate()
1297
 
                return
1298
 
            except errors.NoSuchFile:
1299
 
                self._reload_or_raise()
 
1126
        for index in self._indices:
 
1127
            index.validate()
1300
1128
 
1301
1129
 
1302
1130
class InMemoryGraphIndex(GraphIndexBuilder):
1395
1223
                else:
1396
1224
                    yield self, key, node[2]
1397
1225
            return
1398
 
        nodes_by_key = self._get_nodes_by_key()
1399
1226
        for key in keys:
1400
1227
            # sanity check
1401
1228
            if key[0] is None:
1403
1230
            if len(key) != self._key_length:
1404
1231
                raise errors.BadIndexKey(key)
1405
1232
            # find what it refers to:
1406
 
            key_dict = nodes_by_key
 
1233
            key_dict = self._nodes_by_key
1407
1234
            elements = list(key)
1408
1235
            # find the subdict to return
1409
1236
            try: