~mnordhoff/loggerhead/history_db

« back to all changes in this revision

Viewing changes to loggerhead/history_db.py

  • Committer: John Arbash Meinel
  • Date: 2010-05-14 10:35:09 UTC
  • mfrom: (0.1.128 trunk)
  • Revision ID: john@arbash-meinel.com-20100514103509-yq300i58cvgwmbyw
Merge in the code cleanups and better handling of NULL.

Show diffs side-by-side

added added

removed removed

Lines of Context:
37
37
from loggerhead import history_db_schema as schema
38
38
 
39
39
 
40
 
class TipNotImported(errors.BzrError):
41
 
    """Raised when a Branch tip has not been imported.
42
 
 
43
 
    So we can't actually give a valid response.
44
 
    """
45
 
 
46
 
    _fmt = ('Branch %(branch)s\'s tip revision %(revision)s has'
47
 
            ' not been imported')
48
 
 
49
 
    def __init__(self, branch, revision):
50
 
        errors.BzrError.__init__(self)
51
 
        self.branch = branch
52
 
        self.revision = revision
53
 
 
54
 
 
55
40
NULL_PARENTS = (revision.NULL_REVISION,)
56
41
 
57
42
 
1302
1287
            return None
1303
1288
        return revno[0]
1304
1289
 
1305
 
    def get_dotted_revno(self, revision_id):
1306
 
        """Get the dotted revno for a specific revision id."""
1307
 
        t = time.time()
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)
1319
 
        return None
1320
 
 
1321
 
    def get_dotted_revno_db_ids(self, revision_id):
1322
 
        """Get the dotted revno, but in-memory use db ids."""
1323
 
        t = time.time()
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)
1339
 
        return None
1340
 
 
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
1353
1302
 
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)
1358
 
 
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()
1363
1307
        cursor = self._get_cursor()
1364
1308
        tip_db_id = self._branch_tip_db_id
1365
1309
        if tip_db_id is None:
1366
 
            raise TipNotImported(self._branch, self._branch_tip_rev_id)
 
1310
            return {}
1367
1311
        db_ids = set()
1368
1312
        db_id_to_rev_id = {}
1369
1313
        for rev_id in revision_ids:
1444
1388
        t = time.time()
1445
1389
        tip_db_id = self._branch_tip_db_id
1446
1390
        if tip_db_id is None:
1447
 
            raise TipNotImported(self._branch, self._branch_tip_rev_id)
 
1391
            return {}
1448
1392
        cursor = self._get_cursor()
1449
1393
        db_ids = set()
1450
1394
        db_id_to_rev_id = {}
1485
1429
        self._stats['query_time'] += (time.time() - t)
1486
1430
        return revision_to_mainline_map
1487
1431
 
1488
 
 
1489
 
    def walk_mainline(self):
1490
 
        """Walk the db, and grab all the mainline identifiers."""
1491
 
        t = time.time()
1492
 
        cur_id = self._branch_tip_rev_id
1493
 
        all_ids = []
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)
1498
 
        return all_ids
1499
 
 
1500
 
    def walk_mainline_db_ids(self):
1501
 
        """Walk the db, and grab all the mainline identifiers."""
1502
 
        t = time.time()
1503
 
        db_id = self._get_db_id(self._branch_tip_rev_id)
1504
 
        all_ids = []
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)
1509
 
        return all_ids
1510
 
 
1511
1432
    def _get_mainline_range_starting_at(self, head_db_id):
1512
1433
        """Try to find a range at this tip.
1513
1434
 
1527
1448
        db_ids = [r[0] for r in range_db_ids]
1528
1449
        return db_ids, next_db_id
1529
1450
 
1530
 
    def walk_mainline_using_ranges(self):
 
1451
    def walk_mainline(self):
1531
1452
        t = time.time()
1532
1453
        db_id = self._get_db_id(self._branch_tip_rev_id)
1533
1454
        all_ids = []
1545
1466
        return all_ids
1546
1467
 
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)
1551
 
        while 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)
1564
 
        return all
1565
 
 
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)
1570
 
        remaining = [db_id]
1571
 
        while remaining:
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)",
1577
 
                len(db_ids)), next)
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
1582
 
 
1583
 
    def walk_ancestry_range(self):
1584
 
        """Walk the whole ancestry.
1585
 
        
1586
 
        Use the mainline_parent_range/mainline_parent table to speed things up.
1587
 
        """
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()
1592
 
        t = time.time()
1593
 
        all_ancestors = set(all_mainline)
1594
 
        remaining = list(all_mainline)
1595
 
        while remaining:
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)",
1601
 
                len(next)), next)
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
1615
 
 
1616
 
    def walk_ancestry_range_and_dotted(self):
1617
1469
        """Walk the whole ancestry.
1618
1470
 
1619
1471
        Use the information from the dotted_revno table and the mainline_parent
1744
1596
        t = time.time()
1745
1597
        tip_db_id = self._branch_tip_db_id
1746
1598
        if tip_db_id is None:
1747
 
            raise TipNotImported(self._branch, self._branch_tip_rev_id)
 
1599
            return []
1748
1600
        if start_revision_id is not None:
1749
1601
            start_db_id = self._get_db_id(start_revision_id)
1750
1602
        else: