Merge lp:~adeuring/launchpad/bug-739052-3 into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 13602
Proposed branch: lp:~adeuring/launchpad/bug-739052-3
Merge into: lp:launchpad
Prerequisite: lp:~adeuring/launchpad/bug-739052-2
Diff against target: 182 lines (+78/-16)
2 files modified
lib/canonical/launchpad/webapp/batching.py (+36/-4)
lib/canonical/launchpad/webapp/tests/test_batching.py (+42/-12)
To merge this branch: bzr merge lp:~adeuring/launchpad/bug-739052-3
Reviewer Review Type Date Requested Status
Robert Collins (community) Approve
Review via email: mp+70272@code.launchpad.net

Commit message

[r=lifeless][no-qa] Use a WHERE clause like (col1, col2,..) < (memo1, memo2,...) if sort columns use only ascending order or or only descending order

Description of the change

StormRangeFactory: This branch implements sort expressions like (col1, col2...) > (memo1, momo2...) if all sort columns use only ascending or only descending order.

./bin/test canonical -vvt canonical.launchpad.webapp.tests.test_batching.TestStormRangeFactory

no lint

To post a comment you must log in.
Revision history for this message
Robert Collins (lifeless) wrote :

Nice incremental step.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/canonical/launchpad/webapp/batching.py'
--- lib/canonical/launchpad/webapp/batching.py 2011-08-03 10:21:35 +0000
+++ lib/canonical/launchpad/webapp/batching.py 2011-08-03 10:21:37 +0000
@@ -11,8 +11,10 @@
11from storm import Undef11from storm import Undef
12from storm.expr import (12from storm.expr import (
13 And,13 And,
14 compile,
14 Desc,15 Desc,
15 Or,16 Or,
17 SQL,
16 )18 )
17from storm.properties import PropertyColumn19from storm.properties import PropertyColumn
18from storm.zope.interfaces import IResultSet20from storm.zope.interfaces import IResultSet
@@ -26,6 +28,7 @@
26 )28 )
2729
28from canonical.config import config30from canonical.config import config
31from canonical.database.sqlbase import sqlvalues
29from canonical.launchpad.components.decoratedresultset import (32from canonical.launchpad.components.decoratedresultset import (
30 DecoratedResultSet,33 DecoratedResultSet,
31 )34 )
@@ -342,7 +345,7 @@
342 return expression345 return expression
343 return [plain_expression(column) == memo for column, memo in limits]346 return [plain_expression(column) == memo for column, memo in limits]
344347
345 def whereExpressions(self, limits):348 def genericWhereExpressions(self, limits):
346 """Generate WHERE expressions for the given sort columns and349 """Generate WHERE expressions for the given sort columns and
347 memos values.350 memos values.
348351
@@ -371,14 +374,43 @@
371 clauses = self.andClausesForLeadingColumns(start)374 clauses = self.andClausesForLeadingColumns(start)
372 clauses.append(last_limit)375 clauses.append(last_limit)
373 clause_for_last_column = reduce(And, clauses)376 clause_for_last_column = reduce(And, clauses)
374 return [clause_for_last_column] + self.whereExpressions(start)377 return (
378 [clause_for_last_column]
379 + self.genericWhereExpressions(start))
375 else:380 else:
376 return [last_limit]381 return [last_limit]
377382
383 def whereExpressions(self, sort_expressions, memos):
384 """WHERE expressions for the given sort columns and memos values."""
385 expression = sort_expressions[0]
386 descending = isinstance(expression, Desc)
387 consistent = True
388 for expression in sort_expressions[1:]:
389 if isinstance(expression, Desc) != descending:
390 consistent = False
391 break
392 if not consistent or len(sort_expressions) == 1:
393 return self.genericWhereExpressions(zip(sort_expressions, memos))
394
395 # If the columns are sorted either only ascending or only
396 # descending, we can specify a single WHERE condition
397 # (col1, col2...) > (memo1, memo2...)
398 if descending:
399 sort_expressions = [
400 expression.expr for expression in sort_expressions]
401 sort_expressions = map(compile, sort_expressions)
402 sort_expressions = ', '.join(sort_expressions)
403 memos = sqlvalues(*memos)
404 memos = ', '.join(memos)
405 if descending:
406 return [SQL('(%s) < (%s)' % (sort_expressions, memos))]
407 else:
408 return [SQL('(%s) > (%s)' % (sort_expressions, memos))]
409
378 def getSliceFromMemo(self, size, memo):410 def getSliceFromMemo(self, size, memo):
379 """Return a result set for the given memo values.411 """Return a result set for the given memo values.
380412
381 Note that at least two other implementatians are possible:413 Note that at least two other implementations are possible:
382 Instead of OR-combining the expressions returned by414 Instead of OR-combining the expressions returned by
383 whereExpressions(), these expressions could be used for415 whereExpressions(), these expressions could be used for
384 separate SELECTs which are then merged with UNION ALL.416 separate SELECTs which are then merged with UNION ALL.
@@ -390,7 +422,7 @@
390 differ between different queries.422 differ between different queries.
391 """423 """
392 sort_expressions = self.getOrderBy()424 sort_expressions = self.getOrderBy()
393 where = self.whereExpressions(zip(sort_expressions, memo))425 where = self.whereExpressions(sort_expressions, memo)
394 where = reduce(Or, where)426 where = reduce(Or, where)
395 # From storm.zope.interfaces.IResultSet.__doc__:427 # From storm.zope.interfaces.IResultSet.__doc__:
396 # - C{find()}, C{group_by()} and C{having()} are really428 # - C{find()}, C{group_by()} and C{having()} are really
397429
=== modified file 'lib/canonical/launchpad/webapp/tests/test_batching.py'
--- lib/canonical/launchpad/webapp/tests/test_batching.py 2011-08-03 10:21:35 +0000
+++ lib/canonical/launchpad/webapp/tests/test_batching.py 2011-08-03 10:21:37 +0000
@@ -93,7 +93,7 @@
93 StormRangeFactoryError, range_factory.getOrderValuesFor,93 StormRangeFactoryError, range_factory.getOrderValuesFor,
94 resultset[0])94 resultset[0])
95 self.assertEqual(95 self.assertEqual(
96 "StormRangeFactory supports only sorting by PropertyColumn, "96 "StormRangeFactory only supports sorting by PropertyColumn, "
97 "not by 'Person.id'.",97 "not by 'Person.id'.",
98 str(exception))98 str(exception))
9999
@@ -106,7 +106,7 @@
106 resultset[0])106 resultset[0])
107 self.assertTrue(107 self.assertTrue(
108 str(exception).startswith(108 str(exception).startswith(
109 'StormRangeFactory supports only sorting by PropertyColumn, '109 'StormRangeFactory only supports sorting by PropertyColumn, '
110 'not by <storm.expr.SQL object at'))110 'not by <storm.expr.SQL object at'))
111111
112 def test_getOrderValuesFor__unordered_result_set(self):112 def test_getOrderValuesFor__unordered_result_set(self):
@@ -358,7 +358,7 @@
358 """358 """
359 resultset = self.makeStormResultSet()359 resultset = self.makeStormResultSet()
360 range_factory = StormRangeFactory(resultset, self.logError)360 range_factory = StormRangeFactory(resultset, self.logError)
361 [where_clause] = range_factory.whereExpressions([(Person.id, 1)])361 [where_clause] = range_factory.whereExpressions([Person.id], [1])
362 self.assertEquals('Person.id > ?', compile(where_clause))362 self.assertEquals('Person.id > ?', compile(where_clause))
363363
364 def test_whereExpressions_desc(self):364 def test_whereExpressions_desc(self):
@@ -368,15 +368,45 @@
368 resultset = self.makeStormResultSet()368 resultset = self.makeStormResultSet()
369 range_factory = StormRangeFactory(resultset, self.logError)369 range_factory = StormRangeFactory(resultset, self.logError)
370 [where_clause] = range_factory.whereExpressions(370 [where_clause] = range_factory.whereExpressions(
371 [(Desc(Person.id), 1)])371 [Desc(Person.id)], [1])
372 self.assertEquals('Person.id < ?', compile(where_clause))372 self.assertEquals('Person.id < ?', compile(where_clause))
373373
374 def test_whereExpressions__two_sort_columns(self):374 def test_whereExpressions__two_sort_columns_asc_asc(self):
375 """If the sort columns and memo values (c1, m1) and (c2, m2)375 """If the ascending sort columns c1, c2 and the memo values
376 are specified, whereExpressions() returns two expressions where376 m1, m2 are specified, whereExpressions() returns a WHERE
377 the first expression is377 expressions comparing the tuple (c1, c2) with the memo tuple
378378 (m1, m2):
379 c1 == m1 AND c2 > m2379
380 (c1, c2) > (m1, m2)
381 """
382 resultset = self.makeStormResultSet()
383 range_factory = StormRangeFactory(resultset, self.logError)
384 [where_clause] = range_factory.whereExpressions(
385 [Person.id, Person.name], [1, 'foo'])
386 self.assertEquals(
387 "(Person.id, Person.name) > (1, 'foo')", compile(where_clause))
388
389 def test_whereExpressions__two_sort_columns_desc_desc(self):
390 """If the descending sort columns c1, c2 and the memo values
391 m1, m2 are specified, whereExpressions() returns a WHERE
392 expressions comparing the tuple (c1, c2) with the memo tuple
393 (m1, m2):
394
395 (c1, c2) < (m1, m2)
396 """
397 resultset = self.makeStormResultSet()
398 range_factory = StormRangeFactory(resultset, self.logError)
399 [where_clause] = range_factory.whereExpressions(
400 [Desc(Person.id), Desc(Person.name)], [1, 'foo'])
401 self.assertEquals(
402 "(Person.id, Person.name) < (1, 'foo')", compile(where_clause))
403
404 def test_whereExpressions__two_sort_columns_asc_desc(self):
405 """If the ascending sort column c1, the descending sort column
406 c2 and the memo values m1, m2 are specified, whereExpressions()
407 returns two expressions where the first expression is
408
409 c1 == m1 AND c2 < m2
380410
381 and the second expression is411 and the second expression is
382412
@@ -385,9 +415,9 @@
385 resultset = self.makeStormResultSet()415 resultset = self.makeStormResultSet()
386 range_factory = StormRangeFactory(resultset, self.logError)416 range_factory = StormRangeFactory(resultset, self.logError)
387 [where_clause_1, where_clause_2] = range_factory.whereExpressions(417 [where_clause_1, where_clause_2] = range_factory.whereExpressions(
388 [(Person.id, 1), (Person.name, 'foo')])418 [Person.id, Desc(Person.name)], [1, 'foo'])
389 self.assertEquals(419 self.assertEquals(
390 'Person.id = ? AND Person.name > ?', compile(where_clause_1))420 'Person.id = ? AND Person.name < ?', compile(where_clause_1))
391 self.assertEquals('Person.id > ?', compile(where_clause_2))421 self.assertEquals('Person.id > ?', compile(where_clause_2))
392422
393 def test_getSlice__forward_without_memo(self):423 def test_getSlice__forward_without_memo(self):