Merge lp:~jtv/launchpad/branchrevision into lp:launchpad/db-devel
- branchrevision
- Merge into db-devel
Status: | Merged |
---|---|
Approved by: | Edwin Grubbs |
Approved revision: | no longer in the source branch. |
Merged at revision: | 9539 |
Proposed branch: | lp:~jtv/launchpad/branchrevision |
Merge into: | lp:launchpad/db-devel |
Diff against target: |
1001 lines (+221/-168) 11 files modified
lib/lp/code/doc/branch.txt (+30/-24) lib/lp/code/doc/revision.txt (+26/-18) lib/lp/code/interfaces/branch.py (+14/-11) lib/lp/code/interfaces/branchrevision.py (+0/-1) lib/lp/code/model/branch.py (+36/-32) lib/lp/code/model/branchmergeproposal.py (+43/-30) lib/lp/code/model/branchrevision.py (+30/-17) lib/lp/code/model/revision.py (+4/-4) lib/lp/code/model/tests/test_branchjob.py (+14/-15) lib/lp/codehosting/scanner/tests/test_bzrsync.py (+19/-12) lib/lp/services/database/prejoin.py (+5/-4) |
To merge this branch: | bzr merge lp:~jtv/launchpad/branchrevision |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Edwin Grubbs (community) | code | Approve | |
Review via email: mp+29791@code.launchpad.net |
Commit message
Stormify BranchRevision.
Description of the change
= Stormify BranchRevision =
This branch is part of an effort to eliminate the id field from BranchRevision. The table is too large and ids are not far from running out. (The alternative is to expand the field and make the table even larger). The branch is against db-devel, since this is part of work that will affect the schema. We probably should consider landing it against devel. But all that happens here is to stormify the class and the queries that mention it.
To test, I ran all unit and doc tests for Code (including browser & model unit tests).
There is a bit of pre-existing lint involving tuples like (foo,) not having a space after the comma. I think the existing spelling is appropriate and I'll discuss possible changes to the checker with Curtis.
Jeroen
Jeroen T. Vermeulen (jtv) wrote : | # |
Thanks for a particularly thoughtful and helpful review.
> >=== modified file 'lib/lp/
> >--- lib/lp/
> >+++ lib/lp/
> >@@ -21,7 +21,7 @@
> > ancestry of a branch. History revisions have an integer sequence, merged
> > revisions have sequence set to None.
> > """
> >-
> >+ # XXX JeroenVermeulen 2010-07-12: We're dropping this column.
>
>
> I assume you are planning on adding a bug for this XXX.
This was really just a transient development note; I've dropped it so that we don't leave unnecessary cruft lying around.
> >=== modified file 'lib/lp/
> >--- lib/lp/
> >+++ lib/lp/
> >+ result = Store.of(
> >+ BranchRevision,
> >+ BranchRevision.
> >+ BranchRevision.
> >+ return result.
>
>
> You can prejoin the revision like this. The Revision will be added
> to the cache, and since BranchRevision has the foreign key, it will
> retrieve the Revision from the cache by id, even though the list
> comprehension appears to throw it away.
I used Stuart's prejoin helper from lp.services.
> >@@ -509,7 +510,7 @@
> >
> > def latest_
> > """See `IBranch`."""
> >- return self.revision_
> >+ return self.revision_
>
>
> Of course, if you use the prejoin above, you will need access to
> the storm query before stripping out the Revision.
Thankfully the prejoin helper takes care of that!
> >@@ -532,14 +533,13 @@
> >
> > def getRevisionsSin
> > """See `IBranch`."""
> >- return BranchRevision.
> >- 'Revision.
> >- 'BranchRevision
> >- 'BranchRevision
> >- 'Revision.
> >- (self.id, quote(timestamp)),
> >- orderBy=
> >- clauseTables=
> >+ result = Store.of(
> >+ BranchRevision,
> >+ Revision == BranchRevision.
> >+ BranchRevision.
> >+ BranchRevision.
> >+ Revision.
> >+ return result.
>
> A prejoin would be good here, too.
Done.
> >@@ -860,8 +860,8 @@
> >
> > def getScannerData(
> > """See `IBranch`."""
> >- cur = cursor()
> >- cur.execute("""
> >+# XXX JeroenVermeulen 2010-07-12: We're dropping BranchRevision.id.
>
>
> This XXX needs that bug number and some indentation.
Removed as above.
> >+ rows = Store.of(
>
>
> Storm queries can retriev...
Preview Diff
1 | === modified file 'lib/lp/code/doc/branch.txt' |
2 | --- lib/lp/code/doc/branch.txt 2010-05-27 02:09:13 +0000 |
3 | +++ lib/lp/code/doc/branch.txt 2010-07-14 16:19:43 +0000 |
4 | @@ -158,10 +158,10 @@ |
5 | |
6 | == Determining the recently changed, registered and imported branches == |
7 | |
8 | -The IBranchSet methods getRecentlyChangedBranches, getRecentlyImportedBranches, |
9 | -and getRecentlyRegisteredBranches are used to give summary information that |
10 | -is to be displayed on the code.launchpad.net page to entice the |
11 | -user to click through. |
12 | +The IBranchSet methods getRecentlyChangedBranches, |
13 | +getRecentlyImportedBranches, and getRecentlyRegisteredBranches are used to |
14 | +give summary information that is to be displayed on the code.launchpad.net |
15 | +page to entice the user to click through. |
16 | |
17 | Changed branches are branches that are owned by real people or groups (as |
18 | opposed to vcs-imports), and have recently had new revisions detected by |
19 | @@ -315,8 +315,8 @@ |
20 | True |
21 | >>> subscription.branch == branch and subscription.person == subscriber |
22 | True |
23 | - >>> subscription.notification_level == BranchSubscriptionNotificationLevel.FULL |
24 | - True |
25 | + >>> print subscription.notification_level.name |
26 | + FULL |
27 | >>> subscription.max_diff_lines == BranchSubscriptionDiffSize.FIVEKLINES |
28 | True |
29 | >>> subscription.review_level == CodeReviewNotificationLevel.FULL |
30 | @@ -327,7 +327,7 @@ |
31 | True |
32 | >>> from canonical.launchpad.webapp import canonical_url |
33 | >>> print canonical_url(subscription) |
34 | - http://code.launchpad.dev/~user/product/subscribed/+subscription/subscriber |
35 | + http://code...dev/~user/product/subscribed/+subscription/subscriber |
36 | |
37 | The settings for a subscription can be changed by re-subscribing. |
38 | |
39 | @@ -399,8 +399,8 @@ |
40 | |
41 | Branch.revision_history gives the sequence of revisions in this branch's |
42 | history, latest revisions first. All revision history items must implement the |
43 | -IBranchRevision interface. The Branch.revision_count attribute gives the length |
44 | -of the revision_history attribute but without building the list. |
45 | +IBranchRevision interface. The Branch.revision_count attribute gives the |
46 | +length of the revision_history attribute but without building the list. |
47 | |
48 | >>> from lp.code.interfaces.branchrevision import IBranchRevision |
49 | >>> junk.revision_count |
50 | @@ -427,11 +427,11 @@ |
51 | True |
52 | |
53 | Branch.getRevisionsSince gives all the BranchRevisions for revisions committed |
54 | -since a given timestamp. It may give surprising results if some committers had a |
55 | -skewed clock. |
56 | +since a given timestamp. It may give surprising results if some committers had |
57 | +a skewed clock. |
58 | |
59 | >>> from datetime import datetime |
60 | - >>> timestamp = datetime(2005, 10, 31, 12, 00, 00) |
61 | + >>> timestamp = datetime(2005, 10, 31, 12, 00, 00, 0, pytz.UTC) |
62 | >>> two_latest = list(junk.revision_history)[:2] |
63 | >>> list(junk.getRevisionsSince(timestamp)) == two_latest |
64 | True |
65 | @@ -440,10 +440,11 @@ |
66 | == Ancestry of Revision == |
67 | |
68 | The revision-history of a given branch, is only one possible ancestry path in |
69 | -the ancestry graph. It is also possible to examine the ancestry graph directly. |
70 | +the ancestry graph. It is also possible to examine the ancestry graph |
71 | +directly. |
72 | |
73 | -A Bazaar branch may contains references (by revision_id) to revisions for which |
74 | -no data is available. Such revisions are called "ghosts". |
75 | +A Bazaar branch may contains references (by revision_id) to revisions for |
76 | +which no data is available. Such revisions are called "ghosts". |
77 | |
78 | Initial commits (after a "bzr init") revisions have no parent. |
79 | |
80 | @@ -452,8 +453,8 @@ |
81 | >>> initial.parent_ids |
82 | [] |
83 | |
84 | -Normal commits (as opposed to merges) have exactly one parent. The first parent |
85 | -of a revision is always the revision that was current when committing. |
86 | +Normal commits (as opposed to merges) have exactly one parent. The first |
87 | +parent of a revision is always the revision that was current when committing. |
88 | |
89 | >>> commit = history[-2].revision |
90 | >>> [type(a) for a in commit.parent_ids] == [unicode] |
91 | @@ -492,8 +493,8 @@ |
92 | is identified by the branch attribute "last_scanned_id". This is the textual |
93 | revision_id for the bzr revision. The reason that it is a text id rather than |
94 | an integer foreign key is so it can easily be compared to the |
95 | -"last_mirrored_id". The "last_mirrored_id" is set by the branch puller, and is |
96 | -used to identify when a scan is needed for a branch. |
97 | +"last_mirrored_id". The "last_mirrored_id" is set by the branch puller, and |
98 | +is used to identify when a scan is needed for a branch. |
99 | |
100 | >>> branch = branch_lookup.get(1) |
101 | >>> branch.last_scanned_id |
102 | @@ -504,7 +505,8 @@ |
103 | >>> branch.getTipRevision() is None |
104 | True |
105 | |
106 | - >>> branch.last_scanned_id = 'test@canonical.com-20051031165248-6f1bb97973c2b4f4' |
107 | + >>> branch.last_scanned_id = ( |
108 | + ... 'test@canonical.com-20051031165248-6f1bb97973c2b4f4') |
109 | >>> rev = branch.getTipRevision() |
110 | >>> print rev.date_created |
111 | 2005-10-31 17:21:47.381770+00:00 |
112 | @@ -560,7 +562,8 @@ |
113 | |
114 | == Deleting branches == |
115 | |
116 | -If a user creates a branch in error, they should be able to remove that branch. |
117 | +If a user creates a branch in error, they should be able to remove that |
118 | +branch. |
119 | |
120 | A branch can be deleted trivially if it is not associated with any bugs or |
121 | blueprints, has no subscribers, and hasn't been associated with any product |
122 | @@ -585,9 +588,12 @@ |
123 | >>> cur = cursor() |
124 | >>> references = list(postgresql.listReferences(cur, 'branch', 'id')) |
125 | |
126 | - >>> for name in sorted([ |
127 | - ... '%s.%s' % (src_tab, src_col) for |
128 | - ... src_tab, src_col, ref_tab, ref_col, updact, delact in references]): |
129 | + >>> listing = sorted([ |
130 | + ... '%s.%s' % (src_tab, src_col) |
131 | + ... for src_tab, src_col, ref_tab, ref_col, updact, delact |
132 | + ... in references |
133 | + ... ]) |
134 | + >>> for name in listing: |
135 | ... print name |
136 | branch.stacked_on |
137 | branchjob.branch |
138 | |
139 | === modified file 'lib/lp/code/doc/revision.txt' |
140 | --- lib/lp/code/doc/revision.txt 2009-06-16 03:31:05 +0000 |
141 | +++ lib/lp/code/doc/revision.txt 2010-07-14 16:19:43 +0000 |
142 | @@ -1,11 +1,12 @@ |
143 | = Bazaar Revisions = |
144 | |
145 | Branches are collection of revisions, and a revision can exist independently |
146 | -from any branch. Revisions are created automatically by scanning branches, they |
147 | -have no creation interface and Launchpad cannot create or modify them. |
148 | +from any branch. Revisions are created automatically by scanning branches, |
149 | +they have no creation interface and Launchpad cannot create or modify them. |
150 | |
151 | == Interfaces == |
152 | |
153 | + >>> from canonical.launchpad.interfaces.lpstorm import IStore |
154 | >>> from canonical.launchpad.webapp.testing import verifyObject |
155 | >>> from lp.code.interfaces.revision import ( |
156 | ... IRevision, IRevisionAuthor, IRevisionParent, IRevisionSet) |
157 | @@ -21,14 +22,16 @@ |
158 | True |
159 | >>> verifyObject(IRevisionSet, RevisionSet()) |
160 | True |
161 | - >>> verifyObject(IBranchRevision, BranchRevision.get(1)) |
162 | + >>> verifyObject( |
163 | + ... IBranchRevision, |
164 | + ... IStore(BranchRevision).find(BranchRevision).any()) |
165 | True |
166 | |
167 | == Creating revisions == |
168 | |
169 | The creator of a revision is identified by a RevisionAuthor. A RevisionAuthor |
170 | -is not a person because that is only an informational attribute, and even if we |
171 | -trust it, there's really no simple way to map that reliably to persons. |
172 | +is not a person because that is only an informational attribute, and even if |
173 | +we trust it, there's really no simple way to map that reliably to persons. |
174 | |
175 | >>> from lp.code.model.revision import RevisionAuthor |
176 | >>> author = RevisionAuthor(name='ddaa@localhost') |
177 | @@ -55,7 +58,8 @@ |
178 | >>> date = datetime(2005, 3, 8, 12, 0, tzinfo=UTC) |
179 | >>> from lp.code.model.revision import Revision |
180 | >>> revision_1 = Revision(log_body=log_body_1, |
181 | - ... revision_author=author, revision_id=revision_id_1, revision_date=date) |
182 | + ... revision_author=author, revision_id=revision_id_1, |
183 | + ... revision_date=date) |
184 | |
185 | == Parents == |
186 | |
187 | @@ -67,7 +71,8 @@ |
188 | we can represent revisions whose at least one parent is a ghost revision. |
189 | |
190 | >>> revision_2 = Revision(log_body=log_body_2, |
191 | - ... revision_author=author, revision_id=revision_id_2, revision_date=date) |
192 | + ... revision_author=author, revision_id=revision_id_2, |
193 | + ... revision_date=date) |
194 | |
195 | >>> from lp.code.model.revision import RevisionParent |
196 | >>> rev2_parent = RevisionParent(sequence=0, revision=revision_2, |
197 | @@ -86,8 +91,10 @@ |
198 | |
199 | BranchRevision rows are created using `Branch.createBranchRevision`. |
200 | |
201 | - >>> rev_no_1 = branch.createBranchRevision(sequence=1, revision=revision_1) |
202 | - >>> rev_no_2 = branch.createBranchRevision(sequence=2, revision=revision_2) |
203 | + >>> rev_no_1 = branch.createBranchRevision( |
204 | + ... sequence=1, revision=revision_1) |
205 | + >>> rev_no_2 = branch.createBranchRevision( |
206 | + ... sequence=2, revision=revision_2) |
207 | >>> rev_no_1.branch == rev_no_2.branch == branch |
208 | True |
209 | |
210 | @@ -96,13 +103,14 @@ |
211 | >>> branch = getUtility(IBranchLookup).getByUniqueName( |
212 | ... '~name12/+junk/junk.contrib') |
213 | |
214 | -The full ancestry of a branch is recorded. That includes the history commits on |
215 | -this branch, but also revisions that were merged into this branch. Such merged |
216 | -revisions are associated to the branch using BranchRevision whose sequence |
217 | -attribute is None. |
218 | +The full ancestry of a branch is recorded. That includes the history commits |
219 | +on this branch, but also revisions that were merged into this branch. Such |
220 | +merged revisions are associated to the branch using BranchRevision whose |
221 | +sequence attribute is None. |
222 | |
223 | >>> from lp.code.model.branchrevision import BranchRevision |
224 | - >>> ancestry = BranchRevision.selectBy(branch=branch) |
225 | + >>> ancestry = IStore(BranchRevision).find( |
226 | + ... BranchRevision, BranchRevision.branch == branch) |
227 | >>> for branch_revision in sorted(ancestry, |
228 | ... key=lambda r:(r.sequence, r.revision.id), reverse=True): |
229 | ... print branch_revision.sequence, branch_revision.revision.id |
230 | @@ -116,16 +124,16 @@ |
231 | None 6 |
232 | |
233 | If you need to operate on the ancestry of a branch, you should write a focused |
234 | -query to avoid creating the tens of thousands of objects necessary to represent |
235 | -the ancestry of a large branch. |
236 | +query to avoid creating the tens of thousands of objects necessary to |
237 | +represent the ancestry of a large branch. |
238 | |
239 | In particular, IBranch.getScannerData efficiently retrieves the BranchRevision |
240 | data needed by the branch-scanner script. |
241 | |
242 | >>> ancestry, history, mapping = branch.getScannerData() |
243 | |
244 | -The first return value is a set of revision_id strings for the full ancestry of |
245 | -the branch. |
246 | +The first return value is a set of revision_id strings for the full ancestry |
247 | +of the branch. |
248 | |
249 | >>> for revision_id in sorted(ancestry): |
250 | ... print revision_id |
251 | |
252 | === modified file 'lib/lp/code/interfaces/branch.py' |
253 | --- lib/lp/code/interfaces/branch.py 2010-07-09 10:22:32 +0000 |
254 | +++ lib/lp/code/interfaces/branch.py 2010-07-14 16:19:43 +0000 |
255 | @@ -463,9 +463,9 @@ |
256 | is set, the branch gets moved into the junk namespace of the branch |
257 | owner. |
258 | |
259 | - :raise: `BranchTargetError` if both project and source_package are set, |
260 | - or if either the project or source_package fail to be adapted to an |
261 | - IBranchTarget. |
262 | + :raise: `BranchTargetError` if both project and source_package are |
263 | + set, or if either the project or source_package fail to be adapted |
264 | + to an `IBranchTarget`. |
265 | """ |
266 | |
267 | reviewer = exported( |
268 | @@ -668,12 +668,14 @@ |
269 | |
270 | # Joins |
271 | revision_history = Attribute( |
272 | - """The sequence of BranchRevision for the mainline of that branch. |
273 | + """The sequence of revisions for the mainline of this branch. |
274 | |
275 | They are ordered with the most recent revision first, and the list |
276 | only contains those in the "leftmost tree", or in other words |
277 | the revisions that match the revision history from bzrlib for this |
278 | branch. |
279 | + |
280 | + The revisions are listed as tuples of (`BranchRevision`, `Revision`). |
281 | """) |
282 | subscriptions = exported( |
283 | CollectionField( |
284 | @@ -703,7 +705,7 @@ |
285 | def destroySelfBreakReferences(): |
286 | """Delete the specified branch. |
287 | |
288 | - BranchRevisions associated with this branch will also be deleted as |
289 | + BranchRevisions associated with this branch will also be deleted as |
290 | well as any items with mandatory references. |
291 | """ |
292 | |
293 | @@ -766,8 +768,7 @@ |
294 | title=_('Commit message'), |
295 | description=_('Message to use when committing this merge.')), |
296 | reviewers=List(value_type=Reference(schema=IPerson)), |
297 | - review_types=List(value_type=TextLine()) |
298 | - ) |
299 | + review_types=List(value_type=TextLine())) |
300 | # target_branch and prerequisite_branch are actually IBranch, patched in |
301 | # _schema_circular_imports. |
302 | @call_with(registrant=REQUEST_USER) |
303 | @@ -782,7 +783,8 @@ |
304 | Both the target_branch and the prerequisite_branch, if it is there, |
305 | must be branches with the same target as the source branch. |
306 | |
307 | - Personal branches (a.k.a. junk branches) cannot specify landing targets. |
308 | + Personal branches (a.k.a. junk branches) cannot specify landing |
309 | + targets. |
310 | """ |
311 | |
312 | def addLandingTarget(registrant, target_branch, prerequisite_branch=None, |
313 | @@ -794,7 +796,8 @@ |
314 | Both the target_branch and the prerequisite_branch, if it is there, |
315 | must be branches with the same target as the source branch. |
316 | |
317 | - Personal branches (a.k.a. junk branches) cannot specify landing targets. |
318 | + Personal branches (a.k.a. junk branches) cannot specify landing |
319 | + targets. |
320 | |
321 | :param registrant: The person who is adding the landing target. |
322 | :param target_branch: Must be another branch, and different to self. |
323 | @@ -949,8 +952,8 @@ |
324 | project is accessible using: |
325 | lp:fooix - the linked object is the product fooix |
326 | lp:fooix/trunk - the linked object is the trunk series of fooix |
327 | - lp:~owner/fooix/name - the unique name of the branch where the linked |
328 | - object is the branch itself. |
329 | + lp:~owner/fooix/name - the unique name of the branch where the |
330 | + linked object is the branch itself. |
331 | """ |
332 | |
333 | # subscription-related methods |
334 | |
335 | === modified file 'lib/lp/code/interfaces/branchrevision.py' |
336 | --- lib/lp/code/interfaces/branchrevision.py 2009-06-25 04:06:00 +0000 |
337 | +++ lib/lp/code/interfaces/branchrevision.py 2010-07-14 16:19:43 +0000 |
338 | @@ -21,7 +21,6 @@ |
339 | ancestry of a branch. History revisions have an integer sequence, merged |
340 | revisions have sequence set to None. |
341 | """ |
342 | - |
343 | id = Int(title=_('The database revision ID')) |
344 | |
345 | sequence = Int( |
346 | |
347 | === modified file 'lib/lp/code/model/branch.py' |
348 | --- lib/lp/code/model/branch.py 2010-07-09 10:22:32 +0000 |
349 | +++ lib/lp/code/model/branch.py 2010-07-14 16:19:43 +0000 |
350 | @@ -34,7 +34,7 @@ |
351 | from canonical.config import config |
352 | from canonical.database.constants import DEFAULT, UTC_NOW |
353 | from canonical.database.sqlbase import ( |
354 | - cursor, quote, SQLBase, sqlvalues) |
355 | + SQLBase, sqlvalues) |
356 | from canonical.database.datetimecol import UtcDateTimeCol |
357 | from canonical.database.enumcol import EnumCol |
358 | |
359 | @@ -79,6 +79,7 @@ |
360 | from lp.codehosting.bzrutils import safe_open |
361 | from lp.registry.interfaces.person import ( |
362 | validate_person_not_private_membership, validate_public_person) |
363 | +from lp.services.database.prejoin import prejoin |
364 | from lp.services.job.interfaces.job import JobStatus |
365 | from lp.services.mail.notificationrecipientset import ( |
366 | NotificationRecipientSet) |
367 | @@ -226,11 +227,13 @@ |
368 | |
369 | @property |
370 | def revision_history(self): |
371 | - return BranchRevision.select(''' |
372 | - BranchRevision.branch = %s AND |
373 | - BranchRevision.sequence IS NOT NULL |
374 | - ''' % sqlvalues(self), |
375 | - prejoins=['revision'], orderBy='-sequence') |
376 | + result = Store.of(self).find( |
377 | + (BranchRevision, Revision), |
378 | + BranchRevision.branch_id == self.id, |
379 | + Revision.id == BranchRevision.revision_id, |
380 | + BranchRevision.sequence != None) |
381 | + result = result.order_by(Desc(BranchRevision.sequence)) |
382 | + return prejoin(result, return_slice=slice(0, 1)) |
383 | |
384 | subscriptions = SQLMultipleJoin( |
385 | 'BranchSubscription', joinColumn='branch', orderBy='id') |
386 | @@ -450,7 +453,7 @@ |
387 | @property |
388 | def code_is_browseable(self): |
389 | """See `IBranch`.""" |
390 | - return (self.revision_count > 0 or self.last_mirrored != None) |
391 | + return (self.revision_count > 0 or self.last_mirrored != None) |
392 | |
393 | def codebrowse_url(self, *extras): |
394 | """See `IBranch`.""" |
395 | @@ -509,7 +512,7 @@ |
396 | |
397 | def latest_revisions(self, quantity=10): |
398 | """See `IBranch`.""" |
399 | - return self.revision_history.limit(quantity) |
400 | + return self.revision_history.config(limit=quantity) |
401 | |
402 | def getMainlineBranchRevisions(self, start_date, end_date=None, |
403 | oldest_first=False): |
404 | @@ -532,14 +535,15 @@ |
405 | |
406 | def getRevisionsSince(self, timestamp): |
407 | """See `IBranch`.""" |
408 | - return BranchRevision.select( |
409 | - 'Revision.id=BranchRevision.revision AND ' |
410 | - 'BranchRevision.branch = %d AND ' |
411 | - 'BranchRevision.sequence IS NOT NULL AND ' |
412 | - 'Revision.revision_date > %s' % |
413 | - (self.id, quote(timestamp)), |
414 | - orderBy='-sequence', |
415 | - clauseTables=['Revision']) |
416 | + result = Store.of(self).find( |
417 | + (BranchRevision, Revision), |
418 | + Revision.id == BranchRevision.revision_id, |
419 | + BranchRevision.branch == self, |
420 | + BranchRevision.sequence != None, |
421 | + Revision.revision_date > timestamp) |
422 | + result = result.order_by(Desc(BranchRevision.sequence)) |
423 | + # Return BranchRevision but prejoin Revision as well. |
424 | + return prejoin(result, slice(0, 1)) |
425 | |
426 | def canBeDeleted(self): |
427 | """See `IBranch`.""" |
428 | @@ -860,19 +864,17 @@ |
429 | |
430 | def getScannerData(self): |
431 | """See `IBranch`.""" |
432 | - cur = cursor() |
433 | - cur.execute(""" |
434 | - SELECT BranchRevision.id, BranchRevision.sequence, |
435 | - Revision.revision_id |
436 | - FROM Revision, BranchRevision |
437 | - WHERE Revision.id = BranchRevision.revision |
438 | - AND BranchRevision.branch = %s |
439 | - ORDER BY BranchRevision.sequence |
440 | - """ % sqlvalues(self)) |
441 | + columns = ( |
442 | + BranchRevision.id, BranchRevision.sequence, Revision.revision_id) |
443 | + rows = Store.of(self).using(Revision, BranchRevision).find( |
444 | + columns, |
445 | + Revision.id == BranchRevision.revision_id, |
446 | + BranchRevision.branch_id == self.id) |
447 | + rows = rows.order_by(BranchRevision.sequence) |
448 | ancestry = set() |
449 | history = [] |
450 | branch_revision_map = {} |
451 | - for branch_revision_id, sequence, revision_id in cur.fetchall(): |
452 | + for branch_revision_id, sequence, revision_id in rows: |
453 | ancestry.add(revision_id) |
454 | branch_revision_map[revision_id] = branch_revision_id |
455 | if sequence is not None: |
456 | @@ -890,19 +892,19 @@ |
457 | prefix = config.launchpad.bzr_imports_root_url |
458 | return urlappend(prefix, '%08x' % self.id) |
459 | else: |
460 | - raise AssertionError("No pull URL for %r" % (self,)) |
461 | + raise AssertionError("No pull URL for %r" % (self, )) |
462 | |
463 | def requestMirror(self): |
464 | """See `IBranch`.""" |
465 | if self.branch_type == BranchType.REMOTE: |
466 | raise BranchTypeError(self.unique_name) |
467 | from canonical.launchpad.interfaces import IStore |
468 | - IStore(self).find( |
469 | + branch = IStore(self).find( |
470 | Branch, |
471 | Branch.id == self.id, |
472 | Or(Branch.next_mirror_time > UTC_NOW, |
473 | - Branch.next_mirror_time == None) |
474 | - ).set(next_mirror_time=UTC_NOW) |
475 | + Branch.next_mirror_time == None)) |
476 | + branch.set(next_mirror_time=UTC_NOW) |
477 | self.next_mirror_time = AutoReload |
478 | return self.next_mirror_time |
479 | |
480 | @@ -997,11 +999,12 @@ |
481 | |
482 | def commitsForDays(self, since): |
483 | """See `IBranch`.""" |
484 | + |
485 | class DateTrunc(NamedFunc): |
486 | name = "date_trunc" |
487 | results = Store.of(self).find( |
488 | (DateTrunc('day', Revision.revision_date), Count(Revision.id)), |
489 | - Revision.id == BranchRevision.revisionID, |
490 | + Revision.id == BranchRevision.revision_id, |
491 | Revision.revision_date > since, |
492 | BranchRevision.branch == self) |
493 | results = results.group_by( |
494 | @@ -1080,6 +1083,7 @@ |
495 | def __init__(self, affected_object, rationale): |
496 | self.affected_object = ProxyFactory(affected_object) |
497 | self.rationale = rationale |
498 | + |
499 | def __call__(self): |
500 | """Perform the deletion operation.""" |
501 | raise NotImplementedError(DeletionOperation.__call__) |
502 | @@ -1161,7 +1165,7 @@ |
503 | |
504 | def __init__(self, code_import): |
505 | DeletionOperation.__init__( |
506 | - self, code_import, _( 'This is the import data for this branch.')) |
507 | + self, code_import, _('This is the import data for this branch.')) |
508 | |
509 | def __call__(self): |
510 | from lp.code.model.codeimport import CodeImportSet |
511 | |
512 | === modified file 'lib/lp/code/model/branchmergeproposal.py' |
513 | --- lib/lp/code/model/branchmergeproposal.py 2010-05-07 04:53:47 +0000 |
514 | +++ lib/lp/code/model/branchmergeproposal.py 2010-07-14 16:19:43 +0000 |
515 | @@ -1,4 +1,4 @@ |
516 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
517 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
518 | # GNU Affero General Public License version 3 (see the file LICENSE). |
519 | |
520 | # pylint: disable-msg=E0611,W0212,F0401 |
521 | @@ -13,13 +13,13 @@ |
522 | ] |
523 | |
524 | from email.Utils import make_msgid |
525 | -from storm.expr import And, Or, Select |
526 | +from storm.expr import And, Desc, Join, LeftJoin, Or, Select |
527 | +from storm.info import ClassAlias |
528 | from storm.store import Store |
529 | from zope.component import getUtility |
530 | from zope.event import notify |
531 | from zope.interface import implements |
532 | |
533 | -from storm.expr import Join, LeftJoin |
534 | from storm.locals import Int, Reference |
535 | from sqlobject import ForeignKey, IntCol, StringCol, SQLMultipleJoin |
536 | |
537 | @@ -74,9 +74,17 @@ |
538 | if dupes.count() > 0: |
539 | return False |
540 | |
541 | - [wip, needs_review, code_approved, rejected, |
542 | - merged, merge_failed, queued, superseded |
543 | - ] = BranchMergeProposalStatus.items |
544 | + [ |
545 | + wip, |
546 | + needs_review, |
547 | + code_approved, |
548 | + rejected, |
549 | + merged, |
550 | + merge_failed, |
551 | + queued, |
552 | + superseded, |
553 | + ] = BranchMergeProposalStatus.items |
554 | + |
555 | # Transitioning to code approved, rejected, failed or queued from |
556 | # work in progress, needs review or merge failed needs the |
557 | # user to be a valid reviewer, other states are fine. |
558 | @@ -88,13 +96,13 @@ |
559 | return False |
560 | # Non-reviewers can toggle within the reviewed ok states |
561 | # (approved/queued/failed): they can dequeue something they spot an |
562 | - # environmental issue with (queued or failed to approved). Retry things |
563 | - # that had an environmental issue (failed or approved to queued) and note |
564 | - # things as failing (approved and queued to failed). |
565 | + # environmental issue with (queued or failed to approved). Retry |
566 | + # things that had an environmental issue (failed or approved to |
567 | + # queued) and note things as failing (approved and queued to failed). |
568 | # This is perhaps more generous than needed, but its not clearly wrong |
569 | - # - a key concern is to prevent non reviewers putting things in the |
570 | - # queue that haven't been oked (and thus moved to approved or one of the |
571 | - # workflow states that approved leads to). |
572 | + # - a key concern is to prevent non reviewers putting things in the |
573 | + # queue that haven't been approved (and thus moved to approved or one |
574 | + # of the workflow states that approved leads to). |
575 | elif (next_state in reviewed_ok_states and |
576 | from_state not in reviewed_ok_states): |
577 | return False |
578 | @@ -154,14 +162,14 @@ |
579 | from lp.code.model.branchmergeproposaljob import ( |
580 | BranchMergeProposalJob, BranchMergeProposalJobFactory, |
581 | BranchMergeProposalJobType) |
582 | - job = Store.of(self).find( |
583 | + jobs = Store.of(self).find( |
584 | BranchMergeProposalJob, |
585 | BranchMergeProposalJob.branch_merge_proposal == self, |
586 | BranchMergeProposalJob.job_type == |
587 | BranchMergeProposalJobType.UPDATE_PREVIEW_DIFF, |
588 | BranchMergeProposalJob.job == Job.id, |
589 | - Job._status.is_in([JobStatus.WAITING, JobStatus.RUNNING]) |
590 | - ).order_by(Job.scheduled_start, Job.date_created).first() |
591 | + Job._status.is_in([JobStatus.WAITING, JobStatus.RUNNING])) |
592 | + job = jobs.order_by(Job.scheduled_start, Job.date_created).first() |
593 | if job is not None: |
594 | return BranchMergeProposalJobFactory.create(job) |
595 | else: |
596 | @@ -486,8 +494,10 @@ |
597 | self.queue_position = None |
598 | |
599 | if merged_revno is not None: |
600 | - branch_revision = BranchRevision.selectOneBy( |
601 | - branch=self.target_branch, sequence=merged_revno) |
602 | + branch_revision = Store.of(self).find( |
603 | + BranchRevision, |
604 | + BranchRevision.branch == self.target_branch, |
605 | + BranchRevision.sequence == merged_revno).one() |
606 | if branch_revision is not None: |
607 | date_merged = branch_revision.revision.revision_date |
608 | |
609 | @@ -579,14 +589,20 @@ |
610 | |
611 | def getUnlandedSourceBranchRevisions(self): |
612 | """See `IBranchMergeProposal`.""" |
613 | - return BranchRevision.select(''' |
614 | - BranchRevision.branch = %s AND |
615 | - BranchRevision.sequence IS NOT NULL AND |
616 | - BranchRevision.revision NOT IN ( |
617 | - SELECT revision FROM BranchRevision |
618 | - WHERE branch = %s) |
619 | - ''' % sqlvalues(self.source_branch, self.target_branch), |
620 | - prejoins=['revision'], orderBy='-sequence', limit=10) |
621 | + store = Store.of(self) |
622 | + SourceRevision = ClassAlias(BranchRevision) |
623 | + TargetRevision = ClassAlias(BranchRevision) |
624 | + target_join = LeftJoin( |
625 | + TargetRevision, And( |
626 | + TargetRevision.revision_id == SourceRevision.revision_id, |
627 | + TargetRevision.branch_id == self.target_branch.id)) |
628 | + origin = [SourceRevision, target_join] |
629 | + result = store.using(*origin).find( |
630 | + SourceRevision, |
631 | + SourceRevision.branch_id == self.source_branch.id, |
632 | + SourceRevision.sequence != None, |
633 | + TargetRevision.id == None) |
634 | + return result.order_by(Desc(SourceRevision.sequence)).config(limit=10) |
635 | |
636 | def createComment(self, owner, subject, content=None, vote=None, |
637 | review_type=None, parent=None, _date_created=DEFAULT, |
638 | @@ -649,9 +665,8 @@ |
639 | CodeReviewVoteReference, |
640 | CodeReviewVoteReference.branch_merge_proposal == self, |
641 | CodeReviewVoteReference.review_type == review_type, |
642 | - CodeReviewVoteReference.comment == None |
643 | - ).order_by(CodeReviewVoteReference.date_created) |
644 | - for ref in refs: |
645 | + CodeReviewVoteReference.comment == None) |
646 | + for ref in refs.order_by(CodeReviewVoteReference.date_created): |
647 | if user.inTeam(ref.reviewer): |
648 | return ref |
649 | return None |
650 | @@ -849,5 +864,3 @@ |
651 | BranchMergeProposal.target_branch = %s AND |
652 | BranchMergeProposal.queue_status NOT IN %s |
653 | """ % sqlvalues(source_branch, target_branch, FINAL_STATES)) |
654 | - |
655 | - |
656 | |
657 | === modified file 'lib/lp/code/model/branchrevision.py' |
658 | --- lib/lp/code/model/branchrevision.py 2009-06-25 04:06:00 +0000 |
659 | +++ lib/lp/code/model/branchrevision.py 2010-07-14 16:19:43 +0000 |
660 | @@ -1,4 +1,4 @@ |
661 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
662 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
663 | # GNU Affero General Public License version 3 (see the file LICENSE). |
664 | |
665 | # pylint: disable-msg=E0611,W0212 |
666 | @@ -8,30 +8,43 @@ |
667 | |
668 | from zope.interface import implements |
669 | |
670 | -from sqlobject import ForeignKey, IntCol |
671 | - |
672 | -from canonical.database.sqlbase import SQLBase |
673 | -from lp.code.interfaces.branchrevision import IBranchRevision, IBranchRevisionSet |
674 | -class BranchRevision(SQLBase): |
675 | - """See IBranchRevision.""" |
676 | +from storm.locals import (Int, Reference, Storm) |
677 | + |
678 | +from canonical.launchpad.interfaces.lpstorm import IMasterStore |
679 | + |
680 | +from lp.code.interfaces.branchrevision import ( |
681 | + IBranchRevision, IBranchRevisionSet) |
682 | + |
683 | + |
684 | +class BranchRevision(Storm): |
685 | + """See `IBranchRevision`.""" |
686 | + __storm_table__ = 'BranchRevision' |
687 | + |
688 | + id = Int(primary=True) |
689 | |
690 | implements(IBranchRevision) |
691 | |
692 | - _table = 'BranchRevision' |
693 | - |
694 | - branch = ForeignKey( |
695 | - dbName='branch', foreignKey='Branch', notNull=True) |
696 | - |
697 | - sequence = IntCol() |
698 | - revision = ForeignKey( |
699 | - dbName='revision', foreignKey='Revision', notNull=True) |
700 | + branch_id = Int(name='branch', allow_none=False) |
701 | + branch = Reference(branch_id, 'Branch.id') |
702 | + |
703 | + revision_id = Int(name='revision', allow_none=False) |
704 | + revision = Reference(revision_id, 'Revision.id') |
705 | + |
706 | + sequence = Int(name='sequence', allow_none=True) |
707 | + |
708 | + def __init__(self, branch, revision, sequence=None): |
709 | + self.branch = branch |
710 | + self.revision = revision |
711 | + self.sequence = sequence |
712 | |
713 | |
714 | class BranchRevisionSet: |
715 | - """See IBranchRevisionSet.""" |
716 | + """See `IBranchRevisionSet`.""" |
717 | |
718 | implements(IBranchRevisionSet) |
719 | |
720 | def delete(self, branch_revision_id): |
721 | """See `IBranchRevisionSet`.""" |
722 | - BranchRevision.delete(branch_revision_id) |
723 | + match = IMasterStore(BranchRevision).find( |
724 | + BranchRevision, BranchRevision.id == branch_revision_id) |
725 | + match.remove() |
726 | |
727 | === modified file 'lib/lp/code/model/revision.py' |
728 | --- lib/lp/code/model/revision.py 2010-04-16 15:06:55 +0000 |
729 | +++ lib/lp/code/model/revision.py 2010-07-14 16:19:43 +0000 |
730 | @@ -111,8 +111,8 @@ |
731 | store = Store.of(self) |
732 | |
733 | query = And( |
734 | - self.id == BranchRevision.revisionID, |
735 | - BranchRevision.branchID == Branch.id) |
736 | + self.id == BranchRevision.revision_id, |
737 | + BranchRevision.branch_id == Branch.id) |
738 | if not allow_private: |
739 | query = And(query, Not(Branch.private)) |
740 | if not allow_junk: |
741 | @@ -123,7 +123,7 @@ |
742 | Or( |
743 | (Branch.product != None), |
744 | And( |
745 | - Branch.sourcepackagename != None, |
746 | + Branch.sourcepackagename != None, |
747 | Branch.distroseries != None))) |
748 | result_set = store.find(Branch, query) |
749 | if self.revision_author.person is None: |
750 | @@ -374,7 +374,7 @@ |
751 | BranchRevision.branch == Branch.id, |
752 | Branch.product == product, |
753 | Branch.lifecycle_status.is_in(DEFAULT_BRANCH_STATUS_IN_LISTING), |
754 | - BranchRevision.revisionID >= revision_subselect) |
755 | + BranchRevision.revision_id >= revision_subselect) |
756 | result_set.config(distinct=True) |
757 | return result_set.order_by(Desc(Revision.revision_date)) |
758 | |
759 | |
760 | === modified file 'lib/lp/code/model/tests/test_branchjob.py' |
761 | --- lib/lp/code/model/tests/test_branchjob.py 2010-06-11 03:52:02 +0000 |
762 | +++ lib/lp/code/model/tests/test_branchjob.py 2010-07-14 16:19:43 +0000 |
763 | @@ -26,6 +26,7 @@ |
764 | |
765 | from canonical.config import config |
766 | from canonical.database.constants import UTC_NOW |
767 | +from canonical.launchpad.interfaces.lpstorm import IMasterStore |
768 | from canonical.launchpad.webapp import canonical_url |
769 | from canonical.launchpad.webapp.testing import verifyObject |
770 | from lp.translations.interfaces.translations import ( |
771 | @@ -113,7 +114,7 @@ |
772 | self.useBzrBranches(direct_database=True) |
773 | |
774 | tree_location = tempfile.mkdtemp() |
775 | - self.addCleanup(lambda: shutil.rmtree(tree_location)) |
776 | + self.addCleanup(lambda: shutil.rmtree(tree_location)) |
777 | |
778 | branch, tree = self.create_branch_and_tree( |
779 | tree_location=tree_location) |
780 | @@ -331,7 +332,7 @@ |
781 | job = RevisionMailJob.create( |
782 | branch, 0, 'from@example.com', 'hello', False, 'subject') |
783 | job.run() |
784 | - (mail,) = pop_notifications() |
785 | + (mail, ) = pop_notifications() |
786 | self.assertEqual('0', mail['X-Launchpad-Branch-Revision-Number']) |
787 | self.assertEqual('from@example.com', mail['from']) |
788 | self.assertEqual('subject', mail['subject']) |
789 | @@ -344,7 +345,7 @@ |
790 | 'To unsubscribe from this branch go to' |
791 | ' %(url)s/+edit-subscription\n' % { |
792 | 'url': canonical_url(branch), |
793 | - 'identity': branch.bzr_identity |
794 | + 'identity': branch.bzr_identity, |
795 | }, |
796 | mail.get_payload(decode=True)) |
797 | |
798 | @@ -454,7 +455,9 @@ |
799 | except bzr_errors.NoSuchRevision: |
800 | revno = None |
801 | if existing is not None: |
802 | - BranchRevision.delete(existing.id) |
803 | + branchrevision = IMasterStore(branch).find( |
804 | + BranchRevision, BranchRevision.id == existing.id) |
805 | + branchrevision.remove() |
806 | branch.createBranchRevision(revno, revision) |
807 | |
808 | def create3CommitsBranch(self): |
809 | @@ -913,7 +916,7 @@ |
810 | self.series = None |
811 | |
812 | def _makeBranchWithTreeAndFile(self, file_name, file_content = None): |
813 | - return self._makeBranchWithTreeAndFiles(((file_name, file_content),)) |
814 | + return self._makeBranchWithTreeAndFiles(((file_name, file_content), )) |
815 | |
816 | def _makeBranchWithTreeAndFiles(self, files): |
817 | """Create a branch with a tree that contains the given files. |
818 | @@ -984,7 +987,7 @@ |
819 | |
820 | def _runJobWithFile(self, import_mode, file_name, file_content = None): |
821 | return self._runJobWithFiles( |
822 | - import_mode, ((file_name, file_content),)) |
823 | + import_mode, ((file_name, file_content), )) |
824 | |
825 | def _runJobWithFiles(self, import_mode, files, |
826 | do_upload_translations=False): |
827 | @@ -1018,8 +1021,7 @@ |
828 | pot_name = "foo.pot" |
829 | entries = self._runJobWithFiles( |
830 | TranslationsBranchImportMode.IMPORT_TEMPLATES, |
831 | - ((pot_name,), ('eo.po',), ('README',)) |
832 | - ) |
833 | + ((pot_name,), ('eo.po',), ('README',))) |
834 | self.assertEqual(len(entries), 1) |
835 | entry = entries[0] |
836 | self.assertEqual(pot_name, entry.path) |
837 | @@ -1058,8 +1060,7 @@ |
838 | pot_name = "en-US.xpi" |
839 | entries = self._runJobWithFiles( |
840 | TranslationsBranchImportMode.IMPORT_TEMPLATES, |
841 | - ((pot_name,), ('eo.xpi',), ('README',)) |
842 | - ) |
843 | + ((pot_name,), ('eo.xpi',), ('README',))) |
844 | self.assertEqual(len(entries), 1) |
845 | entry = entries[0] |
846 | self.assertEqual(pot_name, entry.path) |
847 | @@ -1108,7 +1109,7 @@ |
848 | pot_name = "foo.pot" |
849 | revision_id = self._makeBranchWithTreeAndFiles( |
850 | ((pot_name,), ('eo.po',), ('README',))) |
851 | - self._commitFilesToTree(((pot_name,),)) |
852 | + self._commitFilesToTree(((pot_name, ), )) |
853 | entries = self._runJob( |
854 | TranslationsBranchImportMode.IMPORT_TEMPLATES, revision_id) |
855 | self.assertEqual(len(entries), 1) |
856 | @@ -1120,8 +1121,7 @@ |
857 | # not configured to do so. |
858 | entries = self._runJobWithFiles( |
859 | TranslationsBranchImportMode.NO_IMPORT, |
860 | - (('foo.pot',), ('eo.po',), ('README',)) |
861 | - ) |
862 | + (('foo.pot',), ('eo.po',), ('README',))) |
863 | self.assertEqual([], entries) |
864 | |
865 | def test_upload_translations(self): |
866 | @@ -1183,7 +1183,7 @@ |
867 | # only POTemplate object in the database, if there is only one such |
868 | # object for this product series. |
869 | self._makeBranchWithTreeAndFiles( |
870 | - [('foo.pot', None),('bar.pot', None)]) |
871 | + [('foo.pot', None), ('bar.pot', None)]) |
872 | self._makeProductSeries(TranslationsBranchImportMode.IMPORT_TEMPLATES) |
873 | self.factory.makePOTemplate(self.series, path='foo.pot') |
874 | self.factory.makePOTemplate(self.series, path='bar.pot') |
875 | @@ -1354,6 +1354,5 @@ |
876 | self.assertFalse(os.path.exists(branch_path)) |
877 | |
878 | |
879 | - |
880 | def test_suite(): |
881 | return TestLoader().loadTestsFromName(__name__) |
882 | |
883 | === modified file 'lib/lp/codehosting/scanner/tests/test_bzrsync.py' |
884 | --- lib/lp/codehosting/scanner/tests/test_bzrsync.py 2010-06-16 19:47:27 +0000 |
885 | +++ lib/lp/codehosting/scanner/tests/test_bzrsync.py 2010-07-14 16:19:43 +0000 |
886 | @@ -1,6 +1,6 @@ |
887 | #!/usr/bin/python |
888 | # |
889 | -# Copyright 2009 Canonical Ltd. This software is licensed under the |
890 | +# Copyright 2009-2010 Canonical Ltd. This software is licensed under the |
891 | # GNU Affero General Public License version 3 (see the file LICENSE). |
892 | |
893 | # pylint: disable-msg=W0141 |
894 | @@ -21,6 +21,7 @@ |
895 | from zope.security.proxy import removeSecurityProxy |
896 | |
897 | from canonical.config import config |
898 | +from canonical.launchpad.interfaces.lpstorm import IStore |
899 | from lp.translations.interfaces.translations import ( |
900 | TranslationsBranchImportMode) |
901 | from lp.code.interfaces.branchjob import IRosettaUploadJobSource |
902 | @@ -36,7 +37,9 @@ |
903 | def run_as_db_user(username): |
904 | """Create a decorator that will run a function as the given database user. |
905 | """ |
906 | + |
907 | def _run_with_different_user(f): |
908 | + |
909 | def decorated(*args, **kwargs): |
910 | current_user = LaunchpadZopelessLayer.txn._dbuser |
911 | if current_user == username: |
912 | @@ -47,6 +50,7 @@ |
913 | finally: |
914 | LaunchpadZopelessLayer.switchDbUser(current_user) |
915 | return mergeFunctionMetadata(f, decorated) |
916 | + |
917 | return _run_with_different_user |
918 | |
919 | |
920 | @@ -94,10 +98,12 @@ |
921 | :return: (num_revisions, num_branch_revisions, num_revision_parents, |
922 | num_revision_authors) |
923 | """ |
924 | - return (Revision.select().count(), |
925 | - BranchRevision.select().count(), |
926 | - RevisionParent.select().count(), |
927 | - RevisionAuthor.select().count()) |
928 | + store = IStore(Revision) |
929 | + return ( |
930 | + store.find(Revision).count(), |
931 | + store.find(BranchRevision).count(), |
932 | + store.find(RevisionParent).count(), |
933 | + store.find(RevisionAuthor).count()) |
934 | |
935 | def assertCounts(self, counts, new_revisions=0, new_numbers=0, |
936 | new_parents=0, new_authors=0): |
937 | @@ -228,10 +234,10 @@ |
938 | :return: A set of tuples (sequence, revision-id) for all the |
939 | BranchRevisions rows belonging to self.db_branch. |
940 | """ |
941 | - return set( |
942 | - (branch_revision.sequence, branch_revision.revision.revision_id) |
943 | - for branch_revision |
944 | - in BranchRevision.selectBy(branch=db_branch)) |
945 | + return set(IStore(BranchRevision).find( |
946 | + (BranchRevision.sequence, Revision.revision_id), |
947 | + Revision.id == BranchRevision.revision_id, |
948 | + BranchRevision.branch == db_branch)) |
949 | |
950 | def writeToFile(self, filename="file", contents=None): |
951 | """Set the contents of the specified file. |
952 | @@ -459,8 +465,9 @@ |
953 | # retrieveDatabaseAncestry. |
954 | branch = getUtility(IBranchLookup).getByUniqueName( |
955 | '~name12/+junk/junk.contrib') |
956 | - sampledata = list( |
957 | - BranchRevision.selectBy(branch=branch).orderBy('sequence')) |
958 | + branch_revisions = IStore(BranchRevision).find( |
959 | + BranchRevision, BranchRevision.branch == branch) |
960 | + sampledata = list(branch_revisions.order_by(BranchRevision.sequence)) |
961 | expected_ancestry = set(branch_revision.revision.revision_id |
962 | for branch_revision in sampledata) |
963 | expected_history = [branch_revision.revision.revision_id |
964 | @@ -540,7 +547,7 @@ |
965 | |
966 | def test_upload_on_new_revision_series_not_configured(self): |
967 | # Syncing a branch with a changed tip does not create a |
968 | - # new RosettaUploadJob if the linked product series is not |
969 | + # new RosettaUploadJob if the linked product series is not |
970 | # configured for translation uploads. |
971 | self._makeProductSeries() |
972 | self.commitRevision() |
973 | |
974 | === modified file 'lib/lp/services/database/prejoin.py' |
975 | --- lib/lp/services/database/prejoin.py 2009-10-28 12:11:35 +0000 |
976 | +++ lib/lp/services/database/prejoin.py 2010-07-14 16:19:43 +0000 |
977 | @@ -10,6 +10,7 @@ |
978 | |
979 | from lazr.delegates import delegates |
980 | |
981 | + |
982 | class PrejoinResultSet: |
983 | """Prejoin support. |
984 | |
985 | @@ -21,12 +22,12 @@ |
986 | The preferred solution is support in Storm core, so we can just do |
987 | something like: |
988 | |
989 | - >>> results = store.find(Product).prejoin( |
990 | - ... (Person, EmailAddress), |
991 | - ... Product._ownerID == Person.id, |
992 | - ... EmailAddress.personID == Person.id) |
993 | + # Select Product, but prejoin the owner as well. |
994 | + >>> join = store.find((Product, Person), Product.name == name) |
995 | + >>> results = prejoin(join, slice(0, 1)) |
996 | """ |
997 | delegates(IResultSet, context='result_set') |
998 | + |
999 | def __init__(self, result_set, return_slice=slice(0, 1)): |
1000 | self.result_set = result_set |
1001 | self.return_slice = return_slice |
Hi Jeroen,
This is a nice improvement. Comments below.
-Edwin
>=== modified file 'lib/lp/ code/interfaces /branchrevision .py' code/interfaces /branchrevision .py 2009-06-25 04:06:00 +0000 code/interfaces /branchrevision .py 2010-07-13 13:32:27 +0000
>--- lib/lp/
>+++ lib/lp/
>@@ -21,7 +21,7 @@
> ancestry of a branch. History revisions have an integer sequence, merged
> revisions have sequence set to None.
> """
>-
>+ # XXX JeroenVermeulen 2010-07-12: We're dropping this column.
I assume you are planning on adding a bug for this XXX.
> id = Int(title=_('The database revision ID')) code/model/ branch. py' code/model/ branch. py 2010-07-09 10:22:32 +0000 code/model/ branch. py 2010-07-13 13:32:27 +0000 history( self): select( ''' branch = %s AND sequence IS NOT NULL ['revision' ], orderBy= '-sequence' ) self).find( branch_ id == self.id, sequence != None) order_by( Desc(BranchRevi sion.sequence) )
>
> sequence = Int(
>
>=== modified file 'lib/lp/
>--- lib/lp/
>+++ lib/lp/
>@@ -226,11 +226,12 @@
>
> @property
> def revision_
>- return BranchRevision.
>- BranchRevision.
>- BranchRevision.
>- ''' % sqlvalues(self),
>- prejoins=
>+ # XXX JeroenVermeulen 2010-07-12: Prejoin revision.
>+ result = Store.of(
>+ BranchRevision,
>+ BranchRevision.
>+ BranchRevision.
>+ return result.
You can prejoin the revision like this. The Revision will be added
to the cache, and since BranchRevision has the foreign key, it will
retrieve the Revision from the cache by id, even though the list
comprehension appears to throw it away.
> result = Store.of( self).find( revisionID == Revision.id, branch_ id == self.id, sequence != None) order_by( Desc(BranchRevi sion.sequence) ) tion', joinColumn= 'branch' , orderBy='id') revisions( self, quantity=10): history. limit(quantity) history. config( limit=quantity)
> (BranchRevision, Revision),
> BranchRevision.
> BranchRevision.
> BranchRevision.
> result = result.
> return [branch_revision for branch_revision, branch in result]
>
>
> subscriptions = SQLMultipleJoin(
> 'BranchSubscrip
>@@ -509,7 +510,7 @@
>
> def latest_
> """See `IBranch`."""
>- return self.revision_
>+ return self.revision_
Of course, if you use the prejoin above, you will need access to
the storm query before stripping out the Revision.
> def getMainlineBran chRevisions( self, start_date, end_date=None, first=False) : ce(self, timestamp): select( id=BranchRevisi on.revision AND ' .branch = %d AND ' .sequence IS NOT NULL AND ' revision_ date > %s' % '-sequence' , ['Revision' ]) self).using( Revision) .find( revision,
> oldest_
>@@ -532,14 +533,13 @@
>
> def getRevisionsSin
> """See `IBranch`."""
>- return BranchRevision.
>- 'Revision.
>- 'BranchRevision
>- 'BranchRevision
>- 'Revision.
>- (self.id, quote(timestamp)),
>- orderBy=
>- clauseTables=
>+ result = Store.of(
>+ BranchRevision,
>+ Revision == BranchRevision.
>...