Merge lp:~jamesh/storm/result-randomisation into lp:storm

Proposed by James Henstridge
Status: Work in progress
Proposed branch: lp:~jamesh/storm/result-randomisation
Merge into: lp:storm
Diff against target: 219 lines (has conflicts)
Text conflict in tests/store/base.py
To merge this branch: bzr merge lp:~jamesh/storm/result-randomisation
To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

There are some comments on the bug about this one.

217. By James Henstridge

merge from trunk

Unmerged revisions

217. By James Henstridge

merge from trunk

216. By James Henstridge

Add a few tests for various operations when run with result
randomisation turned on. Currently test_randomise_order_distinct()
fails for Postgres and test_randomise_order_union() fails for SQLite.

215. By James Henstridge

* Add Store.set_randomise_order() method to turn on select results
  randomisation.
* Add a test to verify that Random() is being added to the ORDER BY
  clause of select results when the above is turned on.

214. By James Henstridge

* Add Random() expression to storm.expr, compiling to "RANDOM()".
* Compile Random() to "RAND()" on MySQL.
* Add tests for same.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'storm/databases/mysql.py'
--- storm/databases/mysql.py 2008-08-16 07:47:33 +0000
+++ storm/databases/mysql.py 2009-08-13 20:21:08 +0000
@@ -32,7 +32,7 @@
3232
33from storm.expr import (33from storm.expr import (
34 compile, Insert, Select, compile_select, Undef, And, Eq,34 compile, Insert, Select, compile_select, Undef, And, Eq,
35 SQLRaw, SQLToken, is_safe_token)35 Random, SQLRaw, SQLToken, is_safe_token)
36from storm.variables import Variable36from storm.variables import Variable
37from storm.database import Database, Connection, Result37from storm.database import Database, Connection, Result
38from storm.exceptions import (38from storm.exceptions import (
@@ -45,6 +45,11 @@
4545
46compile = compile.create_child()46compile = compile.create_child()
4747
48@compile.when(Random)
49def compile_random_mysql(compile, func, state):
50 """MySQL uses RAND() to produce random values."""
51 return 'RAND(%s)' % compile(func.args, state)
52
48@compile.when(Select)53@compile.when(Select)
49def compile_select_mysql(compile, select, state):54def compile_select_mysql(compile, select, state):
50 if select.offset is not Undef and select.limit is Undef:55 if select.offset is not Undef and select.limit is Undef:
5156
=== modified file 'storm/expr.py'
--- storm/expr.py 2009-07-31 01:53:08 +0000
+++ storm/expr.py 2009-08-13 20:21:08 +0000
@@ -1243,6 +1243,13 @@
1243 __slots__ = ()1243 __slots__ = ()
1244 name = "UPPER"1244 name = "UPPER"
12451245
1246class Random(NamedFunc):
1247 """Expression that returns a random value.
1248
1249 Can be used to randomise the order of a result set.
1250 """
1251 name = "RANDOM"
1252
12461253
1247class Coalesce(NamedFunc):1254class Coalesce(NamedFunc):
1248 __slots__ = ()1255 __slots__ = ()
12491256
=== modified file 'storm/store.py'
--- storm/store.py 2009-07-31 01:53:08 +0000
+++ storm/store.py 2009-08-13 20:21:08 +0000
@@ -33,7 +33,7 @@
33from storm.expr import (33from storm.expr import (
34 Expr, Select, Insert, Update, Delete, Column, Count, Max, Min,34 Expr, Select, Insert, Update, Delete, Column, Count, Max, Min,
35 Avg, Sum, Eq, And, Asc, Desc, compile_python, compare_columns, SQLRaw,35 Avg, Sum, Eq, And, Asc, Desc, compile_python, compare_columns, SQLRaw,
36 Union, Except, Intersect, Alias, SetExpr)36 Union, Except, Intersect, Alias, Random, SetExpr)
37from storm.exceptions import (37from storm.exceptions import (
38 WrongStoreError, NotFlushedError, OrderLoopError, UnorderedError,38 WrongStoreError, NotFlushedError, OrderLoopError, UnorderedError,
39 NotOneError, FeatureError, CompileError, LostObjectError, ClassInfoError)39 NotOneError, FeatureError, CompileError, LostObjectError, ClassInfoError)
@@ -80,6 +80,17 @@
80 self._cache = cache80 self._cache = cache
81 self._implicit_flush_block_count = 081 self._implicit_flush_block_count = 0
82 self._sequence = 0 # Advisory ordering.82 self._sequence = 0 # Advisory ordering.
83 self._randomise_results = False
84
85 def set_randomise_order(self, value):
86 """Set whether the order of result sets should be randomised.
87
88 If enabled, RANDOM() will be appended to the ORDER BY clause
89 of SELECT statements. This is intended to aid testing by
90 preventing tests from relying on the 'natural ordering' of
91 results.
92 """
93 self._randomise_results = bool(value)
8394
84 @staticmethod95 @staticmethod
85 def of(obj):96 def of(obj):
@@ -946,9 +957,17 @@
946 return self957 return self
947958
948 def _get_select(self):959 def _get_select(self):
960 # If results should be randomised, do so here:
961 if self._store._randomise_results:
962 if self._order_by is Undef:
963 order_by = [Random()]
964 else:
965 order_by = list(self._order_by) + [Random()]
966 else:
967 order_by = self._order_by
949 if self._select is not Undef:968 if self._select is not Undef:
950 if self._order_by is not Undef:969 if order_by is not Undef:
951 self._select.order_by = self._order_by970 self._select.order_by = order_by
952 if self._limit is not Undef: # XXX UNTESTED!971 if self._limit is not Undef: # XXX UNTESTED!
953 self._select.limit = self._limit972 self._select.limit = self._limit
954 if self._offset is not Undef: # XXX UNTESTED!973 if self._offset is not Undef: # XXX UNTESTED!
@@ -956,7 +975,7 @@
956 return self._select975 return self._select
957 columns, default_tables = self._find_spec.get_columns_and_tables()976 columns, default_tables = self._find_spec.get_columns_and_tables()
958 return Select(columns, self._where, self._tables, default_tables,977 return Select(columns, self._where, self._tables, default_tables,
959 self._order_by, offset=self._offset, limit=self._limit,978 order_by, offset=self._offset, limit=self._limit,
960 distinct=self._distinct, group_by=self._group_by,979 distinct=self._distinct, group_by=self._group_by,
961 having=self._having)980 having=self._having)
962981
963982
=== modified file 'tests/databases/mysql.py'
--- tests/databases/mysql.py 2008-08-16 07:47:33 +0000
+++ tests/databases/mysql.py 2009-08-13 20:21:08 +0000
@@ -20,9 +20,9 @@
20#20#
21import os21import os
2222
23from storm.databases.mysql import MySQL23from storm.databases.mysql import MySQL, compile
24from storm.database import create_database24from storm.database import create_database
25from storm.expr import Column, Insert25from storm.expr import Column, Insert, Random, State
26from storm.uri import URI26from storm.uri import URI
27from storm.variables import IntVariable, UnicodeVariable27from storm.variables import IntVariable, UnicodeVariable
2828
@@ -75,6 +75,13 @@
75 result = connection.execute("SELECT @@character_set_client")75 result = connection.execute("SELECT @@character_set_client")
76 self.assertEquals(result.get_one(), ("ascii",))76 self.assertEquals(result.get_one(), ("ascii",))
7777
78 def test_compile_random(self):
79 """Test that Random() compiles to RAND() with MySQL"""
80 expr = Random()
81 state = State()
82 statement = compile(expr, state)
83 self.assertEquals(statement, "RAND()")
84
78 def test_get_insert_identity(self):85 def test_get_insert_identity(self):
79 # Primary keys are filled in during execute() for MySQL86 # Primary keys are filled in during execute() for MySQL
80 pass87 pass
8188
=== modified file 'tests/expr.py'
--- tests/expr.py 2009-07-23 22:47:10 +0000
+++ tests/expr.py 2009-08-13 20:21:08 +0000
@@ -165,6 +165,11 @@
165 self.assertEquals(expr.name, "myfunc")165 self.assertEquals(expr.name, "myfunc")
166 self.assertEquals(expr.args, (elem1, elem2))166 self.assertEquals(expr.args, (elem1, elem2))
167167
168 def test_random(self):
169 expr = Random()
170 self.assertEquals(expr.name, "RANDOM")
171 self.assertEquals(expr.args, ())
172
168 def test_like(self):173 def test_like(self):
169 expr = Like(elem1, elem2)174 expr = Like(elem1, elem2)
170 self.assertEquals(expr.expr1, elem1)175 self.assertEquals(expr.expr1, elem1)
171176
=== modified file 'tests/store/base.py'
--- tests/store/base.py 2009-07-31 01:53:08 +0000
+++ tests/store/base.py 2009-08-13 20:21:08 +0000
@@ -24,14 +24,20 @@
24import operator24import operator
25import weakref25import weakref
2626
27from storm import Undef
27from storm.references import Reference, ReferenceSet, Proxy28from storm.references import Reference, ReferenceSet, Proxy
28from storm.database import Result29from storm.database import Result
29from storm.properties import Int, Float, RawStr, Unicode, Property, Pickle30from storm.properties import Int, Float, RawStr, Unicode, Property, Pickle
30from storm.properties import PropertyPublisherMeta, Decimal31from storm.properties import PropertyPublisherMeta, Decimal
31from storm.variables import PickleVariable32from storm.variables import PickleVariable
32from storm.expr import (33from storm.expr import (
34<<<<<<< TREE
33 Asc, Desc, Select, Func, LeftJoin, SQL, Count, Sum, Avg, And, Or, Eq,35 Asc, Desc, Select, Func, LeftJoin, SQL, Count, Sum, Avg, And, Or, Eq,
34 Lower)36 Lower)
37=======
38 Asc, Desc, Select, Func, LeftJoin, SQL, Count, Sum, Avg, And, Or, Eq,
39 Random)
40>>>>>>> MERGE-SOURCE
35from storm.variables import Variable, UnicodeVariable, IntVariable41from storm.variables import Variable, UnicodeVariable, IntVariable
36from storm.info import get_obj_info, ClassAlias42from storm.info import get_obj_info, ClassAlias
37from storm.exceptions import *43from storm.exceptions import *
@@ -5484,6 +5490,45 @@
5484 " (ensure your database was created with CREATE DATABASE"5490 " (ensure your database was created with CREATE DATABASE"
5485 " ... CHARACTER SET utf8)")5491 " ... CHARACTER SET utf8)")
54865492
5493 def test_randomise_order(self):
5494 self.store.set_randomise_order(True)
5495 # With no order specified, Random() is used as the ordering.
5496 result = self.store.find(Foo)
5497 select = result._get_select()
5498 self.assertNotEquals(select.order_by, Undef)
5499 self.assertEquals(len(select.order_by), 1)
5500 self.assertTrue(isinstance(select.order_by[0], Random))
5501 # If the result already has an ordering, Random() is appended
5502 result.order_by(Foo.title, Foo.id)
5503 select = result._get_select()
5504 self.assertNotEquals(select.order_by, Undef)
5505 self.assertEquals(len(select.order_by), 3)
5506 self.assertEquals(select.order_by[0].table, Foo)
5507 self.assertEquals(select.order_by[0].name, 'title')
5508 self.assertEquals(select.order_by[1].table, Foo)
5509 self.assertEquals(select.order_by[1].name, 'id')
5510 self.assertTrue(isinstance(select.order_by[2], Random))
5511
5512 def test_randomise_order_distinct(self):
5513 self.store.set_randomise_order(True)
5514 result = self.store.find(Foo)
5515 result.config(distinct=True)
5516 names = sorted(foo.title for foo in result)
5517 self.assertEquals(names, ['Title 10', 'Title 20', 'Title 30'])
5518
5519 def test_randomise_order_count(self):
5520 self.store.set_randomise_order(True)
5521 result = self.store.find(Foo)
5522 self.assertEquals(result.count(), 3)
5523
5524 def test_randomise_order_union(self):
5525 self.store.set_randomise_order(True)
5526 result1 = self.store.find(Foo, Foo.id < 20)
5527 result2 = self.store.find(Foo, Foo.id >= 20)
5528 result = result1.union(result2)
5529 names = sorted(foo.title for foo in result)
5530 self.assertEquals(names, ['Title 10', 'Title 20', 'Title 30'])
5531
5487 def test_creation_order_is_preserved_when_possible(self):5532 def test_creation_order_is_preserved_when_possible(self):
5488 foos = [self.store.add(Foo()) for i in range(10)]5533 foos = [self.store.add(Foo()) for i in range(10)]
5489 self.store.flush()5534 self.store.flush()

Subscribers

People subscribed via source and target branches

to status/vote changes: