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