xapian-core  1.4.26
api_closedb.cc
Go to the documentation of this file.
1 
4 /* Copyright 2008,2009 Lemur Consulting Ltd
5  * Copyright 2009,2012,2015 Olly Betts
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include <config.h>
23 
24 #include "api_closedb.h"
25 
26 #include <xapian.h>
27 
28 #include "safeunistd.h"
29 
30 #include "apitest.h"
31 #include "testutils.h"
32 
33 using namespace std;
34 
35 #define COUNT_CLOSEDEXC(CODE) \
36  try { \
37  CODE; \
38  } catch (const Xapian::DatabaseClosedError &) { \
39  ++closedexc_count; \
40  }
41 
42 #define IF_NOT_CLOSEDEXC(CODE) \
43  do { \
44  hadexc = false; \
45  try { \
46  CODE; \
47  } catch (const Xapian::DatabaseClosedError &) { \
48  ++closedexc_count; \
49  hadexc = true; \
50  } \
51  } while (false); if (hadexc)
52 
53 // Iterators used by closedb1.
67 
68  void setup(Xapian::Database db_) {
69  db = db_;
70 
71  // Set up the iterators for the test.
72  pl1 = db.postlist_begin("paragraph");
73  pl2 = db.postlist_begin("this");
74  ++pl2;
75  pl1end = db.postlist_end("paragraph");
76  pl2end = db.postlist_end("this");
77  tl1 = db.termlist_begin(1);
78  tlend = db.termlist_end(1);
79  atl1 = db.allterms_begin("t");
80  atlend = db.allterms_end("t");
81  pil1 = db.positionlist_begin(1, "paragraph");
82  pilend = db.positionlist_end(1, "paragraph");
83  }
84 
85  int perform() {
86  int closedexc_count = 0;
87  bool hadexc;
88 
89  // Getting a document may throw closed.
90  IF_NOT_CLOSEDEXC(doc1 = db.get_document(1)) {
91  COUNT_CLOSEDEXC(TEST_EQUAL(doc1.get_data().substr(0, 33),
92  "This is a test document used with"));
94  }
95 
96  // Causing the database to access its files raises the "database
97  // closed" error.
98  COUNT_CLOSEDEXC(db.postlist_begin("paragraph"));
101  COUNT_CLOSEDEXC(db.positionlist_begin(1, "paragraph"));
104  COUNT_CLOSEDEXC(db.get_termfreq("paragraph"));
105  COUNT_CLOSEDEXC(db.get_collection_freq("paragraph"));
106  COUNT_CLOSEDEXC(db.term_exists("paragraph"));
113 
114  // Reopen raises the "database closed" error.
115  COUNT_CLOSEDEXC(db.reopen());
116 
117  TEST_NOT_EQUAL(pl1, pl1end);
118  TEST_NOT_EQUAL(pl2, pl2end);
119  TEST_NOT_EQUAL(tl1, tlend);
120  TEST_NOT_EQUAL(atl1, atlend);
121  TEST_NOT_EQUAL(pil1, pilend);
122 
123  COUNT_CLOSEDEXC(db.postlist_begin("paragraph"));
124 
125  COUNT_CLOSEDEXC(TEST_EQUAL(*pl1, 1));
128 
129  COUNT_CLOSEDEXC(TEST_EQUAL(*pl2, 2));
132 
133  COUNT_CLOSEDEXC(TEST_EQUAL(*tl1, "a"));
136 
137  COUNT_CLOSEDEXC(TEST_EQUAL(*atl1, "test"));
139 
140  COUNT_CLOSEDEXC(TEST_EQUAL(*pil1, 12));
141 
142  // Advancing the iterator may or may not raise an error, but if it
143  // doesn't it must return the correct answers.
144  bool advanced = false;
145  try {
146  ++pl1;
147  advanced = true;
148  } catch (const Xapian::DatabaseClosedError &) {}
149 
150  if (advanced) {
151  COUNT_CLOSEDEXC(TEST_EQUAL(*pl1, 2));
154  }
155 
156  advanced = false;
157  try {
158  ++pl2;
159  advanced = true;
160  } catch (const Xapian::DatabaseClosedError &) {}
161 
162  if (advanced) {
163  COUNT_CLOSEDEXC(TEST_EQUAL(*pl2, 3));
166  }
167 
168  advanced = false;
169  try {
170  ++tl1;
171  advanced = true;
172  } catch (const Xapian::DatabaseClosedError &) {}
173 
174  if (advanced) {
175  COUNT_CLOSEDEXC(TEST_EQUAL(*tl1, "api"));
178  }
179 
180  advanced = false;
181  try {
182  ++atl1;
183  advanced = true;
184  } catch (const Xapian::DatabaseClosedError &) {}
185 
186  if (advanced) {
187  COUNT_CLOSEDEXC(TEST_EQUAL(*atl1, "that"));
189  }
190 
191  advanced = false;
192  try {
193  ++pil1;
194  advanced = true;
195  } catch (const Xapian::DatabaseClosedError &) {}
196 
197  if (advanced) {
198  COUNT_CLOSEDEXC(TEST_EQUAL(*pil1, 28));
199  }
200 
201  return closedexc_count;
202  }
203 };
204 
205 // Test for closing a database
206 DEFINE_TESTCASE(closedb1, backend) {
207  Xapian::Database db(get_database("apitest_simpledata"));
208  closedb1_iterators iters;
209 
210  // Run the test, checking that we get no "closed" exceptions.
211  iters.setup(db);
212  int closedexc_count = iters.perform();
213  TEST_EQUAL(closedexc_count, 0);
214 
215  // Setup for the next test.
216  iters.setup(db);
217 
218  // Close the database.
219  db.close();
220 
221  // Dup stdout to the fds which the database was using, to try to catch
222  // issues with lingering references to closed fds (regression test for
223  // early development versions of honey).
224  vector<int> fds;
225  for (int i = 0; i != 6; ++i) {
226  fds.push_back(dup(1));
227  }
228 
229  // Reopening a closed database should always raise DatabaseClosedError.
231 
232  // Run the test again, checking that we get some "closed" exceptions.
233  closedexc_count = iters.perform();
234  TEST_NOT_EQUAL(closedexc_count, 0);
235 
236  // get_description() shouldn't throw an exception. Actually do something
237  // with the description, in case this method is marked as "pure" in the
238  // future.
239  TEST(!db.get_description().empty());
240 
241  // Calling close repeatedly is okay.
242  db.close();
243 
244  for (int fd : fds) {
245  close(fd);
246  }
247 }
248 
249 // Test closing a writable database, and that it drops the lock.
250 DEFINE_TESTCASE(closedb2, writable && path) {
251  Xapian::WritableDatabase dbw1(get_named_writable_database("apitest_closedb2"));
254  Xapian::DB_OPEN));
255  dbw1.close();
256  Xapian::WritableDatabase dbw2 = get_named_writable_database("apitest_closedb2");
258  dbw1.postlist_begin("paragraph"));
259  TEST_EQUAL(dbw2.postlist_begin("paragraph"), dbw2.postlist_end("paragraph"));
260 }
261 
263 DEFINE_TESTCASE(closedb3, backend) {
264  Xapian::Database db(get_database("etext"));
265  const string & uuid = db.get_uuid();
266  db.close();
267  try {
268  TEST_EQUAL(db.get_uuid(), uuid);
269  } catch (const Xapian::DatabaseClosedError &) {
270  }
271  try {
272  TEST(db.has_positions());
273  } catch (const Xapian::DatabaseClosedError &) {
274  }
275  try {
276  TEST_EQUAL(db.get_doccount(), 566);
277  } catch (const Xapian::DatabaseClosedError &) {
278  }
279  try {
280  TEST_EQUAL(db.get_lastdocid(), 566);
281  } catch (const Xapian::DatabaseClosedError &) {
282  }
283  try {
285  } catch (const Xapian::DatabaseClosedError &) {
286  }
287  try {
289  } catch (const Xapian::DatabaseClosedError &) {
290  }
291  try {
292  TEST(db.get_wdf_upper_bound("king"));
293  } catch (const Xapian::DatabaseClosedError &) {
294  }
295  try {
296  // For non-remote databases, keep_alive() is a no-op anyway.
297  db.keep_alive();
298  } catch (const Xapian::DatabaseClosedError &) {
299  }
300 }
301 
303 DEFINE_TESTCASE(closedb4, writable && !inmemory) {
306  TEST_EQUAL(wdb.get_doccount(), 1);
307  wdb.close();
309  TEST_EQUAL(db.get_doccount(), 1);
310 }
311 
313 DEFINE_TESTCASE(closedb5, transactions) {
314  {
315  // If a transaction is active, close() shouldn't implicitly commit().
317  wdb.begin_transaction();
319  TEST_EQUAL(wdb.get_doccount(), 1);
320  wdb.close();
322  TEST_EQUAL(db.get_doccount(), 0);
323  }
324 
325  {
326  // Same test but for an unflushed transaction.
328  wdb.begin_transaction(false);
330  TEST_EQUAL(wdb.get_doccount(), 1);
331  wdb.close();
333  TEST_EQUAL(db.get_doccount(), 0);
334  }
335 
336  {
337  // commit_transaction() throws InvalidOperationError when
338  // not in a transaction.
340  wdb.close();
342  wdb.commit_transaction());
343 
344  // begin_transaction() is no-op or throws DatabaseClosedError. We may be
345  // able to call db.begin_transaction(), but we can't make any changes
346  // inside that transaction. If begin_transaction() succeeds, then
347  // commit_transaction() either end the transaction or throw
348  // DatabaseClosedError.
349  bool advanced = false;
350  try {
351  wdb.begin_transaction();
352  advanced = true;
353  } catch (const Xapian::DatabaseClosedError &) {
354  }
355  if (advanced) {
356  try {
357  wdb.commit_transaction();
358  } catch (const Xapian::DatabaseClosedError &) {
359  }
360  }
361  }
362 
363  {
364  // Same test but for cancel_transaction().
366  wdb.close();
368  wdb.cancel_transaction());
369 
370  bool advanced = false;
371  try {
372  wdb.begin_transaction();
373  advanced = true;
374  } catch (const Xapian::DatabaseClosedError &) {
375  }
376  if (advanced) {
377  try {
378  wdb.cancel_transaction();
379  } catch (const Xapian::DatabaseClosedError &) {
380  }
381  }
382  }
383 }
384 
386 DEFINE_TESTCASE(closedb6, remote) {
387  Xapian::Database db(get_database("etext"));
388  db.close();
389 
390  try {
391  db.keep_alive();
392  FAIL_TEST("Expected DatabaseClosedError wasn't thrown");
393  } catch (const Xapian::DatabaseClosedError &) {
394  }
395 }
396 
397 // Test WritableDatabase methods.
398 DEFINE_TESTCASE(closedb7, writable) {
401  db.close();
402 
403  // Since we can't make any changes which need to be committed,
404  // db.commit() is a no-op, and so doesn't have to fail.
405  try {
406  db.commit();
407  } catch (const Xapian::DatabaseClosedError &) {
408  }
409 
413  db.delete_document(1));
419  db.replace_document("Qi", Xapian::Document()));
420 }
421 
422 // Test spelling related methods.
423 DEFINE_TESTCASE(closedb8, writable && spelling) {
425  db.add_spelling("pneumatic");
426  db.add_spelling("pneumonia");
427  db.close();
428 
430  db.add_spelling("penmanship"));
432  db.remove_spelling("pneumatic"));
434  db.get_spelling_suggestion("newmonia"));
436  db.spellings_begin());
437 }
438 
439 // Test synonym related methods.
440 DEFINE_TESTCASE(closedb9, writable && synonyms) {
442  db.add_synonym("color", "colour");
443  db.add_synonym("honor", "honour");
444  db.close();
445 
447  db.add_synonym("behavior", "behaviour"));
449  db.remove_synonym("honor", "honour"));
451  db.clear_synonyms("honor"));
453  db.synonyms_begin("color"));
455  db.synonym_keys_begin());
456 }
457 
458 // Test metadata related methods.
459 DEFINE_TESTCASE(closedb10, writable && metadata) {
461  db.set_metadata("foo", "FOO");
462  db.set_metadata("bar", "BAR");
463  db.close();
464 
466  db.set_metadata("test", "TEST"));
468  db.get_metadata("foo"));
470  db.get_metadata("bar"));
472  db.metadata_keys_begin());
473 }
int close(FD &fd)
Definition: fd.h:63
Xapian::Document get_document(Xapian::docid did) const
Get a document from the database, given its document id.
Definition: omdatabase.cc:490
Xapian::docid add_document(const Xapian::Document &document)
Add a new document to the database.
Definition: omdatabase.cc:902
PositionIterator positionlist_end(Xapian::docid, const std::string &) const
Corresponding end iterator to positionlist_begin().
Definition: database.h:254
TermIterator termlist_begin(Xapian::docid did) const
An iterator pointing to the start of the termlist for a given document.
Definition: omdatabase.cc:198
Indicates an attempt to access a closed database.
Definition: error.h:1097
void cancel_transaction()
Abort the transaction currently in progress, discarding the pending modifications made to the databas...
Definition: omdatabase.cc:890
#define TEST(a)
Test a condition, without an additional explanation for failure.
Definition: testsuite.h:275
This class is used to access a database, or a group of databases.
Definition: database.h:68
void remove_spelling(const std::string &word, Xapian::termcount freqdec=1) const
Remove a word from the spelling dictionary.
Definition: omdatabase.cc:1015
InvalidOperationError indicates the API was used in an invalid way.
Definition: error.h:283
PositionIterator positionlist_begin(Xapian::docid did, const std::string &tname) const
An iterator pointing to the start of the position list for a given term in a given document...
Definition: omdatabase.cc:250
virtual std::string get_description() const
Return a string describing this object.
Definition: omdatabase.cc:548
Xapian::termcount get_doclength_lower_bound() const
Get a lower bound on the length of a document in this DB.
Definition: omdatabase.cc:401
bool has_positions() const
Does this database have any positional information?
Definition: omdatabase.cc:238
TermIterator allterms_end(const std::string &=std::string()) const
Corresponding end iterator to allterms_begin(prefix).
Definition: database.h:269
Xapian::WritableDatabase get_writable_database(const string &dbname)
Definition: apitest.cc:87
Xapian::PostingIterator pl2
Definition: api_closedb.cc:58
Xapian::docid get_lastdocid() const
Get the highest document id which has been used in the database.
Definition: omdatabase.cc:279
void begin_transaction(bool flushed=true)
Begin a transaction.
Definition: omdatabase.cc:868
Xapian::doccount get_termfreq() const
Return the term frequency for the term at the current position.
bool reopen()
Re-open the database.
Definition: omdatabase.cc:125
STL namespace.
Xapian::PostingIterator pl1
Definition: api_closedb.cc:57
std::string get_value_upper_bound(Xapian::valueno slot) const
Get an upper bound on the values stored in the given value slot.
Definition: omdatabase.cc:386
void replace_document(Xapian::docid did, const Xapian::Document &document)
Replace a given document in the database.
Definition: omdatabase.cc:952
void set_metadata(const std::string &key, const std::string &metadata)
Set the user-specified metadata associated with a given key.
Definition: omdatabase.cc:1064
#define IF_NOT_CLOSEDEXC(CODE)
Definition: api_closedb.cc:42
Xapian::doccount get_doccount() const
Get the number of documents in the database.
Definition: omdatabase.cc:267
std::string get_metadata(const std::string &key) const
Get the user-specified metadata associated with a given key.
Definition: omdatabase.cc:758
Xapian::WritableDatabase get_named_writable_database(const std::string &name, const std::string &source)
Definition: apitest.cc:93
test functionality of the Xapian API
Xapian::doclength get_avlength() const
Get the average length of the documents in the database.
Definition: omdatabase.cc:293
Xapian::Document doc1
Definition: api_closedb.cc:56
Class for iterating over a list of terms.
Definition: termiterator.h:41
Xapian::PositionIterator pil1
Definition: api_closedb.cc:65
#define TEST_REL(A, REL, B)
Test a relation holds,e.g. TEST_REL(a,>,b);.
Definition: testmacros.h:32
Class for iterating over a list of terms.
#define TEST_NOT_EQUAL(a, b)
Test for non-equality of two things.
Definition: testsuite.h:305
Xapian::termcount get_doclength_upper_bound() const
Get an upper bound on the length of a document in this DB.
Definition: omdatabase.cc:421
void remove_synonym(const std::string &term, const std::string &synonym) const
Remove a synonym for a term.
Definition: omdatabase.cc:1039
std::string get_named_writable_database_path(const std::string &name)
Definition: apitest.cc:99
DatabaseLockError indicates failure to lock a database.
Definition: error.h:493
const int DB_OPEN
Open an existing database.
Definition: constants.h:50
This class provides read/write access to a database.
Definition: database.h:789
Xapian::TermIterator spellings_begin() const
An iterator which returns all the spelling correction targets.
Definition: omdatabase.cc:704
void setup(Xapian::Database db_)
Definition: api_closedb.cc:68
Public interfaces for the Xapian library.
Xapian::TermIterator synonym_keys_begin(const std::string &prefix=std::string()) const
An iterator which returns all terms which have synonyms.
Definition: omdatabase.cc:740
void delete_document(Xapian::docid did)
Delete a document from the database.
Definition: omdatabase.cc:925
#define TEST_EXCEPTION(TYPE, CODE)
Check that CODE throws exactly Xapian exception TYPE.
Definition: testutils.h:109
Xapian::termcount get_doclength(Xapian::docid did) const
Get the length of a document.
Definition: omdatabase.cc:461
void commit()
Commit any pending modifications made to the database.
Definition: omdatabase.cc:857
Xapian::Database get_writable_database_as_database()
Definition: apitest.cc:119
Class for iterating over term positions.
Xapian::TermIterator tlend
Definition: api_closedb.cc:62
Xapian::termcount get_wdf() const
Return the wdf for the term at the current position.
TermIterator allterms_begin(const std::string &prefix=std::string()) const
An iterator which runs across all terms with a given prefix.
Definition: omdatabase.cc:223
TermIterator termlist_end(Xapian::docid) const
Corresponding end iterator to termlist_begin().
Definition: database.h:240
Xapian::TermIterator synonyms_begin(const std::string &term) const
An iterator which returns all the synonyms for a given term.
Definition: omdatabase.cc:722
ValueIterator valuestream_begin(Xapian::valueno slot) const
Return an iterator over the value in slot slot for each document.
Definition: omdatabase.cc:450
#define COUNT_CLOSEDEXC(CODE)
Definition: api_closedb.cc:35
std::string get_spelling_suggestion(const std::string &word, unsigned max_edit_distance=2) const
Suggest a spelling correction.
Definition: omdatabase.cc:594
bool term_exists(const std::string &tname) const
Check if a given term exists in the database.
Definition: omdatabase.cc:524
void clear_synonyms(const std::string &term) const
Remove all synonyms for a term.
Definition: omdatabase.cc:1052
#define FAIL_TEST(MSG)
Fail the current testcase with message MSG.
Definition: testsuite.h:68
Xapian::Database get_database(const string &dbname)
Definition: apitest.cc:48
Xapian::TermIterator atl1
Definition: api_closedb.cc:63
Xapian::termcount get_doclength() const
Return the length of the document at the current position.
std::string get_value_lower_bound(Xapian::valueno slot) const
Get a lower bound on the values stored in the given value slot.
Definition: omdatabase.cc:368
Xapian-specific test helper functions and macros.
void commit_transaction()
Complete the transaction currently in progress.
Definition: omdatabase.cc:879
DEFINE_TESTCASE(closedb1, backend)
Definition: api_closedb.cc:206
Xapian::TermIterator atlend
Definition: api_closedb.cc:64
Xapian::TermIterator metadata_keys_begin(const std::string &prefix=std::string()) const
An iterator which returns all user-specified metadata keys.
Definition: omdatabase.cc:768
void keep_alive()
Send a "keep-alive" to remote databases to stop them timing out.
Definition: omdatabase.cc:538
<unistd.h>, but with compat.
Xapian::Database db
Definition: api_closedb.cc:55
void add_synonym(const std::string &term, const std::string &synonym) const
Add a synonym for a term.
Definition: omdatabase.cc:1028
std::string get_data() const
Get data stored in the document.
Definition: omdocument.cc:71
#define TEST_EQUAL(a, b)
Test for equality of two things.
Definition: testsuite.h:278
Xapian::termcount get_unique_terms(Xapian::docid did) const
Get the number of unique terms in document.
Definition: omdatabase.cc:476
PostingIterator postlist_end(const std::string &) const
Corresponding end iterator to postlist_begin().
Definition: database.h:225
Xapian::PositionIterator pilend
Definition: api_closedb.cc:66
Xapian::TermIterator tl1
Definition: api_closedb.cc:61
std::string get_value(Xapian::valueno slot) const
Get value by number.
Definition: omdocument.cc:64
Xapian::PostingIterator pl1end
Definition: api_closedb.cc:59
virtual void close()
Close the database.
Definition: omdatabase.cc:138
TermIterator termlist_begin() const
Start iterating the terms in this document.
Definition: omdocument.cc:197
Xapian::doccount get_termfreq(const std::string &tname) const
Get the number of documents in the database indexed by a given term.
Definition: omdatabase.cc:323
Xapian::doccount get_value_freq(Xapian::valueno slot) const
Return the frequency of a given value slot.
Definition: omdatabase.cc:355
A handle representing a document in a Xapian database.
Definition: document.h:61
Xapian::termcount get_unique_terms() const
Return the number of unique terms in the current document.
std::string get_uuid() const
Get a UUID for the database.
Definition: omdatabase.cc:776
Xapian::PostingIterator pl2end
Definition: api_closedb.cc:60
void add_spelling(const std::string &word, Xapian::termcount freqinc=1) const
Add a word to the spelling dictionary.
Definition: omdatabase.cc:1004
PostingIterator postlist_begin(const std::string &tname) const
An iterator pointing to the start of the postlist for a given term.
Definition: omdatabase.cc:162
Xapian::termcount get_collection_freq(const std::string &tname) const
Return the total number of occurrences of the given term.
Definition: omdatabase.cc:339
Xapian::termcount get_wdf_upper_bound(const std::string &term) const
Get an upper bound on the wdf of term term.
Definition: omdatabase.cc:435