1 /* database.cc: Database::Internal base class.
2  *
3  * Copyright 1999,2000,2001 BrightStation PLC
4  * Copyright 2002 Ananova Ltd
5  * Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2011,2014,2016 Olly Betts
6  * Copyright 2008 Lemur Consulting Ltd
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 
24 #include <config.h>
25 
26 #include "database.h"
27 
28 #include "xapian/error.h"
29 
30 #include "api/leafpostlist.h"
31 #include "omassert.h"
32 #include "slowvaluelist.h"
33 
34 #include <algorithm>
35 #include <string>
36 
37 using namespace std;
38 using Xapian::Internal::intrusive_ptr;
39 
40 namespace Xapian {
41 
~Internal()42 Database::Internal::~Internal()
43 {
44 }
45 
46 void
keep_alive()47 Database::Internal::keep_alive()
48 {
49     // For the normal case of local databases, nothing needs to be done.
50 }
51 
52 
53 void
readahead_for_query(const Xapian::Query &)54 Database::Internal::readahead_for_query(const Xapian::Query &)
55 {
56 }
57 
58 Xapian::termcount
get_doclength_lower_bound() const59 Database::Internal::get_doclength_lower_bound() const
60 {
61     // A zero-length document can't contain any terms, so we ignore such
62     // documents for the purposes of this lower bound.
63     return 1;
64 }
65 
66 Xapian::termcount
get_doclength_upper_bound() const67 Database::Internal::get_doclength_upper_bound() const
68 {
69     // Not a very tight bound in general, but this is only a fall-back for
70     // backends which don't store these stats.
71     return min(get_total_length(), Xapian::totallength(Xapian::termcount(-1)));
72 }
73 
74 Xapian::termcount
get_wdf_upper_bound(const string & term) const75 Database::Internal::get_wdf_upper_bound(const string & term) const
76 {
77     // Not a very tight bound in general, but this is only a fall-back for
78     // backends which don't store these stats.
79     Xapian::termcount cf;
80     get_freqs(term, NULL, &cf);
81     return cf;
82 }
83 
84 // Discard any exceptions - we're called from the destructors of derived
85 // classes so we can't safely throw.
86 void
dtor_called()87 Database::Internal::dtor_called()
88 {
89     try {
90 	if (transaction_active()) {
91 	    cancel_transaction();
92 	} else if (transaction_state == TRANSACTION_NONE) {
93 	    commit();
94 	}
95     } catch (...) {
96 	// We can't safely throw exceptions from a destructor in case an
97 	// exception is already active and causing us to be destroyed.
98     }
99 }
100 
101 void
commit()102 Database::Internal::commit()
103 {
104     // Writable databases should override this method.
105     Assert(false);
106 }
107 
108 void
cancel()109 Database::Internal::cancel()
110 {
111     // Writable databases should override this method.
112     Assert(false);
113 }
114 
115 void
begin_transaction(bool flushed)116 Database::Internal::begin_transaction(bool flushed)
117 {
118     if (transaction_state != TRANSACTION_NONE) {
119 	if (transaction_state == TRANSACTION_UNIMPLEMENTED)
120 	    throw Xapian::UnimplementedError("This backend doesn't implement transactions");
121 	throw InvalidOperationError("Cannot begin transaction - transaction already in progress");
122     }
123     if (flushed) {
124 	// N.B. Call commit() before we set transaction_state since commit()
125 	// isn't allowing during a transaction.
126 	commit();
127 	transaction_state = TRANSACTION_FLUSHED;
128     } else {
129 	transaction_state = TRANSACTION_UNFLUSHED;
130     }
131 }
132 
133 void
commit_transaction()134 Database::Internal::commit_transaction()
135 {
136     if (!transaction_active()) {
137 	if (transaction_state == TRANSACTION_UNIMPLEMENTED)
138 	    throw Xapian::UnimplementedError("This backend doesn't implement transactions");
139 	throw InvalidOperationError("Cannot commit transaction - no transaction currently in progress");
140     }
141     bool flushed = (transaction_state == TRANSACTION_FLUSHED);
142     transaction_state = TRANSACTION_NONE;
143     // N.B. Call commit() after we clear transaction_state since commit()
144     // isn't allowing during a transaction.
145     if (flushed) commit();
146 }
147 
148 void
cancel_transaction()149 Database::Internal::cancel_transaction()
150 {
151     if (!transaction_active()) {
152 	if (transaction_state == TRANSACTION_UNIMPLEMENTED)
153 	    throw Xapian::UnimplementedError("This backend doesn't implement transactions");
154 	throw InvalidOperationError("Cannot cancel transaction - no transaction currently in progress");
155     }
156     transaction_state = TRANSACTION_NONE;
157     cancel();
158 }
159 
160 Xapian::docid
add_document(const Xapian::Document &)161 Database::Internal::add_document(const Xapian::Document &)
162 {
163     // Writable databases should override this method.
164     Assert(false);
165     return 0;
166 }
167 
168 void
delete_document(Xapian::docid)169 Database::Internal::delete_document(Xapian::docid)
170 {
171     // Writable databases should override this method.
172     Assert(false);
173 }
174 
175 void
delete_document(const string & unique_term)176 Database::Internal::delete_document(const string & unique_term)
177 {
178     // Default implementation - overridden for remote databases
179     intrusive_ptr<LeafPostList> pl(open_post_list(unique_term));
180     while (pl->next(), !pl->at_end()) {
181 	delete_document(pl->get_docid());
182     }
183 }
184 
185 void
replace_document(Xapian::docid,const Xapian::Document &)186 Database::Internal::replace_document(Xapian::docid, const Xapian::Document &)
187 {
188     // Writable databases should override this method.
189     Assert(false);
190 }
191 
192 Xapian::docid
replace_document(const string & unique_term,const Xapian::Document & document)193 Database::Internal::replace_document(const string & unique_term,
194 				     const Xapian::Document & document)
195 {
196     // Default implementation - overridden for remote databases
197     intrusive_ptr<LeafPostList> pl(open_post_list(unique_term));
198     pl->next();
199     if (pl->at_end()) {
200 	return add_document(document);
201     }
202     Xapian::docid did = pl->get_docid();
203     replace_document(did, document);
204     while (pl->next(), !pl->at_end()) {
205 	delete_document(pl->get_docid());
206     }
207     return did;
208 }
209 
210 ValueList *
open_value_list(Xapian::valueno slot) const211 Database::Internal::open_value_list(Xapian::valueno slot) const
212 {
213     return new SlowValueList(this, slot);
214 }
215 
216 TermList *
open_spelling_termlist(const string &) const217 Database::Internal::open_spelling_termlist(const string &) const
218 {
219     // Only implemented for some database backends - others will just not
220     // suggest spelling corrections (or not contribute to them in a multiple
221     // database situation).
222     return NULL;
223 }
224 
225 TermList *
open_spelling_wordlist() const226 Database::Internal::open_spelling_wordlist() const
227 {
228     // Only implemented for some database backends - others will just not
229     // suggest spelling corrections (or not contribute to them in a multiple
230     // database situation).
231     return NULL;
232 }
233 
234 Xapian::doccount
get_spelling_frequency(const string &) const235 Database::Internal::get_spelling_frequency(const string &) const
236 {
237     // Only implemented for some database backends - others will just not
238     // suggest spelling corrections (or not contribute to them in a multiple
239     // database situation).
240     return 0;
241 }
242 
243 void
add_spelling(const string &,Xapian::termcount) const244 Database::Internal::add_spelling(const string &, Xapian::termcount) const
245 {
246     throw Xapian::UnimplementedError("This backend doesn't implement spelling correction");
247 }
248 
249 void
remove_spelling(const string &,Xapian::termcount) const250 Database::Internal::remove_spelling(const string &, Xapian::termcount) const
251 {
252     throw Xapian::UnimplementedError("This backend doesn't implement spelling correction");
253 }
254 
255 TermList *
open_synonym_termlist(const string &) const256 Database::Internal::open_synonym_termlist(const string &) const
257 {
258     // Only implemented for some database backends - others will just not
259     // expand synonyms (or not contribute to them in a multiple database
260     // situation).
261     return NULL;
262 }
263 
264 TermList *
open_synonym_keylist(const string &) const265 Database::Internal::open_synonym_keylist(const string &) const
266 {
267     // Only implemented for some database backends - others will just not
268     // expand synonyms (or not contribute to them in a multiple database
269     // situation).
270     return NULL;
271 }
272 
273 void
add_synonym(const string &,const string &) const274 Database::Internal::add_synonym(const string &, const string &) const
275 {
276     throw Xapian::UnimplementedError("This backend doesn't implement synonyms");
277 }
278 
279 void
remove_synonym(const string &,const string &) const280 Database::Internal::remove_synonym(const string &, const string &) const
281 {
282     throw Xapian::UnimplementedError("This backend doesn't implement synonyms");
283 }
284 
285 void
clear_synonyms(const string &) const286 Database::Internal::clear_synonyms(const string &) const
287 {
288     throw Xapian::UnimplementedError("This backend doesn't implement synonyms");
289 }
290 
291 string
get_metadata(const string &) const292 Database::Internal::get_metadata(const string &) const
293 {
294     return string();
295 }
296 
297 TermList *
open_metadata_keylist(const string &) const298 Database::Internal::open_metadata_keylist(const string &) const
299 {
300     // Only implemented for some database backends - others will simply report
301     // there being no metadata keys.
302     return NULL;
303 }
304 
305 void
set_metadata(const string &,const string &)306 Database::Internal::set_metadata(const string &, const string &)
307 {
308     throw Xapian::UnimplementedError("This backend doesn't implement metadata");
309 }
310 
311 bool
reopen()312 Database::Internal::reopen()
313 {
314     // Database backends which don't support simultaneous update and reading
315     // probably don't need to do anything here.  And since we didn't do
316     // anything we should return false to indicate that nothing has changed.
317     return false;
318 }
319 
320 void
request_document(Xapian::docid) const321 Database::Internal::request_document(Xapian::docid /*did*/) const
322 {
323 }
324 
325 Xapian::Document::Internal *
collect_document(Xapian::docid did) const326 Database::Internal::collect_document(Xapian::docid did) const
327 {
328     // Open the document lazily - collect document is only called by
329     // Enquire::Internal::read_doc() for a given MSetItem, so we know that the
330     // document already exists.
331     return open_document(did, true);
332 }
333 
334 void
write_changesets_to_fd(int,const string &,bool,ReplicationInfo *)335 Database::Internal::write_changesets_to_fd(int, const string &, bool, ReplicationInfo *)
336 {
337     throw Xapian::UnimplementedError("This backend doesn't provide changesets");
338 }
339 
340 string
get_revision_info() const341 Database::Internal::get_revision_info() const
342 {
343     throw Xapian::UnimplementedError("This backend doesn't provide access to revision information");
344 }
345 
346 string
get_uuid() const347 Database::Internal::get_uuid() const
348 {
349     return string();
350 }
351 
352 void
invalidate_doc_object(Xapian::Document::Internal *) const353 Database::Internal::invalidate_doc_object(Xapian::Document::Internal *) const
354 {
355     // Do nothing, by default.
356 }
357 
358 void
get_used_docid_range(Xapian::docid &,Xapian::docid &) const359 Database::Internal::get_used_docid_range(Xapian::docid &,
360 					 Xapian::docid &) const
361 {
362     throw Xapian::UnimplementedError("This backend doesn't implement get_used_docid_range()");
363 }
364 
365 bool
locked() const366 Database::Internal::locked() const
367 {
368     return false;
369 }
370 
371 }
372