LCOV - code coverage report
Current view: top level - backends - dbfactory.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 954b5873a738 Lines: 94 132 71.2 %
Date: 2019-06-30 05:20:33 Functions: 15 18 83.3 %
Branches: 89 291 30.6 %

           Branch data     Line data    Source code
       1                 :            : /** @file dbfactory.cc
       2                 :            :  * @brief Database factories for non-remote databases.
       3                 :            :  */
       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                 :            : #include <cstdlib> // For atoi().
      42                 :            : 
      43                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
      44                 :            : # include "glass/glass_database.h"
      45                 :            : #endif
      46                 :            : #include "glass/glass_defs.h"
      47                 :            : #ifdef XAPIAN_HAS_HONEY_BACKEND
      48                 :            : # include "honey/honey_database.h"
      49                 :            : #endif
      50                 :            : #include "honey/honey_defs.h"
      51                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
      52                 :            : # include "inmemory/inmemory_database.h"
      53                 :            : #endif
      54                 :            : // Even if none of the above get included, we still need a definition of
      55                 :            : // Database::Internal.
      56                 :            : #include "backends/databaseinternal.h"
      57                 :            : 
      58                 :            : #include <fstream>
      59                 :            : #include <string>
      60                 :            : 
      61                 :            : using namespace std;
      62                 :            : 
      63                 :            : namespace Xapian {
      64                 :            : 
      65                 :            : static void
      66                 :        426 : open_stub(Database& db, const string& file)
      67                 :            : {
      68                 :            :     read_stub_file(file,
      69                 :        101 :                    [&db](const string& path) {
      70         [ +  - ]:        101 :                        db.add_database(Database(path));
      71                 :        101 :                    },
      72                 :        624 :                    [&db](const string& path) {
      73                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
      74 [ +  - ][ +  - ]:        624 :                        db.add_database(Database(new GlassDatabase(path)));
      75                 :            : #else
      76                 :            :                        (void)path;
      77                 :            : #endif
      78                 :        624 :                    },
      79                 :          0 :                    [&db](const string& path) {
      80                 :            : #ifdef XAPIAN_HAS_HONEY_BACKEND
      81 [ #  # ][ #  # ]:          0 :                        db.add_database(Database(new HoneyDatabase(path)));
      82                 :            : #else
      83                 :            :                        (void)path;
      84                 :            : #endif
      85                 :          0 :                    },
      86                 :          8 :                    [&db](const string& prog, const string& args) {
      87                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
      88         [ +  - ]:          8 :                        db.add_database(Remote::open(prog, args));
      89                 :            : #else
      90                 :            :                        (void)prog;
      91                 :            :                        (void)args;
      92                 :            : #endif
      93                 :          8 :                    },
      94                 :          4 :                    [&db](const string& host, unsigned port) {
      95                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
      96         [ #  # ]:          4 :                        db.add_database(Remote::open(host, port));
      97                 :            : #else
      98                 :            :                        (void)host;
      99                 :            :                        (void)port;
     100                 :            : #endif
     101                 :          0 :                    },
     102                 :          2 :                    [&db]() {
     103                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     104 [ +  - ][ +  - ]:          2 :                        db.add_database(Database(string(), DB_BACKEND_INMEMORY));
     105                 :            : #endif
     106                 :        428 :                    });
     107                 :            : 
     108                 :            :     // Allowing a stub database with no databases listed allows things like
     109                 :            :     // a "search all databases" feature to be implemented by generating a
     110                 :            :     // stub database file without having to special case there not being any
     111                 :            :     // databases yet.
     112                 :            :     //
     113                 :            :     // 1.0.x threw DatabaseOpeningError here, but with a "Bad line" message
     114                 :            :     // with the line number just past the end of the file, which was a bit odd.
     115                 :        391 : }
     116                 :            : 
     117                 :            : static void
     118                 :        140 : open_stub(WritableDatabase& db, const string& file, int flags)
     119                 :            : {
     120                 :            :     read_stub_file(file,
     121                 :          2 :                    [&db, flags](const string& path) {
     122         [ +  - ]:          2 :                        db.add_database(WritableDatabase(path, flags));
     123                 :          1 :                    },
     124                 :        237 :                    [&db, &flags](const string& path) {
     125                 :        237 :                        flags |= DB_BACKEND_GLASS;
     126         [ +  - ]:        237 :                        db.add_database(WritableDatabase(path, flags));
     127                 :        236 :                    },
     128                 :          0 :                    [](const string&) {
     129                 :          0 :                        auto msg = "Honey databases don't support writing";
     130 [ #  # ][ #  # ]:          0 :                        throw Xapian::DatabaseOpeningError(msg);
                 [ #  # ]
     131                 :            :                    },
     132                 :          0 :                    [&db, flags](const string& prog, const string& args) {
     133                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
     134                 :            :                        db.add_database(Remote::open_writable(prog, args,
     135         [ #  # ]:          0 :                                                              0, flags));
     136                 :            : #else
     137                 :            :                        (void)prog;
     138                 :            :                        (void)args;
     139                 :            : #endif
     140                 :          0 :                    },
     141                 :          4 :                    [&db, flags](const string& host, unsigned port) {
     142                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
     143                 :            :                        db.add_database(Remote::open_writable(host, port,
     144         [ #  # ]:          4 :                                                              0, 10000, flags));
     145                 :            : #else
     146                 :            :                        (void)host;
     147                 :            :                        (void)port;
     148                 :            : #endif
     149                 :          0 :                    },
     150                 :          2 :                    [&db]() {
     151                 :            :                        db.add_database(WritableDatabase(string(),
     152 [ +  - ][ +  - ]:          2 :                                                         DB_BACKEND_INMEMORY));
     153         [ +  + ]:        142 :                    });
     154                 :            : 
     155         [ -  + ]:        121 :     if (db.internal->size() == 0) {
     156 [ #  # ][ #  # ]:          0 :         throw DatabaseOpeningError(file + ": No databases listed");
                 [ #  # ]
     157                 :            :     }
     158                 :        121 : }
     159                 :            : 
     160                 :       2738 : Database::Database(const string& path, int flags)
     161                 :       2777 :     : Database()
     162                 :            : {
     163                 :            :     LOGCALL_CTOR(API, "Database", path|flags);
     164                 :            : 
     165                 :       2738 :     int type = flags & DB_BACKEND_MASK_;
     166   [ -  +  -  +  :       2738 :     switch (type) {
                   +  + ]
     167                 :            :         case DB_BACKEND_CHERT:
     168 [ #  # ][ #  # ]:          0 :             throw FeatureUnavailableError("Chert backend no longer supported");
                 [ #  # ]
     169                 :            :         case DB_BACKEND_GLASS:
     170                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     171 [ +  - ][ +  + ]:          9 :             internal = new GlassDatabase(path);
                 [ +  - ]
     172                 :          6 :             return;
     173                 :            : #else
     174                 :            :             throw FeatureUnavailableError("Glass backend disabled");
     175                 :            : #endif
     176                 :            :         case DB_BACKEND_HONEY:
     177                 :            : #ifdef XAPIAN_HAS_HONEY_BACKEND
     178 [ #  # ][ #  # ]:          0 :             internal = new HoneyDatabase(path);
                 [ #  # ]
     179                 :          0 :             return;
     180                 :            : #else
     181                 :            :             throw FeatureUnavailableError("Honey backend disabled");
     182                 :            : #endif
     183                 :            :         case DB_BACKEND_STUB:
     184         [ +  + ]:         35 :             open_stub(*this, path);
     185                 :          9 :             return;
     186                 :            :         case DB_BACKEND_INMEMORY:
     187                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     188 [ +  - ][ +  - ]:          8 :             internal = new InMemoryDatabase();
                 [ +  - ]
     189                 :          8 :             return;
     190                 :            : #else
     191                 :            :             throw FeatureUnavailableError("Inmemory backend disabled");
     192                 :            : #endif
     193                 :            :     }
     194                 :            : 
     195                 :            :     struct stat statbuf;
     196         [ +  + ]:       2686 :     if (stat(path.c_str(), &statbuf) == -1) {
     197 [ +  - ][ +  - ]:          1 :         throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
                 [ +  - ]
     198                 :            :     }
     199                 :            : 
     200         [ +  + ]:       2685 :     if (S_ISREG(statbuf.st_mode)) {
     201                 :            :         // Could be a stub database file, or a single file glass database.
     202                 :            : 
     203                 :            :         // Initialise to avoid bogus warning from GCC 4.9.2 with -Os.
     204                 :        652 :         int fd = -1;
     205         [ +  - ]:        652 :         switch (test_if_single_file_db(statbuf, path, &fd)) {
              [ +  +  + ]
     206                 :            :             case BACKEND_GLASS:
     207                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     208                 :            :                 // Single file glass format.
     209 [ +  - ][ +  - ]:        305 :                 internal = new GlassDatabase(fd);
                 [ +  - ]
     210                 :        305 :                 return;
     211                 :            : #else
     212                 :            :                 throw FeatureUnavailableError("Glass backend disabled");
     213                 :            : #endif
     214                 :            :             case BACKEND_HONEY:
     215                 :            : #ifdef XAPIAN_HAS_HONEY_BACKEND
     216                 :            :                 // Single file honey format.
     217 [ +  - ][ +  - ]:         10 :                 internal = new HoneyDatabase(fd);
                 [ +  - ]
     218                 :          1 :                 return;
     219                 :            : #else
     220                 :            :                 throw FeatureUnavailableError("Honey backend disabled");
     221                 :            : #endif
     222                 :            :         }
     223                 :            : 
     224         [ +  + ]:        346 :         open_stub(*this, path);
     225                 :        643 :         return;
     226                 :            :     }
     227                 :            : 
     228         [ -  + ]:       2033 :     if (rare(!S_ISDIR(statbuf.st_mode))) {
     229 [ #  # ][ #  # ]:          0 :         throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
         [ #  # ][ #  # ]
     230                 :            :     }
     231                 :            : 
     232                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     233 [ +  - ][ +  + ]:       2033 :     if (file_exists(path + "/iamglass")) {
     234 [ +  - ][ +  - ]:       1634 :         internal = new GlassDatabase(path);
                 [ +  - ]
     235                 :       1634 :         return;
     236                 :            :     }
     237                 :            : #endif
     238                 :            : 
     239                 :            : #ifdef XAPIAN_HAS_HONEY_BACKEND
     240 [ +  - ][ +  + ]:        399 :     if (file_exists(path + "/iamhoney")) {
     241 [ +  - ][ +  - ]:        354 :         internal = new HoneyDatabase(path);
                 [ +  - ]
     242                 :        354 :         return;
     243                 :            :     }
     244                 :            : #endif
     245                 :            : 
     246                 :            :     // Check for "stub directories".
     247         [ +  - ]:         45 :     string stub_file = path;
     248         [ +  - ]:         45 :     stub_file += "/XAPIANDB";
     249         [ +  - ]:         45 :     if (usual(file_exists(stub_file))) {
     250         [ +  - ]:         45 :         open_stub(*this, stub_file);
     251                 :         45 :         return;
     252                 :            :     }
     253                 :            : 
     254                 :            : #ifndef XAPIAN_HAS_GLASS_BACKEND
     255                 :            :     if (file_exists(path + "/iamglass")) {
     256                 :            :         throw FeatureUnavailableError("Glass backend disabled");
     257                 :            :     }
     258                 :            : #endif
     259                 :            : #ifndef XAPIAN_HAS_HONEY_BACKEND
     260                 :            :     if (file_exists(path + "/iamhoney")) {
     261                 :            :         throw FeatureUnavailableError("Honey backend disabled");
     262                 :            :     }
     263                 :            : #endif
     264 [ #  # ][ #  # ]:          0 :     if (file_exists(path + "/iamchert")) {
     265 [ #  # ][ #  # ]:          0 :         throw FeatureUnavailableError("Chert backend no longer supported");
                 [ #  # ]
     266                 :            :     }
     267 [ #  # ][ #  # ]:          0 :     if (file_exists(path + "/iamflint")) {
     268 [ #  # ][ #  # ]:          0 :         throw FeatureUnavailableError("Flint backend no longer supported");
                 [ #  # ]
     269                 :            :     }
     270                 :            : 
     271 [ #  # ][ #  # ]:       2738 :     throw DatabaseOpeningError("Couldn't detect type of database");
                 [ #  # ]
     272                 :            : }
     273                 :            : 
     274                 :            : /** Helper factory function.
     275                 :            :  *
     276                 :            :  *  This allows us to initialise Database::internal via the constructor's
     277                 :            :  *  initialiser list, which we want to be able to do as Database::internal
     278                 :            :  *  is an intrusive_ptr_nonnull, so we can't set it to NULL in the initialiser
     279                 :            :  *  list and then fill it in later in the constructor body.
     280                 :            :  */
     281                 :            : static Database::Internal*
     282                 :          4 : database_factory(int fd, int flags)
     283                 :            : {
     284         [ -  + ]:          4 :     if (rare(fd < 0))
     285 [ #  # ][ #  # ]:          0 :         throw InvalidArgumentError("fd < 0");
                 [ #  # ]
     286                 :            : 
     287                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     288                 :          4 :     int type = flags & DB_BACKEND_MASK_;
     289         [ +  - ]:          4 :     switch (type) {
     290                 :            :         case 0:
     291                 :            :         case DB_BACKEND_GLASS:
     292         [ +  - ]:          4 :             return new GlassDatabase(fd);
     293                 :            :     }
     294                 :            : #else
     295                 :            :     (void)flags;
     296                 :            : #endif
     297                 :            : 
     298                 :          0 :     (void)::close(fd);
     299 [ #  # ][ #  # ]:          0 :     throw DatabaseOpeningError("Couldn't detect type of database");
                 [ #  # ]
     300                 :            : }
     301                 :            : 
     302                 :          4 : Database::Database(int fd, int flags)
     303                 :          4 :     : internal(database_factory(fd, flags))
     304                 :            : {
     305                 :            :     LOGCALL_CTOR(API, "Database", fd|flags);
     306                 :          4 : }
     307                 :            : 
     308                 :            : #if defined XAPIAN_HAS_GLASS_BACKEND
     309                 :            : #define HAVE_DISK_BACKEND
     310                 :            : #endif
     311                 :            : 
     312                 :       2060 : WritableDatabase::WritableDatabase(const std::string &path, int flags, int block_size)
     313                 :       2095 :     : Database()
     314                 :            : {
     315                 :            :     LOGCALL_CTOR(API, "WritableDatabase", path|flags|block_size);
     316                 :            :     // Avoid warning if all disk-based backends are disabled.
     317                 :            :     (void)block_size;
     318                 :       2060 :     int type = flags & DB_BACKEND_MASK_;
     319                 :            :     // Clear the backend bits, so we just pass on other flags to open_stub, etc.
     320                 :       2060 :     flags &= ~DB_BACKEND_MASK_;
     321         [ +  + ]:       2060 :     if (type == 0) {
     322                 :            :         struct stat statbuf;
     323         [ +  + ]:        445 :         if (stat(path.c_str(), &statbuf) == -1) {
     324                 :            :             // ENOENT probably just means that we need to create the directory.
     325         [ -  + ]:          6 :             if (errno != ENOENT)
     326 [ #  # ][ #  # ]:          0 :                 throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
                 [ #  # ]
     327                 :            :         } else {
     328                 :            :             // File or directory already exists.
     329                 :            : 
     330         [ +  + ]:        439 :             if (S_ISREG(statbuf.st_mode)) {
     331                 :            :                 // The path is a file, so assume it is a stub database file.
     332         [ +  + ]:        119 :                 open_stub(*this, path, flags);
     333                 :        118 :                 return;
     334                 :            :             }
     335                 :            : 
     336         [ -  + ]:        320 :             if (rare(!S_ISDIR(statbuf.st_mode))) {
     337 [ #  # ][ #  # ]:          0 :                 throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
         [ #  # ][ #  # ]
     338                 :            :             }
     339                 :            : 
     340 [ +  - ][ +  - ]:        320 :             if (file_exists(path + "/iamglass")) {
     341                 :            :                 // Existing glass DB.
     342                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     343                 :        320 :                 type = DB_BACKEND_GLASS;
     344                 :            : #else
     345                 :            :                 throw FeatureUnavailableError("Glass backend disabled");
     346                 :            : #endif
     347 [ #  # ][ #  # ]:          0 :             } else if (file_exists(path + "/iamhoney")) {
     348                 :            :                 // Existing honey DB.
     349                 :            :                 throw InvalidOperationError("Honey backend doesn't support "
     350 [ #  # ][ #  # ]:          0 :                                             "updating existing databases");
                 [ #  # ]
     351 [ #  # ][ #  # ]:          0 :             } else if (file_exists(path + "/iamchert")) {
     352                 :            :                 // Existing chert DB.
     353 [ #  # ][ #  # ]:          0 :                 throw FeatureUnavailableError("Chert backend no longer supported");
                 [ #  # ]
     354 [ #  # ][ #  # ]:          0 :             } else if (file_exists(path + "/iamflint")) {
     355                 :            :                 // Existing flint DB.
     356 [ #  # ][ #  # ]:          0 :                 throw FeatureUnavailableError("Flint backend no longer supported");
                 [ #  # ]
     357                 :            :             } else {
     358                 :            :                 // Check for "stub directories".
     359         [ #  # ]:          0 :                 string stub_file = path;
     360         [ #  # ]:          0 :                 stub_file += "/XAPIANDB";
     361         [ #  # ]:          0 :                 if (usual(file_exists(stub_file))) {
     362         [ #  # ]:          0 :                     open_stub(*this, stub_file, flags);
     363         [ #  # ]:          0 :                     return;
     364                 :        327 :                 }
     365                 :            :             }
     366                 :            :         }
     367                 :            :     }
     368                 :            : 
     369   [ +  +  -  -  :       1941 :     switch (type) {
                   +  - ]
     370                 :            :         case DB_BACKEND_STUB:
     371         [ +  + ]:         21 :             open_stub(*this, path, flags);
     372                 :          3 :             return;
     373                 :            :         case 0:
     374                 :            :             // Fall through to first enabled case, so order the remaining cases
     375                 :            :             // by preference.
     376                 :            : #ifdef XAPIAN_HAS_GLASS_BACKEND
     377                 :            :         case DB_BACKEND_GLASS:
     378 [ +  - ][ +  + ]:       1552 :             internal = new GlassWritableDatabase(path, flags, block_size);
                 [ +  - ]
     379                 :       1536 :             return;
     380                 :            : #endif
     381                 :            :         case DB_BACKEND_HONEY:
     382                 :            :             throw InvalidArgumentError("Honey backend doesn't support "
     383 [ #  # ][ #  # ]:          0 :                                        "updating existing databases");
                 [ #  # ]
     384                 :            :         case DB_BACKEND_CHERT:
     385 [ #  # ][ #  # ]:          0 :             throw FeatureUnavailableError("Chert backend no longer supported");
                 [ #  # ]
     386                 :            :         case DB_BACKEND_INMEMORY:
     387                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     388 [ +  - ][ +  - ]:        368 :             internal = new InMemoryDatabase();
                 [ +  - ]
     389                 :       2025 :             return;
     390                 :            : #else
     391                 :            :             throw FeatureUnavailableError("Inmemory backend disabled");
     392                 :            : #endif
     393                 :            :     }
     394                 :            : #ifndef HAVE_DISK_BACKEND
     395                 :            :     throw FeatureUnavailableError("No disk-based writable backend is enabled");
     396                 :            : #endif
     397                 :            : }
     398                 :            : 
     399                 :            : }

Generated by: LCOV version 1.11