Edgewall Software

Ticket #3752: cache.py

File cache.py, 6.3 KB (added by andreas.l@…, 2 years ago)

Modified cache.py

Line 
1# -*- coding: iso-8859-1 -*-
2#
3# Copyright (C) 2005 Edgewall Software
4# Copyright (C) 2005 Christopher Lenz <cmlenz@gmx.de>
5# All rights reserved.
6#
7# This software is licensed as described in the file COPYING, which
8# you should have received as part of this distribution. The terms
9# are also available at http://trac.edgewall.com/license.html.
10#
11# This software consists of voluntary contributions made by many
12# individuals. For the exact contribution history, see the revision
13# history and logs, available at http://projects.edgewall.com/trac/.
14#
15# Author: Christopher Lenz <cmlenz@gmx.de>
16
17from __future__ import generators
18
19from trac.util import TracError
20from trac.versioncontrol import Changeset, Node, Repository, Authorizer
21
22
23_kindmap = {'D': Node.DIRECTORY, 'F': Node.FILE}
24_actionmap = {'A': Changeset.ADD, 'C': Changeset.COPY,
25              'D': Changeset.DELETE, 'E': Changeset.EDIT,
26              'M': Changeset.MOVE}
27
28
29class CachedRepository(Repository):
30
31    def __init__(self, db, repos, authz, log):
32        Repository.__init__(self, repos.name, authz, log)
33        self.db = db
34        self.repos = repos
35        self.synced = 0
36
37    def close(self):
38        self.repos.close()
39
40    def get_changeset(self, rev):
41        if not self.synced:
42            self.sync()
43            self.synced = 1
44        return CachedChangeset(self.repos.normalize_rev(rev), self.db,
45                               self.authz)
46
47    def sync(self):
48        self.log.debug("Checking whether sync with repository is needed")
49        cursor = self.db.cursor()
50
51        # -- repository used for populating the cache
52        cursor.execute("SELECT value FROM system WHERE name='repository_dir'")
53        row = cursor.fetchone()
54        if row:
55            previous_repository_dir = row[0]
56        else: # no 'repository_dir' stored yet, assume everything's OK
57            previous_repository_dir = self.name
58
59        if self.name != previous_repository_dir:
60            raise TracError, ("The 'repository_dir' has changed, "
61                              "a 'trac-admin resync' operation is needed.")
62
63        youngest_stored = self.repos.get_youngest_rev_in_cache(self.db)
64
65        if youngest_stored != str(self.repos.youngest_rev):
66            authz = self.repos.authz
67            self.repos.authz = Authorizer() # remove permission checking
68
69            kindmap = dict(zip(_kindmap.values(), _kindmap.keys()))
70            actionmap = dict(zip(_actionmap.values(), _actionmap.keys()))
71            self.log.info("Syncing with repository (%s to %s)"
72                          % (youngest_stored, self.repos.youngest_rev))
73            if youngest_stored:
74                try:
75                    current_rev = self.repos.next_rev(youngest_stored)
76                except ValueError:
77                    current_rev = int(youngest_stored) + 1 # for scoped repos.
78            else:
79                try:
80                    current_rev = self.repos.oldest_rev
81                    current_rev = self.repos.normalize_rev(current_rev)
82                except TracError:
83                    current_rev = None
84            while current_rev is not None:
85                changeset = self.repos.get_changeset(current_rev)
86                cursor.execute("INSERT INTO revision (rev,time,author,message) "
87                               "VALUES (%s,%s,%s,%s)", (str(current_rev),
88                               changeset.date, changeset.author,
89                               changeset.message))
90                for path,kind,action,base_path,base_rev in changeset.get_changes():
91                    self.log.debug("Caching node change in [%s]: %s"
92                                   % (current_rev, (path, kind, action,
93                                      base_path, base_rev)))
94                    kind = kindmap[kind]
95                    action = actionmap[action]
96                    cursor.execute("INSERT INTO node_change (rev,path,kind,"
97                                   "change,base_path,base_rev) "
98                                   "VALUES (%s,%s,%s,%s,%s,%s)",
99                                   (str(current_rev), path, kind, action,
100                                   base_path, base_rev))
101                current_rev = self.repos.next_rev(current_rev)
102            self.db.commit()
103            self.repos.authz = authz # restore permission checking
104
105    def get_node(self, path, rev=None):
106        return self.repos.get_node(path, rev)
107
108    def has_node(self, path, rev):
109        return self.repos.has_node(path, rev)
110
111    def get_oldest_rev(self):
112        return self.repos.oldest_rev
113
114    def get_youngest_rev(self):
115        return self.repos.youngest_rev
116
117    def previous_rev(self, rev):
118        return self.repos.previous_rev(rev)
119
120    def next_rev(self, rev):
121        return self.repos.next_rev(rev)
122
123    def rev_older_than(self, rev1, rev2):
124        return self.repos.rev_older_than(rev1, rev2)
125
126    def get_path_history(self, path, rev=None, limit=None):
127        return self.repos.get_path_history(path, rev, limit)
128
129    def normalize_path(self, path):
130        return self.repos.normalize_path(path)
131
132    def normalize_rev(self, rev):
133        return self.repos.normalize_rev(rev)
134
135
136class CachedChangeset(Changeset):
137
138    def __init__(self, rev, db, authz):
139        self.db = db
140        self.authz = authz
141        cursor = self.db.cursor()
142        cursor.execute("SELECT time,author,message FROM revision "
143                       "WHERE rev=%s", (rev,))
144        row = cursor.fetchone()
145        if row:
146            date, author, message = row
147            Changeset.__init__(self, rev, message, author, int(date))
148        else:
149            raise TracError, "No changeset %s in the repository" % rev
150
151    def get_changes(self):
152        cursor = self.db.cursor()
153        cursor.execute("SELECT path,kind,change,base_path,base_rev "
154                       "FROM node_change WHERE rev=%s "
155                       "ORDER BY path", (self.rev,))
156        for path, kind, change, base_path, base_rev in cursor:
157            if not self.authz.has_permission(path):
158                # FIXME: what about the base_path?
159                continue
160            kind = _kindmap[kind]
161            change = _actionmap[change]
162            yield path, kind, change, base_path, base_rev