Merge lp:~chipaca/u1db/split_out_bad_globbage into lp:u1db

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
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.

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'))

Subscribers

People subscribed via source and target branches