xapian-core  1.4.19
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  throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
191  }
192 
193  if (S_ISREG(statbuf.st_mode)) {
194  // Could be a stub database file, or a single file glass database.
195 
196  // Initialise to avoid bogus warning from GCC 4.9.2 with -Os.
197  int fd = -1;
198  switch (test_if_single_file_db(statbuf, path, &fd)) {
199  case BACKEND_GLASS:
200 #ifdef XAPIAN_HAS_GLASS_BACKEND
201  // Single file glass format.
202  internal.push_back(new GlassDatabase(fd));
203  return;
204 #else
205  throw FeatureUnavailableError("Glass backend disabled");
206 #endif
207  }
208 
209  open_stub(*this, path);
210  return;
211  }
212 
213  if (rare(!S_ISDIR(statbuf.st_mode))) {
214  throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
215  }
216 
217 #ifdef XAPIAN_HAS_CHERT_BACKEND
218  if (file_exists(path + "/iamchert")) {
219  internal.push_back(new ChertDatabase(path));
220  return;
221  }
222 #endif
223 
224 #ifdef XAPIAN_HAS_GLASS_BACKEND
225  if (file_exists(path + "/iamglass")) {
226  internal.push_back(new GlassDatabase(path));
227  return;
228  }
229 #endif
230 
231  // Check for "stub directories".
232  string stub_file = path;
233  stub_file += "/XAPIANDB";
234  if (usual(file_exists(stub_file))) {
235  open_stub(*this, stub_file);
236  return;
237  }
238 
239 #ifndef XAPIAN_HAS_CHERT_BACKEND
240  if (file_exists(path + "/iamchert")) {
241  throw FeatureUnavailableError("Chert backend disabled");
242  }
243 #endif
244 #ifndef XAPIAN_HAS_GLASS_BACKEND
245  if (file_exists(path + "/iamglass")) {
246  throw FeatureUnavailableError("Glass backend disabled");
247  }
248 #endif
249  if (file_exists(path + "/iamflint")) {
250  throw FeatureUnavailableError("Flint backend no longer supported");
251  }
252 
253  throw DatabaseOpeningError("Couldn't detect type of database");
254 }
255 
256 Database::Database(int fd, int flags)
257 {
258  LOGCALL_CTOR(API, "Database", fd|flags);
259  if (rare(fd < 0))
260  throw InvalidArgumentError("fd < 0");
261 
262 #ifdef XAPIAN_HAS_GLASS_BACKEND
263  int type = flags & DB_BACKEND_MASK_;
264  switch (type) {
265  case 0: case DB_BACKEND_GLASS:
266  internal.push_back(new GlassDatabase(fd));
267  return;
268  }
269 #else
270  (void)flags;
271 #endif
272 
273  (void)::close(fd);
274  throw DatabaseOpeningError("Couldn't detect type of database");
275 }
276 
277 #if defined XAPIAN_HAS_CHERT_BACKEND || \
278  defined XAPIAN_HAS_GLASS_BACKEND
279 #define HAVE_DISK_BACKEND
280 #endif
281 
282 WritableDatabase::WritableDatabase(const std::string &path, int flags, int block_size)
283  : Database()
284 {
285  LOGCALL_CTOR(API, "WritableDatabase", path|flags|block_size);
286  // Avoid warning if both chert and glass are disabled.
287  (void)block_size;
288  int type = flags & DB_BACKEND_MASK_;
289  // Clear the backend bits, so we just pass on other flags to open_stub, etc.
290  flags &= ~DB_BACKEND_MASK_;
291  if (type == 0) {
292  struct stat statbuf;
293  if (stat(path.c_str(), &statbuf) == -1) {
294  // ENOENT probably just means that we need to create the directory.
295  if (errno != ENOENT)
296  throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
297  } else {
298  // File or directory already exists.
299 
300  if (S_ISREG(statbuf.st_mode)) {
301  // The path is a file, so assume it is a stub database file.
302  open_stub(*this, path, flags);
303  return;
304  }
305 
306  if (rare(!S_ISDIR(statbuf.st_mode))) {
307  throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
308  }
309 
310  if (file_exists(path + "/iamchert")) {
311  // Existing chert DB.
312 #ifdef XAPIAN_HAS_CHERT_BACKEND
313  type = DB_BACKEND_CHERT;
314 #else
315  throw FeatureUnavailableError("Chert backend disabled");
316 #endif
317  } else if (file_exists(path + "/iamglass")) {
318  // Existing glass DB.
319 #ifdef XAPIAN_HAS_GLASS_BACKEND
320  type = DB_BACKEND_GLASS;
321 #else
322  throw FeatureUnavailableError("Glass backend disabled");
323 #endif
324  } else if (file_exists(path + "/iamflint")) {
325  // Existing flint DB.
326  throw FeatureUnavailableError("Flint backend no longer supported");
327  } else {
328  // Check for "stub directories".
329  string stub_file = path;
330  stub_file += "/XAPIANDB";
331  if (usual(file_exists(stub_file))) {
332  open_stub(*this, stub_file, flags);
333  return;
334  }
335  }
336  }
337  }
338 
339  switch (type) {
340  case DB_BACKEND_STUB:
341  open_stub(*this, path, flags);
342  return;
343  case 0:
344  // Fall through to first enabled case, so order the remaining cases
345  // by preference.
346 #ifdef XAPIAN_HAS_GLASS_BACKEND
347  case DB_BACKEND_GLASS:
348  internal.push_back(new GlassWritableDatabase(path, flags, block_size));
349  return;
350 #endif
351 #ifdef XAPIAN_HAS_CHERT_BACKEND
352  case DB_BACKEND_CHERT:
353  internal.push_back(new ChertWritableDatabase(path, flags, block_size));
354  return;
355 #endif
356  case DB_BACKEND_INMEMORY:
357 #ifdef XAPIAN_HAS_INMEMORY_BACKEND
358  internal.push_back(new InMemoryDatabase());
359  return;
360 #else
361  throw FeatureUnavailableError("Inmemory backend disabled");
362 #endif
363  }
364 #ifndef HAVE_DISK_BACKEND
365  throw FeatureUnavailableError("No disk-based writable backend is enabled");
366 #endif
367 }
368 
369 }
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:544
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:543
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.
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.