Merge lp:~thisfred/u1db/result-ordering into lp:u1db

Proposed by Eric Casteleijn
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 319
Merged at revision: 318
Proposed branch: lp:~thisfred/u1db/result-ordering
Merge into: lp:u1db
Diff against target: 262 lines (+59/-21)
5 files modified
src/u1db_query.c (+7/-0)
u1db/backends/inmemory.py (+1/-1)
u1db/backends/sqlite_backend.py (+6/-3)
u1db/tests/test_backends.py (+25/-6)
u1db/tests/test_c_backend.py (+20/-11)
To merge this branch: bzr merge lp:~thisfred/u1db/result-ordering
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+108045@code.launchpad.net

Commit message

Made ordering of index query results explicit, and enforced it in tests.

Description of the change

Made ordering of index query results explicit, and enforced it in tests.

To post a comment you must log in.
Revision history for this message
Samuele Pedroni (pedronis) wrote :

looks good

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'src/u1db_query.c'
2--- src/u1db_query.c 2012-05-24 21:09:21 +0000
3+++ src/u1db_query.c 2012-05-30 19:45:25 +0000
4@@ -741,6 +741,13 @@
5 add_to_buf(&cur, &buf_size, " AND d%d.value = ?", i);
6 }
7 }
8+ add_to_buf(&cur, &buf_size, " ORDER BY ");
9+ for (i = 0; i < n_fields; ++i) {
10+ if (i != 0) {
11+ add_to_buf(&cur, &buf_size, ", ");
12+ }
13+ add_to_buf(&cur, &buf_size, "d%d.value", i);
14+ }
15 finish:
16 if (status != U1DB_OK && *buf != NULL) {
17 free(*buf);
18
19=== modified file 'u1db/backends/inmemory.py'
20--- u1db/backends/inmemory.py 2012-05-29 15:49:21 +0000
21+++ u1db/backends/inmemory.py 2012-05-30 19:45:25 +0000
22@@ -354,7 +354,7 @@
23 key_prefix = '\x01'.join(value)
24 key_prefix = key_prefix.rstrip('*')
25 all_doc_ids = []
26- for key, doc_ids in self._values.iteritems():
27+ for key, doc_ids in sorted(self._values.iteritems()):
28 if key.startswith(key_prefix):
29 all_doc_ids.extend(doc_ids)
30 return all_doc_ids
31
32=== modified file 'u1db/backends/sqlite_backend.py'
33--- u1db/backends/sqlite_backend.py 2012-05-29 15:49:21 +0000
34+++ u1db/backends/sqlite_backend.py 2012-05-30 19:45:25 +0000
35@@ -619,9 +619,12 @@
36 raise errors.InvalidGlobbing
37 where.append(exact_where[idx])
38 args.append(value)
39- statement = ("SELECT d.doc_id, d.doc_rev, d.content"
40- " FROM document d, "
41- + ', '.join(tables) + " WHERE " + ' AND '.join(where))
42+ statement = (
43+ "SELECT d.doc_id, d.doc_rev, d.content FROM document d, %s "
44+ "WHERE %s ORDER BY %s;" % (
45+ ', '.join(tables), ' AND '.join(where),
46+ ', '.join(
47+ ['d%d.value' % i for i in range(len(definition))])))
48 try:
49 c.execute(statement, tuple(args))
50 except dbapi2.OperationalError, e:
51
52=== modified file 'u1db/tests/test_backends.py'
53--- u1db/tests/test_backends.py 2012-05-29 15:49:21 +0000
54+++ u1db/tests/test_backends.py 2012-05-30 19:45:25 +0000
55@@ -811,6 +811,15 @@
56 self.assertEqual([doc],
57 self.db.get_from_index('test-idx', [('value', 'value2')]))
58
59+ def test_get_from_index_multi_ordered(self):
60+ doc1 = self.db.create_doc('{"key": "value3", "key2": "value4"}')
61+ doc2 = self.db.create_doc('{"key": "value2", "key2": "value3"}')
62+ doc3 = self.db.create_doc('{"key": "value2", "key2": "value2"}')
63+ doc4 = self.db.create_doc('{"key": "value1", "key2": "value1"}')
64+ self.db.create_index('test-idx', ['key', 'key2'])
65+ self.assertEqual([doc4, doc3, doc2, doc1],
66+ self.db.get_from_index('test-idx', [('v*', '*')]))
67+
68 def test_get_from_index_fails_if_no_index(self):
69 self.assertRaises(errors.IndexDoesNotExist,
70 self.db.get_from_index,
71@@ -870,13 +879,23 @@
72 doc1 = self.db.create_doc(simple_doc)
73 doc2 = self.db.create_doc(nested_doc)
74 # This one should not be in the index
75- doc3 = self.db.create_doc('{"no": "key"}')
76+ self.db.create_doc('{"no": "key"}')
77 diff_value_doc = '{"key": "diff value"}'
78 doc4 = self.db.create_doc(diff_value_doc)
79 # This is essentially a 'prefix' match, but we match every entry.
80 self.assertEqual(sorted([doc1, doc2, doc4]),
81 sorted(self.db.get_from_index('test-idx', [('*',)])))
82
83+ def test_get_all_from_index_ordered(self):
84+ self.db.create_index('test-idx', ['key'])
85+ doc1 = self.db.create_doc('{"key": "value x"}')
86+ doc2 = self.db.create_doc('{"key": "value b"}')
87+ doc3 = self.db.create_doc('{"key": "value a"}')
88+ doc4 = self.db.create_doc('{"key": "value m"}')
89+ # This is essentially a 'prefix' match, but we match every entry.
90+ self.assertEqual([doc3, doc2, doc4, doc1],
91+ self.db.get_from_index('test-idx', [('*',)]))
92+
93 def test_put_updates_when_adding_key(self):
94 doc = self.db.create_doc("{}")
95 self.db.create_index('test-idx', ['key'])
96@@ -901,7 +920,7 @@
97 def test_get_from_index_not_null(self):
98 self.db.create_index('test-idx', ['key'])
99 doc1 = self.db.create_doc(simple_doc)
100- doc2 = self.db.create_doc('{"key": null}')
101+ self.db.create_doc('{"key": null}')
102 self.assertEqual([doc1],
103 self.db.get_from_index('test-idx', [('*',)]))
104
105@@ -914,7 +933,7 @@
106 doc1 = self.db.create_doc(content1)
107 doc2 = self.db.create_doc(content2)
108 doc3 = self.db.create_doc(content3)
109- doc4 = self.db.create_doc(content4)
110+ self.db.create_doc(content4)
111 self.db.create_index('test-idx', ['k1', 'k2'])
112 self.assertEqual(sorted([doc1, doc2, doc3]),
113 sorted(self.db.get_from_index('test-idx', [("v1", "*")])))
114@@ -930,7 +949,7 @@
115 doc1 = self.db.create_doc(content1)
116 doc2 = self.db.create_doc(content2)
117 doc3 = self.db.create_doc(content3)
118- doc4 = self.db.create_doc(content4)
119+ self.db.create_doc(content4)
120 self.assertEqual(sorted([doc1, doc2, doc3]),
121 sorted(self.db.get_from_index('test-idx', [("v1", "v*")])))
122
123@@ -945,13 +964,13 @@
124 sorted(self.db.get_from_index('test-idx', [('underneath',)])))
125
126 def test_nested_nonexistent(self):
127- doc = self.db.create_doc(nested_doc)
128+ self.db.create_doc(nested_doc)
129 # sub exists, but sub.foo does not:
130 self.db.create_index('test-idx', ['sub.foo'])
131 self.assertEqual([], self.db.get_from_index('test-idx', [('*',)]))
132
133 def test_nested_nonexistent2(self):
134- doc = self.db.create_doc(nested_doc)
135+ self.db.create_doc(nested_doc)
136 # sub exists, but sub.foo does not:
137 self.db.create_index('test-idx', ['sub.foo.bar.baz.qux.fnord'])
138 self.assertEqual([], self.db.get_from_index('test-idx', [('*',)]))
139
140=== modified file 'u1db/tests/test_c_backend.py'
141--- u1db/tests/test_c_backend.py 2012-05-22 18:58:04 +0000
142+++ u1db/tests/test_c_backend.py 2012-05-30 19:45:25 +0000
143@@ -83,7 +83,7 @@
144 self.assertIsNot(None, self.db._replica_uid)
145 self.assertEqual(32, len(self.db._replica_uid))
146 # casting to an int from the uid *is* the check for correct behavior.
147- val = int(self.db._replica_uid, 16)
148+ int(self.db._replica_uid, 16)
149
150 def test_get_conflicts_with_borked_data(self):
151 self.db = c_backend_wrapper.CDatabase(':memory:')
152@@ -140,14 +140,15 @@
153 def test__format_query(self):
154 self.assertFormatQueryEquals(
155 "SELECT d0.doc_id FROM document_fields d0"
156- " WHERE d0.field_name = ? AND d0.value = ?",
157+ " WHERE d0.field_name = ? AND d0.value = ? ORDER BY d0.value",
158 [0], ["1"])
159 self.assertFormatQueryEquals(
160 "SELECT d0.doc_id"
161 " FROM document_fields d0, document_fields d1"
162 " WHERE d0.field_name = ? AND d0.value = ?"
163 " AND d0.doc_id = d1.doc_id"
164- " AND d1.field_name = ? AND d1.value = ?",
165+ " AND d1.field_name = ? AND d1.value = ?"
166+ " ORDER BY d0.value, d1.value",
167 [0, 0], ["1", "2"])
168 self.assertFormatQueryEquals(
169 "SELECT d0.doc_id"
170@@ -156,26 +157,28 @@
171 " AND d0.doc_id = d1.doc_id"
172 " AND d1.field_name = ? AND d1.value = ?"
173 " AND d0.doc_id = d2.doc_id"
174- " AND d2.field_name = ? AND d2.value = ?",
175+ " AND d2.field_name = ? AND d2.value = ?"
176+ " ORDER BY d0.value, d1.value, d2.value",
177 [0, 0, 0], ["1", "2", "3"])
178
179 def test__format_query_wildcard(self):
180 self.assertFormatQueryEquals(
181 "SELECT d0.doc_id FROM document_fields d0"
182- " WHERE d0.field_name = ? AND d0.value NOT NULL",
183+ " WHERE d0.field_name = ? AND d0.value NOT NULL ORDER BY d0.value",
184 [1], ["*"])
185 self.assertFormatQueryEquals(
186 "SELECT d0.doc_id"
187 " FROM document_fields d0, document_fields d1"
188 " WHERE d0.field_name = ? AND d0.value = ?"
189 " AND d0.doc_id = d1.doc_id"
190- " AND d1.field_name = ? AND d1.value NOT NULL",
191+ " AND d1.field_name = ? AND d1.value NOT NULL"
192+ " ORDER BY d0.value, d1.value",
193 [0, 1], ["1", "*"])
194
195 def test__format_query_glob(self):
196 self.assertFormatQueryEquals(
197 "SELECT d0.doc_id FROM document_fields d0"
198- " WHERE d0.field_name = ? AND d0.value GLOB ?",
199+ " WHERE d0.field_name = ? AND d0.value GLOB ? ORDER BY d0.value",
200 [2], ["1*"])
201
202
203@@ -187,7 +190,8 @@
204 self.st = self.db.get_sync_target()
205
206 def test_attached_to_db(self):
207- self.assertEqual(self.db._replica_uid, self.st.get_sync_info("misc")[0])
208+ self.assertEqual(
209+ self.db._replica_uid, self.st.get_sync_info("misc")[0])
210
211 def test_get_sync_exchange(self):
212 exc = self.st._get_sync_exchange("source-uid", 10)
213@@ -213,7 +217,8 @@
214 self.assertEqual([], exc.get_seen_ids())
215 # The insert should be rejected and the doc_id not considered 'seen'
216 exc.insert_doc_from_source(doc2, 10)
217- self.assertGetDoc(self.db, doc.doc_id, doc.rev, tests.simple_doc, False)
218+ self.assertGetDoc(
219+ self.db, doc.doc_id, doc.rev, tests.simple_doc, False)
220 self.assertEqual([], exc.get_seen_ids())
221
222 def test_sync_exchange_find_doc_ids(self):
223@@ -237,8 +242,10 @@
224
225 def test_sync_exchange_return_docs(self):
226 returned = []
227+
228 def return_doc_cb(doc, gen):
229 returned.append((doc, gen))
230+
231 doc1 = self.db.create_doc(tests.simple_doc)
232 exc = self.st._get_sync_exchange("source-uid", 0)
233 exc.find_doc_ids_to_return()
234@@ -250,8 +257,10 @@
235 db2 = c_backend_wrapper.CDatabase(':memory:')
236 doc2 = db2.create_doc(tests.nested_doc, doc_id='doc-2')
237 returned = []
238+
239 def return_doc_cb(doc, gen):
240 returned.append((doc, gen))
241+
242 target_gen = self.st.sync_exchange_doc_ids(db2, [(doc2.doc_id, 1)], 0,
243 return_doc_cb)
244 self.assertEqual(2, self.db._get_generation())
245@@ -402,7 +411,7 @@
246 return c_backend_wrapper.make_document(*args, **kwargs)
247
248 def test_create(self):
249- doc = self.make_document('doc-id', 'uid:1', tests.simple_doc)
250+ self.make_document('doc-id', 'uid:1', tests.simple_doc)
251
252 def assertPyDocEqualCDoc(self, *args, **kwargs):
253 cdoc = self.make_document(*args, **kwargs)
254@@ -452,7 +461,7 @@
255 self.assertIsInstance(uuid, str)
256 self.assertEqual(32, len(uuid))
257 # This will raise ValueError if it isn't a valid hex string
258- v = long(uuid, 16)
259+ long(uuid, 16)
260 # Version 4 uuids have 2 other requirements, the high 4 bits of the
261 # seventh byte are always '0x4', and the middle bits of byte 9 are
262 # always set

Subscribers

People subscribed via source and target branches