Merge lp:~chipaca/u1db/split_out_bad_globbage into lp:u1db
- split_out_bad_globbage
- Merge into trunk
Proposed by
John Lenton
Status: | Superseded |
---|---|
Proposed branch: | lp:~chipaca/u1db/split_out_bad_globbage |
Merge into: | lp:u1db |
Diff against target: |
379 lines (+192/-17) 10 files modified
include/u1db/u1db.h (+1/-0) src/u1db_query.c (+2/-2) u1db/backends/inmemory.py (+2/-2) u1db/backends/sqlite_backend.py (+2/-2) u1db/commandline/client.py (+79/-1) u1db/errors.py (+5/-4) u1db/tests/c_backend_wrapper.pyx (+3/-0) u1db/tests/commandline/test_client.py (+92/-0) u1db/tests/test_backends.py (+5/-5) u1db/tests/test_inmemory.py (+1/-1) |
To merge this branch: | bzr merge lp:~chipaca/u1db/split_out_bad_globbage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu One hackers | Pending | ||
Review via email: mp+106990@code.launchpad.net |
This proposal has been superseded by a proposal from 2012-05-23.
Commit message
Description of the change
Split the «user globbed wrong» error from the «user used the wrong number of arguments» error when querying indexes.
To post a comment you must log in.
- 310. By John Lenton
-
fix up docstrings as per issues reported by thisfred and suggestions from sil
Unmerged revisions
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'include/u1db/u1db.h' |
2 | --- include/u1db/u1db.h 2012-05-21 15:01:18 +0000 |
3 | +++ include/u1db/u1db.h 2012-05-23 12:00:25 +0000 |
4 | @@ -67,6 +67,7 @@ |
5 | #define U1DB_INVALID_FIELD_SPECIFIER -16 |
6 | #define U1DB_DUPLICATE_INDEX_NAME -17 |
7 | #define U1DB_INDEX_DOES_NOT_EXIST -18 |
8 | +#define U1DB_INVALID_GLOBBING -19 |
9 | #define U1DB_INTERNAL_ERROR -999 |
10 | |
11 | // Used by put_doc_if_newer |
12 | |
13 | === modified file 'src/u1db_query.c' |
14 | --- src/u1db_query.c 2012-05-21 16:29:25 +0000 |
15 | +++ src/u1db_query.c 2012-05-23 12:00:25 +0000 |
16 | @@ -725,7 +725,7 @@ |
17 | wildcard[i] = 2; |
18 | if (have_wildcard) { |
19 | //globs not allowed after another wildcard |
20 | - status = U1DB_INVALID_VALUE_FOR_INDEX; |
21 | + status = U1DB_INVALID_GLOBBING; |
22 | goto finish; |
23 | } |
24 | have_wildcard = 1; |
25 | @@ -734,7 +734,7 @@ |
26 | wildcard[i] = 0; |
27 | if (have_wildcard) { |
28 | // Can't have a non-wildcard after a wildcard |
29 | - status = U1DB_INVALID_VALUE_FOR_INDEX; |
30 | + status = U1DB_INVALID_GLOBBING; |
31 | goto finish; |
32 | } |
33 | add_to_buf(&cur, &buf_size, " AND d%d.value = ?", i); |
34 | |
35 | === modified file 'u1db/backends/inmemory.py' |
36 | --- u1db/backends/inmemory.py 2012-05-21 15:01:18 +0000 |
37 | +++ u1db/backends/inmemory.py 2012-05-23 12:00:25 +0000 |
38 | @@ -302,14 +302,14 @@ |
39 | # We have an 'x*' style wildcard |
40 | if is_wildcard: |
41 | # We were already in wildcard mode, so this is invalid |
42 | - raise errors.InvalidValueForIndex() |
43 | + raise errors.InvalidGlobbing |
44 | last = idx + 1 |
45 | is_wildcard = True |
46 | else: |
47 | if is_wildcard: |
48 | # We were in wildcard mode, we can't follow that with |
49 | # non-wildcard |
50 | - raise errors.InvalidValueForIndex() |
51 | + raise errors.InvalidGlobbing |
52 | last = idx + 1 |
53 | if not is_wildcard: |
54 | return -1 |
55 | |
56 | === modified file 'u1db/backends/sqlite_backend.py' |
57 | --- u1db/backends/sqlite_backend.py 2012-05-21 16:29:25 +0000 |
58 | +++ u1db/backends/sqlite_backend.py 2012-05-23 12:00:25 +0000 |
59 | @@ -592,13 +592,13 @@ |
60 | if is_wildcard: |
61 | # We can't have a partial wildcard following |
62 | # another wildcard |
63 | - raise errors.InvalidValueForIndex() |
64 | + raise errors.InvalidGlobbing |
65 | where.append(like_where[idx]) |
66 | args.append(self._transform_glob(value)) |
67 | is_wildcard = True |
68 | else: |
69 | if is_wildcard: |
70 | - raise errors.InvalidValueForIndex() |
71 | + raise errors.InvalidGlobbing |
72 | where.append(exact_where[idx]) |
73 | args.append(value) |
74 | statement = ("SELECT d.doc_id, d.doc_rev, d.content" |
75 | |
76 | === modified file 'u1db/commandline/client.py' |
77 | --- u1db/commandline/client.py 2012-05-21 17:32:38 +0000 |
78 | +++ u1db/commandline/client.py 2012-05-23 12:00:25 +0000 |
79 | @@ -18,6 +18,7 @@ |
80 | |
81 | import argparse |
82 | import os |
83 | +import simplejson |
84 | import sys |
85 | |
86 | from u1db import ( |
87 | @@ -322,9 +323,86 @@ |
88 | return |
89 | return 1 |
90 | |
91 | - |
92 | client_commands.register(CmdGetIndexKeys) |
93 | |
94 | |
95 | +class CmdGetFromIndex(OneDbCmd): |
96 | + """Find documents by searching an index""" |
97 | + |
98 | + name = "get-from-index" |
99 | + argv = None |
100 | + |
101 | + @classmethod |
102 | + def _populate_subparser(cls, parser): |
103 | + parser.add_argument('database', help='The local database to query', |
104 | + metavar='database-path') |
105 | + parser.add_argument('index', help='the name of the index') |
106 | + parser.add_argument('values', metavar="value", |
107 | + help='the value to look up (one per index column)', |
108 | + nargs="+") |
109 | + |
110 | + def run(self, database, index, values): |
111 | + try: |
112 | + db = self._open(database, create=False) |
113 | + docs = db.get_from_index(index, [values]) |
114 | + except errors.DatabaseDoesNotExist: |
115 | + self.stderr.write("Database does not exist.\n") |
116 | + except errors.IndexDoesNotExist: |
117 | + self.stderr.write("Index does not exist.\n") |
118 | + except errors.InvalidValueForIndex: |
119 | + index_def = db._get_index_definition(index) |
120 | + msg = ["Invalid query;"] |
121 | + len_diff = len(index_def) - len(values) |
122 | + if len_diff: |
123 | + msg.append("index %r requires %d query expression%s" |
124 | + % (index, len(index_def), |
125 | + "s" if len(index_def) > 1 else "")) |
126 | + if len(values): |
127 | + msg[-1] += ", not %d" % len(values) |
128 | + if len_diff > 0: |
129 | + msg[-1] += ".\nPerhaps you meant:" |
130 | + argv = self.argv if self.argv is not None else sys.argv |
131 | + msg.extend(argv[:2]) |
132 | + msg.append(repr(database)) |
133 | + msg.extend(map(repr, values)) |
134 | + msg.extend(["'*'" for i in range(len_diff)]) |
135 | + else: |
136 | + # can't happen (HAH) |
137 | + msg.append("not sure how to help you (read the docs?)") |
138 | + self.stderr.write(" ".join(msg)) |
139 | + self.stderr.write(".\n") |
140 | + except errors.InvalidGlobbing: |
141 | + # XXX this needs to actually help the user |
142 | + argv = self.argv if self.argv is not None else sys.argv |
143 | + fixed = [] |
144 | + for (i, v) in enumerate(values): |
145 | + fixed.append(v) |
146 | + if v.endswith('*'): |
147 | + break |
148 | + # values has at least one element, so i is defined |
149 | + fixed.extend('*'*(len(values)-i-1)) |
150 | + self.stderr.write("Invalid query; you cannot do a partial search" |
151 | + " twice in one query.\nPerhaps you meant: " |
152 | + "%s %s %r %s.\n" |
153 | + % (argv[0], argv[1], database, |
154 | + " ".join(map(repr, fixed)))) |
155 | + |
156 | + else: |
157 | + self.stdout.write("[") |
158 | + for i, doc in enumerate(docs): |
159 | + if i: |
160 | + self.stdout.write(",") |
161 | + self.stdout.write(simplejson.dumps(dict( |
162 | + id=doc.doc_id, |
163 | + rev=doc.rev, |
164 | + content=simplejson.loads(doc.content)), |
165 | + indent=4)) |
166 | + self.stdout.write("]\n") |
167 | + return |
168 | + return 1 |
169 | + |
170 | +client_commands.register(CmdGetFromIndex) |
171 | + |
172 | + |
173 | def main(args): |
174 | return client_commands.run_argv(args, sys.stdin, sys.stdout, sys.stderr) |
175 | |
176 | === modified file 'u1db/errors.py' |
177 | --- u1db/errors.py 2012-05-21 15:01:18 +0000 |
178 | +++ u1db/errors.py 2012-05-23 12:00:25 +0000 |
179 | @@ -44,10 +44,11 @@ |
180 | |
181 | |
182 | class InvalidValueForIndex(U1DBError): |
183 | - """The values supplied does not match the index definition. |
184 | - |
185 | - Can also be raised if wildcard matches are not strictly at the tail of the |
186 | - request. |
187 | + """The values supplied does not match the index definition.""" |
188 | + |
189 | + |
190 | +class InvalidGlobbing(U1DBError): |
191 | + """Raised if wildcard matches are not strictly at the tail of the request. |
192 | """ |
193 | |
194 | |
195 | |
196 | === modified file 'u1db/tests/c_backend_wrapper.pyx' |
197 | --- u1db/tests/c_backend_wrapper.pyx 2012-05-21 18:28:05 +0000 |
198 | +++ u1db/tests/c_backend_wrapper.pyx 2012-05-23 12:00:25 +0000 |
199 | @@ -116,6 +116,7 @@ |
200 | int U1DB_NOT_IMPLEMENTED |
201 | int U1DB_INVALID_JSON |
202 | int U1DB_INVALID_VALUE_FOR_INDEX |
203 | + int U1DB_INVALID_GLOBBING |
204 | int U1DB_BROKEN_SYNC_STREAM |
205 | int U1DB_DUPLICATE_INDEX_NAME |
206 | int U1DB_INDEX_DOES_NOT_EXIST |
207 | @@ -560,6 +561,8 @@ |
208 | % (context,)) |
209 | if status == U1DB_INVALID_VALUE_FOR_INDEX: |
210 | raise errors.InvalidValueForIndex() |
211 | + if status == U1DB_INVALID_GLOBBING: |
212 | + raise errors.InvalidGlobbing() |
213 | if status == U1DB_INTERNAL_ERROR: |
214 | raise errors.U1DBError("internal error") |
215 | if status == U1DB_BROKEN_SYNC_STREAM: |
216 | |
217 | === modified file 'u1db/tests/commandline/test_client.py' |
218 | --- u1db/tests/commandline/test_client.py 2012-05-21 17:32:38 +0000 |
219 | +++ u1db/tests/commandline/test_client.py 2012-05-23 12:00:25 +0000 |
220 | @@ -17,6 +17,7 @@ |
221 | import cStringIO |
222 | import os |
223 | import sys |
224 | +import simplejson |
225 | import subprocess |
226 | |
227 | from u1db import ( |
228 | @@ -145,6 +146,13 @@ |
229 | self.assertEqual('db', args.database) |
230 | self.assertEqual('index', args.index) |
231 | |
232 | + def test_get_from_index(self): |
233 | + args = self.parse_args(['get-from-index', 'db', 'index', 'foo']) |
234 | + self.assertEqual(client.CmdGetFromIndex, args.subcommand) |
235 | + self.assertEqual('db', args.database) |
236 | + self.assertEqual('index', args.index) |
237 | + self.assertEqual(['foo'], args.values) |
238 | + |
239 | |
240 | class TestCaseWithDB(tests.TestCase): |
241 | """These next tests are meant to have one class per Command. |
242 | @@ -472,6 +480,90 @@ |
243 | self.assertEqual(cmd.stderr.getvalue(), 'Index does not exist.\n') |
244 | |
245 | |
246 | +class TestCmdGetFromIndex(TestCaseWithDB): |
247 | + |
248 | + def test_get_from_index(self): |
249 | + self.db.create_index("index", ["key"]) |
250 | + doc1 = self.db.create_doc(tests.simple_doc) |
251 | + doc2 = self.db.create_doc(tests.nested_doc) |
252 | + cmd = self.make_command(client.CmdGetFromIndex) |
253 | + retval = cmd.run(self.db_path, "index", ["value"]) |
254 | + self.assertEqual(retval, None) |
255 | + self.assertEqual(sorted(simplejson.loads(cmd.stdout.getvalue())), |
256 | + sorted([dict(id=doc1.doc_id, |
257 | + rev=doc1.rev, |
258 | + content=simplejson.loads(doc1.content)), |
259 | + dict(id=doc2.doc_id, |
260 | + rev=doc2.rev, |
261 | + content=simplejson.loads(doc2.content)), |
262 | + ])) |
263 | + self.assertEqual(cmd.stderr.getvalue(), '') |
264 | + |
265 | + def test_get_from_index_empty(self): |
266 | + self.db.create_index("index", ["key"]) |
267 | + cmd = self.make_command(client.CmdGetFromIndex) |
268 | + retval = cmd.run(self.db_path, "index", ["value"]) |
269 | + self.assertEqual(retval, None) |
270 | + self.assertEqual(cmd.stdout.getvalue(), '[]\n') |
271 | + self.assertEqual(cmd.stderr.getvalue(), '') |
272 | + |
273 | + def test_get_from_index_no_db(self): |
274 | + cmd = self.make_command(client.CmdGetFromIndex) |
275 | + retval = cmd.run(self.db_path + "__DOES_NOT_EXIST", "foo", []) |
276 | + self.assertEqual(retval, 1) |
277 | + self.assertEqual(cmd.stdout.getvalue(), '') |
278 | + self.assertEqual(cmd.stderr.getvalue(), 'Database does not exist.\n') |
279 | + |
280 | + def test_get_from_index_no_index(self): |
281 | + cmd = self.make_command(client.CmdGetFromIndex) |
282 | + retval = cmd.run(self.db_path, "foo", []) |
283 | + self.assertEqual(retval, 1) |
284 | + self.assertEqual(cmd.stdout.getvalue(), '') |
285 | + self.assertEqual(cmd.stderr.getvalue(), 'Index does not exist.\n') |
286 | + |
287 | + def test_get_from_index_two_expr_instead_of_one(self): |
288 | + self.db.create_index("index", ["key1"]) |
289 | + cmd = self.make_command(client.CmdGetFromIndex) |
290 | + retval = cmd.run(self.db_path, "index", ["value1", "value2"]) |
291 | + self.assertEqual(retval, 1) |
292 | + self.assertEqual(cmd.stdout.getvalue(), '') |
293 | + self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index" |
294 | + " 'index' requires 1 query expression, not 2.\n") |
295 | + |
296 | + def test_get_from_index_three_expr_instead_of_two(self): |
297 | + self.db.create_index("index", ["key1", "key2"]) |
298 | + cmd = self.make_command(client.CmdGetFromIndex) |
299 | + retval = cmd.run(self.db_path, "index", ["value1", "value2", "value3"]) |
300 | + self.assertEqual(retval, 1) |
301 | + self.assertEqual(cmd.stdout.getvalue(), '') |
302 | + self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index" |
303 | + " 'index' requires 2 query expressions, not 3.\n") |
304 | + |
305 | + def test_get_from_index_one_expr_instead_of_two(self): |
306 | + self.db.create_index("index", ["key1", "key2"]) |
307 | + cmd = self.make_command(client.CmdGetFromIndex) |
308 | + cmd.argv = ["XX", "YY"] |
309 | + retval = cmd.run(self.db_path, "index", ["value1"]) |
310 | + self.assertEqual(retval, 1) |
311 | + self.assertEqual(cmd.stdout.getvalue(), '') |
312 | + self.assertEqual(cmd.stderr.getvalue(), "Invalid query; index" |
313 | + " 'index' requires 2 query expressions, not 1.\n" |
314 | + "Perhaps you meant: XX YY %r 'value1' '*'.\n" |
315 | + % self.db_path) |
316 | + |
317 | + def test_get_from_index_cant_bad_glob(self): |
318 | + self.db.create_index("index", ["key1", "key2"]) |
319 | + cmd = self.make_command(client.CmdGetFromIndex) |
320 | + cmd.argv = ["XX", "YY"] |
321 | + retval = cmd.run(self.db_path, "index", ["value1*", "value2"]) |
322 | + self.assertEqual(retval, 1) |
323 | + self.assertEqual(cmd.stdout.getvalue(), '') |
324 | + self.assertEqual(cmd.stderr.getvalue(), "Invalid query; you" |
325 | + " cannot do a partial search twice in one query.\n" |
326 | + "Perhaps you meant: XX YY %r 'value1*' '*'.\n" |
327 | + % self.db_path) |
328 | + |
329 | + |
330 | class RunMainHelper(object): |
331 | |
332 | def run_main(self, args, stdin=None): |
333 | |
334 | === modified file 'u1db/tests/test_backends.py' |
335 | --- u1db/tests/test_backends.py 2012-05-21 16:29:25 +0000 |
336 | +++ u1db/tests/test_backends.py 2012-05-23 12:00:25 +0000 |
337 | @@ -769,12 +769,12 @@ |
338 | |
339 | def test_get_from_index_illegal_wildcard_order(self): |
340 | self.db.create_index('test-idx', ['k1', 'k2']) |
341 | - self.assertRaises(errors.InvalidValueForIndex, |
342 | + self.assertRaises(errors.InvalidGlobbing, |
343 | self.db.get_from_index, 'test-idx', [('*', 'v2')]) |
344 | |
345 | def test_get_from_index_illegal_glob_after_wildcard(self): |
346 | self.db.create_index('test-idx', ['k1', 'k2']) |
347 | - self.assertRaises(errors.InvalidValueForIndex, |
348 | + self.assertRaises(errors.InvalidGlobbing, |
349 | self.db.get_from_index, 'test-idx', [('*', 'v*')]) |
350 | |
351 | def test_get_all_from_index(self): |
352 | @@ -891,13 +891,13 @@ |
353 | |
354 | def test_get_from_index_illegal_glob_before_value(self): |
355 | self.db.create_index('test-idx', ['k1', 'k2']) |
356 | - self.assertRaises(errors.InvalidValueForIndex, |
357 | + self.assertRaises(errors.InvalidGlobbing, |
358 | self.db.get_from_index, 'test-idx', [('v*', 'v2')]) |
359 | |
360 | def test_get_from_index_illegal_glob_after_glob(self): |
361 | self.db.create_index('test-idx', ['k1', 'k2']) |
362 | - self.assertRaises(errors.InvalidValueForIndex, |
363 | - self.db.get_from_index, 'test-idx', [('*', 'v*')]) |
364 | + self.assertRaises(errors.InvalidGlobbing, |
365 | + self.db.get_from_index, 'test-idx', [('v*', 'v*')]) |
366 | |
367 | def test_get_from_index_with_sql_wildcards(self): |
368 | self.db.create_index('test-idx', ['key']) |
369 | |
370 | === modified file 'u1db/tests/test_inmemory.py' |
371 | --- u1db/tests/test_inmemory.py 2012-04-27 14:22:47 +0000 |
372 | +++ u1db/tests/test_inmemory.py 2012-05-23 12:00:25 +0000 |
373 | @@ -124,5 +124,5 @@ |
374 | idx._find_non_wildcards, ('a', 'b')) |
375 | self.assertRaises(errors.InvalidValueForIndex, |
376 | idx._find_non_wildcards, ('a', 'b', 'c', 'd')) |
377 | - self.assertRaises(errors.InvalidValueForIndex, |
378 | + self.assertRaises(errors.InvalidGlobbing, |
379 | idx._find_non_wildcards, ('*', 'b', 'c')) |