37
37
from loggerhead import history_db_schema as schema
40
class TipNotImported(errors.BzrError):
41
"""Raised when a Branch tip has not been imported.
43
So we can't actually give a valid response.
46
_fmt = ('Branch %(branch)s\'s tip revision %(revision)s has'
49
def __init__(self, branch, revision):
50
errors.BzrError.__init__(self)
52
self.revision = revision
55
40
NULL_PARENTS = (revision.NULL_REVISION,)
1303
1288
return revno[0]
1305
def get_dotted_revno(self, revision_id):
1306
"""Get the dotted revno for a specific revision id."""
1308
cur_tip_revision_id = self._branch_tip_rev_id
1309
while cur_tip_revision_id is not None:
1310
possible_revno = self._get_possible_dotted_revno(
1311
cur_tip_revision_id, revision_id)
1312
if possible_revno is not None:
1313
self._stats['query_time'] += (time.time() - t)
1314
return tuple(map(int, possible_revno.split('.')))
1315
cur_tip_revision_id = self._get_lh_parent_rev_id(
1316
cur_tip_revision_id)
1317
# If we got here, we just don't have an answer
1318
self._stats['query_time'] += (time.time() - t)
1321
def get_dotted_revno_db_ids(self, revision_id):
1322
"""Get the dotted revno, but in-memory use db ids."""
1324
rev_id_to_db_id = {}
1325
db_id_to_rev_id = {}
1326
schema.ensure_revisions(self._get_cursor(),
1327
[revision_id, self._branch_tip_rev_id],
1328
rev_id_to_db_id, db_id_to_rev_id, graph=None)
1329
tip_db_id = rev_id_to_db_id[self._branch_tip_rev_id]
1330
rev_db_id = rev_id_to_db_id[revision_id]
1331
while tip_db_id is not None:
1332
possible_revno = self._get_possible_dotted_revno_db_id(
1333
tip_db_id, rev_db_id)
1334
if possible_revno is not None:
1335
self._stats['query_time'] += (time.time() - t)
1336
return tuple(map(int, possible_revno.split('.')))
1337
tip_db_id = self._get_lh_parent_db_id(tip_db_id)
1338
self._stats['query_time'] += (time.time() - t)
1341
1290
def _get_range_key_and_tail(self, tip_db_id):
1342
1291
"""Return the best range w/ head = tip_db_id or None."""
1343
1292
range_res = self._get_cursor().execute(
1351
1300
return None, tail
1352
1301
return range_res
1354
# Compatibility thunk use get_dotted_revnos instead
1355
def get_dotted_revno_range_multi(self, revision_ids):
1356
"""See get_dotted_revnos"""
1357
return self.get_dotted_revnos(revision_ids)
1359
1303
def get_dotted_revnos(self, revision_ids):
1360
1304
"""Determine the dotted revno, using the range info, etc."""
1361
1305
self.ensure_branch_tip()
1485
1429
self._stats['query_time'] += (time.time() - t)
1486
1430
return revision_to_mainline_map
1489
def walk_mainline(self):
1490
"""Walk the db, and grab all the mainline identifiers."""
1492
cur_id = self._branch_tip_rev_id
1494
while cur_id is not None:
1495
all_ids.append(cur_id)
1496
cur_id = self._get_lh_parent_rev_id(cur_id)
1497
self._stats['query_time'] += (time.time() - t)
1500
def walk_mainline_db_ids(self):
1501
"""Walk the db, and grab all the mainline identifiers."""
1503
db_id = self._get_db_id(self._branch_tip_rev_id)
1505
while db_id is not None:
1506
all_ids.append(db_id)
1507
db_id = self._get_lh_parent_db_id(db_id)
1508
self._stats['query_time'] += (time.time() - t)
1511
1432
def _get_mainline_range_starting_at(self, head_db_id):
1512
1433
"""Try to find a range at this tip.
1547
1468
def walk_ancestry(self):
1548
"""Walk all parents of the given revision."""
1549
remaining = deque([self._branch_tip_rev_id])
1550
all = set(remaining)
1552
next = remaining.popleft()
1553
parents = self._get_cursor().execute("""
1554
SELECT p.revision_id
1555
FROM parent, revision p, revision c
1556
WHERE parent.child = c.db_id
1557
AND parent.parent = p.db_id
1558
AND c.revision_id = ?
1559
""", (next,)).fetchall()
1560
self._stats['num_steps'] += 1
1561
next_parents = [p[0] for p in parents if p[0] not in all]
1562
all.update(next_parents)
1563
remaining.extend(next_parents)
1566
def walk_ancestry_db_ids(self):
1567
all_ancestors = set()
1568
db_id = self._get_db_id(self._branch_tip_rev_id)
1569
all_ancestors.add(db_id)
1572
self._stats['num_steps'] += 1
1573
next = remaining[:100]
1574
remaining = remaining[len(next):]
1575
res = self._get_cursor().execute(_add_n_params(
1576
"SELECT parent FROM parent WHERE child in (%s)",
1578
next_p = [p[0] for p in res if p[0] not in all_ancestors]
1579
all_ancestors.update(next_p)
1580
remaining.extend(next_p)
1581
return all_ancestors
1583
def walk_ancestry_range(self):
1584
"""Walk the whole ancestry.
1586
Use the mainline_parent_range/mainline_parent table to speed things up.
1588
# All we are doing is pre-seeding the search with all the mainline
1589
# revisions, we could probably do more with interleaving calls to
1590
# mainline with calls to parents but this is easier to write :)
1591
all_mainline = self.walk_mainline_using_ranges()
1593
all_ancestors = set(all_mainline)
1594
remaining = list(all_mainline)
1596
self._stats['num_steps'] += 1
1597
next = remaining[:100]
1598
remaining = remaining[len(next):]
1599
res = self._get_cursor().execute(_add_n_params(
1600
"SELECT parent FROM parent WHERE child in (%s)",
1602
next_p = [p[0] for p in res if p[0] not in all_ancestors]
1603
all_ancestors.update(next_p)
1604
remaining.extend(next_p)
1605
self._stats['query_time'] += (time.time() - t)
1606
# Using this shortcut to grab the mainline first helps, but not a lot.
1607
# Probably because the limiting factor is the 'child in (...)' step,
1608
# which is 100 entries or so. (note that setting the range to :1000
1609
# shows a failure, which indicates the old code path was definitely
1610
# capped at a maximum range.)
1611
# 1.719s walk_ancestry
1612
# 0.198s walk_ancestry_db_ids
1613
# 0.164s walk_ancestry_range
1614
return all_ancestors
1616
def walk_ancestry_range_and_dotted(self):
1617
1469
"""Walk the whole ancestry.
1619
1471
Use the information from the dotted_revno table and the mainline_parent