Merge lp:~thisfred/u1db/get_all_the_docs into lp:u1db

Proposed by Eric Casteleijn
Status: Merged
Approved by: Eric Casteleijn
Approved revision: 315
Merged at revision: 316
Proposed branch: lp:~thisfred/u1db/get_all_the_docs
Merge into: lp:u1db
Diff against target: 230 lines (+133/-0)
7 files modified
include/u1db/u1db.h (+16/-0)
src/u1db.c (+39/-0)
u1db/__init__.py (+12/-0)
u1db/backends/inmemory.py (+10/-0)
u1db/backends/sqlite_backend.py (+13/-0)
u1db/tests/c_backend_wrapper.pyx (+15/-0)
u1db/tests/test_backends.py (+28/-0)
To merge this branch: bzr merge lp:~thisfred/u1db/get_all_the_docs
Reviewer Review Type Date Requested Status
Samuele Pedroni Approve
Review via email: mp+107388@code.launchpad.net

Commit message

added db.get_all_docs() method to all backends that returns all (non-deleted) documents.

Description of the change

added db.get_all_docs() method to all backends that returns all (non-deleted) documents.

To post a comment you must log in.
Revision history for this message
John Lenton (chipaca) wrote :

Two questions: 1. don't we want to return a generation number, so you can call get_all_docs and then move to whats_changed from there on out? 2. isn't this a candidate for include_deleted?

Revision history for this message
Eric Casteleijn (thisfred) wrote :

thanks,

1. is a fair point, though I'm not sure what the best way to return the generation is
2. I had thought about but didn't add yet, should not be hard, I'll add it tomorrow

Revision history for this message
John A Meinel (jameinel) wrote :

On 5/28/2012 4:09 PM, Eric Casteleijn wrote:
> thanks,
>
> 1. is a fair point, though I'm not sure what the best way to return the generation is
> 2. I had thought about but didn't add yet, should not be hard, I'll add it tomorrow
>

It needs to be a return parameter so that it it accurate to the point of
the documents we return.

The cheap way to do it, is to call db._get_generation() at the
beginning, then just iterate whatever documents, and then return the
value we saw at the beginning.

Concurrent updates then will have you see the same value twice, but you
won't ever miss content.

John
=:->

lp:~thisfred/u1db/get_all_the_docs updated
313. By Eric Casteleijn

changed api definition

314. By Eric Casteleijn

merged trunk

315. By Eric Casteleijn

added include_deleted parameter, and included db generation in results

Revision history for this message
Samuele Pedroni (pedronis) :
review: Approve

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-24 21:09:21 +0000
3+++ include/u1db/u1db.h 2012-05-29 14:35:21 +0000
4@@ -143,6 +143,7 @@
5 * Get the document defined by the given document id.
6 *
7 * @param doc_id The document we are looking for
8+ * @param include_deleted If true, return the document even if it was deleted.
9 * @param doc (OUT) a document (or NULL) matching the request
10 * @return status, will be U1DB_OK if there is no error, even if there is no
11 * document matching that doc_id.
12@@ -158,6 +159,7 @@
13 * @param doc_ids An array of document ids to retrieve.
14 * @param check_for_conflicts If true, check if each document has any
15 * conflicts, if false, the conflict checking will be skipped.
16+ * @param include_deleted If true, return documents even if they were deleted.
17 * @param context A void* that is returned via the callback function.
18 * @param cb This will be called with each document requested. The api is
19 * cb(void* context, u1db_document *doc). The returned documents are
20@@ -169,6 +171,20 @@
21 u1db_doc_callback cb);
22
23 /**
24+ * Retrieve all documents from the database.
25+ *
26+ * @param include_deleted If true, return documents even if they were deleted.
27+ * @param generation (OUT) the generation the database is at
28+ * @param context A void* that is returned via the callback function.
29+ * @param cb This will be called with each document requested. The api is
30+ * cb(void* context, u1db_document *doc). The returned documents are
31+ * allocated on the heap, and must be freed by the caller via
32+ * u1db_free_doc.
33+ */
34+int u1db_get_all_docs(u1database *db, int include_deleted, int *generation,
35+ void *context, u1db_doc_callback cb);
36+
37+/**
38 * Get all of the contents associated with a conflicted document.
39 *
40 * If a document is not conflicted, then this will not invoke the callback
41
42=== modified file 'src/u1db.c'
43--- src/u1db.c 2012-05-25 20:33:57 +0000
44+++ src/u1db.c 2012-05-29 14:35:21 +0000
45@@ -1037,6 +1037,45 @@
46 return status;
47 }
48
49+int
50+u1db_get_all_docs(u1database *db, int include_deleted, int *generation,
51+ void *context, u1db_doc_callback cb)
52+{
53+ int status;
54+ sqlite3_stmt *statement;
55+
56+ if (db == NULL || cb == NULL) {
57+ return U1DB_INVALID_PARAMETER;
58+ }
59+ status = u1db__get_generation(db, generation);
60+ if (status != U1DB_OK)
61+ return status;
62+ status = sqlite3_prepare_v2(db->sql_handle,
63+ "SELECT doc_id, doc_rev, content FROM document", -1, &statement, NULL);
64+ if (status != SQLITE_OK) { goto finish; }
65+ status = sqlite3_step(statement);
66+ while (status == SQLITE_ROW) {
67+ const char *doc_id;
68+ const char *revision;
69+ const char *content;
70+ u1db_document *doc;
71+ doc_id = (char *)sqlite3_column_text(statement, 0);
72+ revision = (char *)sqlite3_column_text(statement, 1);
73+ content = (char *)sqlite3_column_text(statement, 2);
74+ if (content != NULL || include_deleted) {
75+ doc = u1db__allocate_document(doc_id, revision, content, 0);
76+ cb(context, doc);
77+ }
78+ status = sqlite3_step(statement);
79+ }
80+ if (status == SQLITE_DONE) {
81+ status = SQLITE_OK;
82+ }
83+finish:
84+ sqlite3_finalize(statement);
85+ return status;
86+}
87+
88 // Take cur_rev, and update it to have a version incremented based on the
89 // database replica uid
90 static int
91
92=== modified file 'u1db/__init__.py'
93--- u1db/__init__.py 2012-05-24 21:09:21 +0000
94+++ u1db/__init__.py 2012-05-29 14:35:21 +0000
95@@ -93,6 +93,18 @@
96 """
97 raise NotImplementedError(self.get_docs)
98
99+ def get_all_docs(self, include_deleted=False):
100+ """Get the JSON content for all documents in the database.
101+
102+ :param include_deleted: If set to True, deleted documents will be
103+ returned with empty content. Otherwise deleted documents will not
104+ be included in the results.
105+ :return: (generation, [Document])
106+ The current generation of the database, followed by a list of all
107+ the documents in the database.
108+ """
109+ raise NotImplementedError(self.get_all_docs)
110+
111 def create_doc(self, content, doc_id=None):
112 """Create a new document.
113
114
115=== modified file 'u1db/backends/inmemory.py'
116--- u1db/backends/inmemory.py 2012-05-25 20:33:57 +0000
117+++ u1db/backends/inmemory.py 2012-05-29 14:35:21 +0000
118@@ -117,6 +117,16 @@
119 doc.has_conflicts = (doc.doc_id in self._conflicts)
120 return doc
121
122+ def get_all_docs(self, include_deleted=False):
123+ """Return all documents in the database."""
124+ generation = self._get_generation()
125+ results = []
126+ for doc_id, (doc_rev, content) in self._docs.items():
127+ if content is None and not include_deleted:
128+ continue
129+ results.append(Document(doc_id, doc_rev, content))
130+ return (generation, results)
131+
132 def get_doc_conflicts(self, doc_id):
133 if doc_id not in self._conflicts:
134 return []
135
136=== modified file 'u1db/backends/sqlite_backend.py'
137--- u1db/backends/sqlite_backend.py 2012-05-25 20:33:57 +0000
138+++ u1db/backends/sqlite_backend.py 2012-05-29 14:35:21 +0000
139@@ -301,6 +301,19 @@
140 doc.has_conflicts = self._has_conflicts(doc.doc_id)
141 return doc
142
143+ def get_all_docs(self, include_deleted=False):
144+ """Get all documents from the database."""
145+ generation = self._get_generation()
146+ results = []
147+ c = self._db_handle.cursor()
148+ c.execute("SELECT doc_id, doc_rev, content FROM document;")
149+ rows = c.fetchall()
150+ for doc_id, doc_rev, content in rows:
151+ if content is None and not include_deleted:
152+ continue
153+ results.append(Document(doc_id, doc_rev, content))
154+ return (generation, results)
155+
156 def put_doc(self, doc):
157 if doc.doc_id is None:
158 raise errors.InvalidDocId()
159
160=== modified file 'u1db/tests/c_backend_wrapper.pyx'
161--- u1db/tests/c_backend_wrapper.pyx 2012-05-24 21:09:21 +0000
162+++ u1db/tests/c_backend_wrapper.pyx 2012-05-29 14:35:21 +0000
163@@ -76,6 +76,8 @@
164 int u1db_get_docs(u1database *db, int n_doc_ids, const_char_ptr *doc_ids,
165 int check_for_conflicts, int include_deleted,
166 void *context, u1db_doc_callback cb)
167+ int u1db_get_all_docs(u1database *db, int include_deleted, int *generation,
168+ void *context, u1db_doc_callback cb)
169 int u1db_put_doc(u1database *db, u1db_document *doc)
170 int u1db__put_doc_if_newer(u1database *db, u1db_document *doc,
171 int save_conflict, char *replica_uid,
172@@ -934,6 +936,19 @@
173 free(<void*>c_doc_ids)
174 return a_list
175
176+ def get_all_docs(self, include_deleted=False):
177+ cdef int c_generation
178+
179+ a_list = []
180+ deleted = 1 if include_deleted else 0
181+ generation = 0
182+ c_generation = generation
183+ handle_status(
184+ "get_all_docs", u1db_get_all_docs(
185+ self._db, deleted, &c_generation, <void*>a_list,
186+ _append_doc_to_list))
187+ return (c_generation, a_list)
188+
189 def resolve_doc(self, CDocument doc, conflicted_doc_revs):
190 cdef const_char_ptr *revs
191 cdef int n_revs
192
193=== modified file 'u1db/tests/test_backends.py'
194--- u1db/tests/test_backends.py 2012-05-25 20:33:57 +0000
195+++ u1db/tests/test_backends.py 2012-05-29 14:35:21 +0000
196@@ -281,6 +281,34 @@
197 def test_get_docs_empty_list(self):
198 self.assertEqual([], self.db.get_docs([]))
199
200+ def test_get_all_docs_empty(self):
201+ self.assertEqual([], self.db.get_all_docs()[1])
202+
203+ def test_get_all_docs(self):
204+ doc1 = self.db.create_doc(simple_doc)
205+ doc2 = self.db.create_doc(nested_doc)
206+ self.assertEqual(
207+ sorted([doc1, doc2]), sorted(self.db.get_all_docs()[1]))
208+
209+ def test_get_all_docs_exclude_deleted(self):
210+ doc1 = self.db.create_doc(simple_doc)
211+ doc2 = self.db.create_doc(nested_doc)
212+ self.db.delete_doc(doc2)
213+ self.assertEqual([doc1], self.db.get_all_docs()[1])
214+
215+ def test_get_all_docs_include_deleted(self):
216+ doc1 = self.db.create_doc(simple_doc)
217+ doc2 = self.db.create_doc(nested_doc)
218+ self.db.delete_doc(doc2)
219+ self.assertEqual(
220+ sorted([doc1, doc2]),
221+ sorted(self.db.get_all_docs(include_deleted=True)[1]))
222+
223+ def test_get_all_docs_generation(self):
224+ self.db.create_doc(simple_doc)
225+ self.db.create_doc(nested_doc)
226+ self.assertEqual(2, self.db.get_all_docs()[0])
227+
228 def test_simple_put_doc_if_newer(self):
229 doc = self.make_document('my-doc-id', 'test:1', simple_doc)
230 state_at_gen = self.db._put_doc_if_newer(doc, save_conflict=False)

Subscribers

People subscribed via source and target branches