Merge lp:~jml/launchpad/archive-commercial-rename-support into lp:launchpad

Proposed by Jonathan Lange
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 15368
Proposed branch: lp:~jml/launchpad/archive-commercial-rename-support
Merge into: lp:launchpad
Diff against target: 104 lines (+47/-17)
2 files modified
lib/lp/soyuz/interfaces/archive.py (+0/-8)
lib/lp/soyuz/model/archive.py (+47/-9)
To merge this branch: bzr merge lp:~jml/launchpad/archive-commercial-rename-support
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+107819@code.launchpad.net

Commit message

Allow Archive to work with db column named either suppress_subscription_notifications or commercial. Supports a column rename.

Description of the change

This branch updates the Launchpad code base in preparation for a database patch that will rename Archive.commercial to Archive.suppress_subscription_notifications.

It deletes the commercial field from the class (unused), and changes the suppress_subscription_notifications property to first check with the db whether the 'archive' table has a 'commercial' field. If so, it uses that, if not, it uses the new name.

This approach works because Archive.commercial is never queried on, never used in a WHERE clause.

Other approaches discussed and dismissed were:
  * hack Storm to support this somehow (too hard)
  * add a new column and have that update with triggers; deploy that; change code to always use new column; deploy that; remove old column and triggers (quite cumbersome)

This branch adds 30 lines of code. This are mostly temporary, as they can be deleted after the rename. Further, we are currently 140 lines in credit on this arc of work anyway. Happy to count those toward that total, leaving us at 110.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

Your evil truly knows no bounds.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/soyuz/interfaces/archive.py'
2--- lib/lp/soyuz/interfaces/archive.py 2012-05-22 10:50:19 +0000
3+++ lib/lp/soyuz/interfaces/archive.py 2012-05-29 16:01:24 +0000
4@@ -319,14 +319,6 @@
5 is_main = Bool(
6 title=_("True if archive is a main archive type"), required=False)
7
8- commercial = Bool(
9- title=_("Commercial"),
10- required=True,
11- description=_(
12- "True if the archive is for commercial applications in the "
13- "Ubuntu Software Centre. Governs whether subscribers or "
14- "uploaders get mail from Launchpad about archive events."))
15-
16 suppress_subscription_notifications = exported(
17 Bool(
18 title=_("Suppress subscription notifications"),
19
20=== modified file 'lib/lp/soyuz/model/archive.py'
21--- lib/lp/soyuz/model/archive.py 2012-05-14 01:29:38 +0000
22+++ lib/lp/soyuz/model/archive.py 2012-05-29 16:01:24 +0000
23@@ -78,10 +78,8 @@
24 from lp.services.database.datetimecol import UtcDateTimeCol
25 from lp.services.database.decoratedresultset import DecoratedResultSet
26 from lp.services.database.enumcol import EnumCol
27-from lp.services.database.lpstorm import (
28- ISlaveStore,
29- IStore,
30- )
31+from lp.services.database.lpstorm import ISlaveStore
32+from lp.services.database.postgresql import table_has_column
33 from lp.services.database.sqlbase import (
34 cursor,
35 quote,
36@@ -325,9 +323,6 @@
37 dbName='external_dependencies', notNull=False, default=None,
38 storm_validator=storm_validate_external_dependencies)
39
40- commercial = BoolCol(
41- dbName='commercial', notNull=True, default=False)
42-
43 def _init(self, *args, **kw):
44 """Provide the right interface for URL traversal."""
45 SQLBase._init(self, *args, **kw)
46@@ -340,13 +335,56 @@
47 else:
48 alsoProvides(self, IDistributionArchive)
49
50+ # Here we provide manual properties instead of declaring the column in
51+ # storm. This is to handle a transition period where we rename the
52+ # 'commercial' column to 'suppress_subscription_notifications'. During
53+ # the transition period, the code needs to work with both column names.
54+ #
55+ # The approach taken here only works because we never use 'commercial' in
56+ # a WHERE clause or anything like that.
57+ #
58+ # Once the database change has taken place, these properties should be
59+ # deleted, and replaced with a class variable declaration that looks
60+ # something like:
61+ #
62+ # suppress_subscription_notifications = BoolCol(
63+ # dbName='suppress_subscription_notifications',
64+ # notNull=True, default=False)
65+
66+ def _get_suppress_column_name(self):
67+ """Get the name of the column for suppressing notifications.
68+
69+ Older versions of the database call it 'commercial', newer ones call
70+ it 'suppress_subscription_notifications'.
71+
72+ Works by interrogating PostgreSQL's own records.
73+ """
74+ # Chose this look-before-you-leap implementation so as to avoid
75+ # invalidating the query by forcing a ProgrammingError.
76+ cur = cursor()
77+ has_old_column = table_has_column(cur, 'archive', 'commercial')
78+ if has_old_column:
79+ return 'commercial'
80+ else:
81+ return 'suppress_subscription_notifications'
82+
83 @property
84 def suppress_subscription_notifications(self):
85- return self.commercial
86+ """See `IArchive`."""
87+ store = Store.of(self)
88+ store.flush()
89+ suppress_column = self._get_suppress_column_name()
90+ query = "SELECT %s FROM archive WHERE id=%%s" % (suppress_column,)
91+ return store.execute(query, (self.id,)).get_one()[0]
92
93 @suppress_subscription_notifications.setter
94 def suppress_subscription_notifications(self, suppress):
95- self.commercial = suppress
96+ """See `IArchive`."""
97+ store = Store.of(self)
98+ store.flush()
99+ suppress_column = self._get_suppress_column_name()
100+ query = "UPDATE archive SET %s=%%s WHERE id=%%s" % (suppress_column,)
101+ store.execute(query, (bool(suppress), self.id))
102
103 # Note: You may safely ignore lint when it complains about this
104 # declaration. As of Python 2.6, this is a perfectly valid way