xapian-core  1.4.20
dbfactory.cc
Go to the documentation of this file.
1 
4 /* Copyright 2002,2003,2004,2005,2006,2007,2008,2009,2011,2012,2013,2014,2015,2016,2017,2019 Olly Betts
5  * Copyright 2008 Lemur Consulting Ltd
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License as
9  * published by the Free Software Foundation; either version 2 of the
10  * License, or (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
20  * USA
21  */
22 
23 #include <config.h>
24 
25 #include "xapian/dbfactory.h"
26 
27 #include "xapian/constants.h"
28 #include "xapian/database.h"
29 #include "xapian/error.h"
30 #include "xapian/version.h" // For XAPIAN_HAS_XXX_BACKEND.
31 
32 #include "backends.h"
33 #include "databasehelpers.h"
34 #include "debuglog.h"
35 #include "filetests.h"
36 #include "fileutils.h"
37 #include "posixy_wrapper.h"
38 #include "str.h"
39 
40 #include <cerrno>
41 
42 #ifdef XAPIAN_HAS_GLASS_BACKEND
43 # include "glass/glass_database.h"
44 #endif
45 #ifdef XAPIAN_HAS_CHERT_BACKEND
46 # include "chert/chert_database.h"
47 #endif
48 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
50 #endif
51 // Even if none of the above get included, we still need a definition of
52 // Database::Internal.
53 #include "backends/database.h"
54 
55 #include <string>
56 
57 using namespace std;
58 
59 namespace Xapian {
60 
61 static void
62 open_stub(Database& db, const string& file)
63 {
64  read_stub_file(file,
65  [&db](const string& path) {
66  db.add_database(Database(path));
67  },
68  [&db](const string& path) {
69 #ifdef XAPIAN_HAS_CHERT_BACKEND
70  db.add_database(Database(new ChertDatabase(path)));
71 #else
72  (void)path;
73 #endif
74  },
75  [&db](const string& path) {
76 #ifdef XAPIAN_HAS_GLASS_BACKEND
77  db.add_database(Database(new GlassDatabase(path)));
78 #else
79  (void)path;
80 #endif
81  },
82  [&db](const string& prog, const string& args) {
83 #ifdef XAPIAN_HAS_REMOTE_BACKEND
84  db.add_database(Remote::open(prog, args));
85 #else
86  (void)prog;
87  (void)args;
88 #endif
89  },
90  [&db](const string& host, unsigned port) {
91 #ifdef XAPIAN_HAS_REMOTE_BACKEND
92  db.add_database(Remote::open(host, port));
93 #else
94  (void)host;
95  (void)port;
96 #endif
97  },
98  [&db]() {
99 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
101 #endif
102  });
103 
104  // Allowing a stub database with no databases listed allows things like
105  // a "search all databases" feature to be implemented by generating a
106  // stub database file without having to special case there not being any
107  // databases yet.
108  //
109  // 1.0.x threw DatabaseOpeningError here, but with a "Bad line" message
110  // with the line number just past the end of the file, which was a bit odd.
111 }
112 
113 static void
114 open_stub(WritableDatabase& db, const string& file, int flags)
115 {
116  read_stub_file(file,
117  [&db, flags](const string& path) {
118  db.add_database(WritableDatabase(path, flags));
119  },
120  [&db, &flags](const string& path) {
121  flags |= DB_BACKEND_CHERT;
122  db.add_database(WritableDatabase(path, flags));
123  },
124  [&db, &flags](const string& path) {
125  flags |= DB_BACKEND_GLASS;
126  db.add_database(WritableDatabase(path, flags));
127  },
128  [&db, flags](const string& prog, const string& args) {
129 #ifdef XAPIAN_HAS_REMOTE_BACKEND
130  db.add_database(Remote::open_writable(prog, args,
131  0, flags));
132 #else
133  (void)prog;
134  (void)args;
135 #endif
136  },
137  [&db, flags](const string& host, unsigned port) {
138 #ifdef XAPIAN_HAS_REMOTE_BACKEND
139  db.add_database(Remote::open_writable(host, port,
140  0, 10000, flags));
141 #else
142  (void)host;
143  (void)port;
144 #endif
145  },
146  [&db]() {
147  db.add_database(WritableDatabase(string(),
149  });
150 
151  if (db.internal.empty()) {
152  throw DatabaseOpeningError(file + ": No databases listed");
153  }
154 }
155 
156 Database::Database(const string &path, int flags)
157 {
158  LOGCALL_CTOR(API, "Database", path|flags);
159 
160  int type = flags & DB_BACKEND_MASK_;
161  switch (type) {
162  case DB_BACKEND_CHERT:
163 #ifdef XAPIAN_HAS_CHERT_BACKEND
164  internal.push_back(new ChertDatabase(path));
165  return;
166 #else
167  throw FeatureUnavailableError("Chert backend disabled");
168 #endif
169  case DB_BACKEND_GLASS:
170 #ifdef XAPIAN_HAS_GLASS_BACKEND
171  internal.push_back(new GlassDatabase(path));
172  return;
173 #else
174  throw FeatureUnavailableError("Glass backend disabled");
175 #endif
176  case DB_BACKEND_STUB:
177  open_stub(*this, path);
178  return;
179  case DB_BACKEND_INMEMORY:
180 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
181  internal.push_back(new InMemoryDatabase());
182  return;
183 #else
184  throw FeatureUnavailableError("Inmemory backend disabled");
185 #endif
186  }
187 
188  struct stat statbuf;
189  if (stat(path.c_str(), &statbuf) == -1) {
190  if (errno == ENOENT) {
191  throw DatabaseNotFoundError("Couldn't stat '" + path + "'", errno);
192  } else {
193  throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
194  }
195  }
196 
197  if (S_ISREG(statbuf.st_mode)) {
198  // Could be a stub database file, or a single file glass database.
199 
200  // Initialise to avoid bogus warning from GCC 4.9.2 with -Os.
201  int fd = -1;
202  switch (test_if_single_file_db(statbuf, path, &fd)) {
203  case BACKEND_GLASS:
204 #ifdef XAPIAN_HAS_GLASS_BACKEND
205  // Single file glass format.
206  internal.push_back(new GlassDatabase(fd));
207  return;
208 #else
209  throw FeatureUnavailableError("Glass backend disabled");
210 #endif
211  }
212 
213  open_stub(*this, path);
214  return;
215  }
216 
217  if (rare(!S_ISDIR(statbuf.st_mode))) {
218  throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
219  }
220 
221 #ifdef XAPIAN_HAS_CHERT_BACKEND
222  if (file_exists(path + "/iamchert")) {
223  internal.push_back(new ChertDatabase(path));
224  return;
225  }
226 #endif
227 
228 #ifdef XAPIAN_HAS_GLASS_BACKEND
229  if (file_exists(path + "/iamglass")) {
230  internal.push_back(new GlassDatabase(path));
231  return;
232  }
233 #endif
234 
235  // Check for "stub directories".
236  string stub_file = path;
237  stub_file += "/XAPIANDB";
238  if (usual(file_exists(stub_file))) {
239  open_stub(*this, stub_file);
240  return;
241  }
242 
243 #ifndef XAPIAN_HAS_CHERT_BACKEND
244  if (file_exists(path + "/iamchert")) {
245  throw FeatureUnavailableError("Chert backend disabled");
246  }
247 #endif
248 #ifndef XAPIAN_HAS_GLASS_BACKEND
249  if (file_exists(path + "/iamglass")) {
250  throw FeatureUnavailableError("Glass backend disabled");
251  }
252 #endif
253  if (file_exists(path + "/iamflint")) {
254  throw FeatureUnavailableError("Flint backend no longer supported");
255  }
256 
257  throw DatabaseNotFoundError("Couldn't detect type of database");
258 }
259 
260 Database::Database(int fd, int flags)
261 {
262  LOGCALL_CTOR(API, "Database", fd|flags);
263  if (rare(fd < 0))
264  throw InvalidArgumentError("fd < 0");
265 
266 #ifdef XAPIAN_HAS_GLASS_BACKEND
267  int type = flags & DB_BACKEND_MASK_;
268  switch (type) {
269  case 0: case DB_BACKEND_GLASS:
270  internal.push_back(new GlassDatabase(fd));
271  return;
272  }
273 #else
274  (void)flags;
275 #endif
276 
277  (void)::close(fd);
278  throw DatabaseOpeningError("Couldn't detect type of database");
279 }
280 
281 #if defined XAPIAN_HAS_CHERT_BACKEND || \
282  defined XAPIAN_HAS_GLASS_BACKEND
283 #define HAVE_DISK_BACKEND
284 #endif
285 
286 WritableDatabase::WritableDatabase(const std::string &path, int flags, int block_size)
287  : Database()
288 {
289  LOGCALL_CTOR(API, "WritableDatabase", path|flags|block_size);
290  // Avoid warning if both chert and glass are disabled.
291  (void)block_size;
292  int type = flags & DB_BACKEND_MASK_;
293  // Clear the backend bits, so we just pass on other flags to open_stub, etc.
294  flags &= ~DB_BACKEND_MASK_;
295  if (type == 0) {
296  struct stat statbuf;
297  if (stat(path.c_str(), &statbuf) == -1) {
298  // ENOENT probably just means that we need to create the directory.
299  if (errno != ENOENT)
300  throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
301  } else {
302  // File or directory already exists.
303 
304  if (S_ISREG(statbuf.st_mode)) {
305  // The path is a file, so assume it is a stub database file.
306  open_stub(*this, path, flags);
307  return;
308  }
309 
310  if (rare(!S_ISDIR(statbuf.st_mode))) {
311  throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
312  }
313 
314  if (file_exists(path + "/iamchert")) {
315  // Existing chert DB.
316 #ifdef XAPIAN_HAS_CHERT_BACKEND
317  type = DB_BACKEND_CHERT;
318 #else
319  throw FeatureUnavailableError("Chert backend disabled");
320 #endif
321  } else if (file_exists(path + "/iamglass")) {
322  // Existing glass DB.
323 #ifdef XAPIAN_HAS_GLASS_BACKEND
324  type = DB_BACKEND_GLASS;
325 #else
326  throw FeatureUnavailableError("Glass backend disabled");
327 #endif
328  } else if (file_exists(path + "/iamflint")) {
329  // Existing flint DB.
330  throw FeatureUnavailableError("Flint backend no longer supported");
331  } else {
332  // Check for "stub directories".
333  string stub_file = path;
334  stub_file += "/XAPIANDB";
335  if (usual(file_exists(stub_file))) {
336  open_stub(*this, stub_file, flags);
337  return;
338  }
339  }
340  }
341  }
342 
343  switch (type) {
344  case DB_BACKEND_STUB:
345  open_stub(*this, path, flags);
346  return;
347  case 0:
348  // Fall through to first enabled case, so order the remaining cases
349  // by preference.
350 #ifdef XAPIAN_HAS_GLASS_BACKEND
351  case DB_BACKEND_GLASS:
352  internal.push_back(new GlassWritableDatabase(path, flags, block_size));
353  return;
354 #endif
355 #ifdef XAPIAN_HAS_CHERT_BACKEND
356  case DB_BACKEND_CHERT:
357  internal.push_back(new ChertWritableDatabase(path, flags, block_size));
358  return;
359 #endif
360  case DB_BACKEND_INMEMORY:
361 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
362  internal.push_back(new InMemoryDatabase());
363  return;
364 #else
365  throw FeatureUnavailableError("Inmemory backend disabled");
366 #endif
367  }
368 #ifndef HAVE_DISK_BACKEND
369  throw FeatureUnavailableError("No disk-based writable backend is enabled");
370 #endif
371 }
372 
373 }
const int DB_BACKEND_INMEMORY
Use the "in memory" backend.
Definition: constants.h:195
The Xapian namespace contains public interfaces for the Xapian library.
Definition: compactor.cc:80
int close(FD &fd)
Definition: fd.h:63
A database held entirely in memory.
This class is used to access a database, or a group of databases.
Definition: database.h:68
DatabaseOpeningError indicates failure to open a database.
Definition: error.h:581
#define S_ISDIR(ST_MODE)
Definition: safesysstat.h:57
#define usual(COND)
Definition: config.h:563
A writable chert database.
Provides wrappers with POSIXy semantics.
Constants in the Xapian namespace.
Helper functions for database handling.
WritableDatabase open()
Construct a WritableDatabase object for a new, empty InMemory database.
Definition: dbfactory.h:104
STL namespace.
static void open_stub(WritableDatabase &db, const string &file, int flags)
Definition: dbfactory.cc:114
Convert types to std::string.
Utility functions for testing files.
#define rare(COND)
Definition: config.h:562
const int DB_BACKEND_GLASS
Use the glass backend.
Definition: constants.h:158
A writable glass database.
int test_if_single_file_db(const struct stat &sb, const string &path, int *fd_ptr)
Probe if a path is a single-file database.
Hierarchy of classes which Xapian can throw as exceptions.
InvalidArgumentError indicates an invalid parameter value was passed to the API.
Definition: error.h:241
BACKEND_* constants.
This class provides read/write access to a database.
Definition: database.h:785
Indicates an attempt to use a feature which is unavailable.
Definition: error.h:719
#define S_ISREG(ST_MODE)
Definition: safesysstat.h:60
API for working with Xapian databases.
C++ class definition for glass database.
C++ class definition for inmemory database access.
Indicates an attempt to access a database not present.
Definition: error.h:1055
void read_stub_file(const std::string &file, A1 action_auto, A2 action_chert, A3 action_glass, A4 action_remote_prog, A5 action_remote_tcp, A6 action_inmemory)
Open, read and process a stub database file.
#define LOGCALL_CTOR(CATEGORY, CLASS, PARAMS)
Definition: debuglog.h:478
A backend designed for efficient indexing and retrieval, using compressed posting lists and a btree s...
void add_database(const Database &database)
Add an existing database (or group of databases) to those accessed by this object.
Definition: omdatabase.cc:148
C++ class definition for chert database.
Define preprocessor symbols for the library version.
const int DB_BACKEND_STUB
Open a stub database file.
Definition: constants.h:179
static void open_stub(Database &db, const string &file)
Definition: dbfactory.cc:62
void add_database(const WritableDatabase &other)
Add shards from another WritableDatabase.
Definition: database.h:891
A backend designed for efficient indexing and retrieval, using compressed posting lists and a btree s...
WritableDatabase open_writable(const std::string &host, unsigned int port, useconds_t timeout=0, useconds_t connect_timeout=10000, int flags=0)
Construct a WritableDatabase object for update access to a remote database accessed via a TCP connect...
Factory functions for constructing Database and WritableDatabase objects.
bool file_exists(const char *path)
Test if a file exists.
Definition: filetests.h:39
File and path manipulation routines.
const int DB_BACKEND_CHERT
Use the chert backend.
Definition: constants.h:170
Debug logging macros.