LCOV - code coverage report
Current view: top level - backends - dbfactory.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core r Lines: 138 180 76.7 %
Date: 2011-08-21 Functions: 13 15 86.7 %
Branches: 74 148 50.0 %

           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 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/database.h"
      28                 :            : #include "xapian/error.h"
      29                 :            : #include "xapian/version.h" // For XAPIAN_HAS_XXX_BACKEND.
      30                 :            : 
      31                 :            : #include "debuglog.h"
      32                 :            : #include "fileutils.h"
      33                 :            : #include "str.h"
      34                 :            : #include "utils.h"
      35                 :            : 
      36                 :            : #include "safeerrno.h"
      37                 :            : 
      38                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
      39                 :            : # include "brass/brass_database.h"
      40                 :            : #endif
      41                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
      42                 :            : # include "chert/chert_database.h"
      43                 :            : #endif
      44                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
      45                 :            : # include "flint/flint_database.h"
      46                 :            : #endif
      47                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
      48                 :            : # include "inmemory/inmemory_database.h"
      49                 :            : #endif
      50                 :            : 
      51                 :            : #include <fstream>
      52                 :            : #include <string>
      53                 :            : 
      54                 :            : using namespace std;
      55                 :            : 
      56                 :            : namespace Xapian {
      57                 :            : 
      58                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
      59                 :            : Database
      60                 :        341 : Brass::open(const string &dir) {
      61                 :            :     LOGCALL_STATIC(API, Database, "Brass::open", dir);
      62                 :        341 :     RETURN(Database(new BrassDatabase(dir)));
      63                 :            : }
      64                 :            : 
      65                 :            : WritableDatabase
      66                 :        312 : Brass::open(const string &dir, int action, int block_size) {
      67                 :            :     LOGCALL_STATIC(API, WritableDatabase, "Brass::open", dir | action | block_size);
      68                 :        315 :     RETURN(WritableDatabase(new BrassWritableDatabase(dir, action, block_size)));
      69                 :            : }
      70                 :            : #endif
      71                 :            : 
      72                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
      73                 :            : Database
      74                 :        341 : Chert::open(const string &dir) {
      75                 :            :     LOGCALL_STATIC(API, Database, "Chert::open", dir);
      76                 :        341 :     return Database(new ChertDatabase(dir));
      77                 :            : }
      78                 :            : 
      79                 :            : WritableDatabase
      80                 :        314 : Chert::open(const string &dir, int action, int block_size) {
      81                 :            :     LOGCALL_STATIC(API, WritableDatabase, "Chert::open", dir | action | block_size);
      82                 :        317 :     return WritableDatabase(new ChertWritableDatabase(dir, action, block_size));
      83                 :            : }
      84                 :            : #endif
      85                 :            : 
      86                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
      87                 :            : Database
      88                 :        344 : Flint::open(const string &dir) {
      89                 :            :     LOGCALL_STATIC(API, Database, "Flint::open", dir);
      90                 :        348 :     return Database(new FlintDatabase(dir));
      91                 :            : }
      92                 :            : 
      93                 :            : WritableDatabase
      94                 :        320 : Flint::open(const string &dir, int action, int block_size) {
      95                 :            :     LOGCALL_STATIC(API, WritableDatabase, "Flint::open", dir | action | block_size);
      96                 :        332 :     return WritableDatabase(new FlintWritableDatabase(dir, action, block_size));
      97                 :            : }
      98                 :            : #endif
      99                 :            : 
     100                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     101                 :            : WritableDatabase
     102                 :        266 : InMemory::open() {
     103                 :            :     LOGCALL_STATIC(API, Database, "InMemory::open", NO_ARGS);
     104                 :        266 :     return WritableDatabase(new InMemoryDatabase);
     105                 :            : }
     106                 :            : #endif
     107                 :            : 
     108                 :            : static void
     109                 :        644 : open_stub(Database &db, const string &file)
     110                 :            : {
     111                 :            :     // A stub database is a text file with one or more lines of this format:
     112                 :            :     // <dbtype> <serialised db object>
     113                 :            :     //
     114                 :            :     // Lines which start with a "#" character are ignored.
     115                 :            :     //
     116                 :            :     // Any paths specified in stub database files which are relative will be
     117                 :            :     // considered to be relative to the directory containing the stub database.
     118                 :        644 :     ifstream stub(file.c_str());
     119                 :        644 :     string line;
     120                 :        644 :     unsigned int line_no = 0;
     121         [ +  + ]:       1959 :     while (getline(stub, line)) {
     122                 :       1315 :         ++line_no;
     123 [ +  - ][ +  + ]:       1315 :         if (line.empty() || line[0] == '#')
                 [ +  + ]
     124                 :        166 :             continue;
     125                 :       1149 :         string::size_type space = line.find(' ');
     126         [ +  + ]:       1149 :         if (space == string::npos) space = line.size();
     127                 :            : 
     128                 :       1149 :         string type(line, 0, space);
     129                 :       1149 :         line.erase(0, space + 1);
     130                 :            : 
     131         [ +  + ]:       1149 :         if (type == "auto") {
     132                 :        137 :             resolve_relative_path(line, file);
     133                 :        137 :             db.add_database(Database(line));
     134                 :        137 :             continue;
     135                 :            :         }
     136                 :            : 
     137                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     138         [ +  + ]:       1012 :         if (type == "chert") {
     139                 :        322 :             resolve_relative_path(line, file);
     140                 :        322 :             db.add_database(Chert::open(line));
     141                 :        322 :             continue;
     142                 :            :         }
     143                 :            : #endif
     144                 :            : 
     145                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     146         [ +  + ]:        690 :         if (type == "flint") {
     147                 :        318 :             resolve_relative_path(line, file);
     148                 :        318 :             db.add_database(Flint::open(line));
     149                 :        318 :             continue;
     150                 :            :         }
     151                 :            : #endif
     152                 :            : 
     153                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     154         [ +  + ]:        372 :         if (type == "brass") {
     155                 :        322 :             resolve_relative_path(line, file);
     156                 :        322 :             db.add_database(Brass::open(line));
     157                 :        322 :             continue;
     158                 :            :         }
     159                 :            : #endif
     160                 :            : 
     161                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
     162         [ +  + ]:         50 :         if (type == "remote") {
     163                 :         12 :             string::size_type colon = line.find(':');
     164         [ +  - ]:         12 :             if (colon == 0) {
     165                 :            :                 // prog
     166                 :            :                 // FIXME: timeouts
     167                 :            :                 // Is it a security risk?
     168                 :         12 :                 space = line.find(' ');
     169                 :         12 :                 string args;
     170         [ +  - ]:         12 :                 if (space != string::npos) {
     171                 :         12 :                     args.assign(line, space + 1, string::npos);
     172                 :         12 :                     line.assign(line, 1, space - 1);
     173                 :            :                 } else {
     174                 :          0 :                     line.erase(0, 1);
     175                 :            :                 }
     176                 :         12 :                 db.add_database(Remote::open(line, args));
     177         [ #  # ]:          0 :             } else if (colon != string::npos) {
     178                 :            :                 // tcp
     179                 :            :                 // FIXME: timeouts
     180                 :          0 :                 unsigned int port = atoi(line.c_str() + colon + 1);
     181                 :          0 :                 line.erase(colon);
     182                 :          0 :                 db.add_database(Remote::open(line, port));
     183                 :            :             }
     184                 :         12 :             continue;
     185                 :            :         }
     186                 :            : #endif
     187                 :            : 
     188                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     189 [ +  + ][ +  - ]:         38 :         if (type == "inmemory" && line.empty()) {
                 [ +  + ]
     190                 :          2 :             db.add_database(InMemory::open());
     191                 :          2 :             continue;
     192                 :            :         }
     193                 :            : #endif
     194                 :            : 
     195                 :            :         // Don't include the line itself - that might help an attacker
     196                 :            :         // by revealing part of a sensitive file's contents if they can
     197                 :            :         // arrange for it to be read as a stub database via infelicities in
     198                 :            :         // an application which uses Xapian.  The line number is enough
     199                 :            :         // information to identify the problem line.
     200                 :         36 :         throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
     201                 :        680 :     }
     202                 :            : 
     203                 :            :     // Allowing a stub database with no databases listed allows things like
     204                 :            :     // a "search all databases" feature to be implemented by generating a
     205                 :            :     // stub database file without having to special case there not being any
     206                 :            :     // databases yet.
     207                 :            :     //
     208                 :            :     // 1.0.x throws DatabaseOpeningError here, but with a "Bad line" message
     209                 :            :     // with the line number just past the end of the file, which is a bit odd.
     210                 :        608 : }
     211                 :            : 
     212                 :            : static void
     213                 :          5 : open_stub(WritableDatabase &db, const string &file, int action)
     214                 :            : {
     215                 :            :     // A stub database is a text file with one or more lines of this format:
     216                 :            :     // <dbtype> <serialised db object>
     217                 :            :     //
     218                 :            :     // Lines which start with a "#" character, and lines which have no spaces
     219                 :            :     // in them, are ignored.
     220                 :            :     //
     221                 :            :     // Any paths specified in stub database files which are relative will be
     222                 :            :     // considered to be relative to the directory containing the stub database.
     223                 :          5 :     ifstream stub(file.c_str());
     224                 :          5 :     string line;
     225                 :          5 :     unsigned int line_no = 0;
     226                 :         11 :     while (true) {
     227         [ -  + ]:         16 :         if (db.internal.size() > 1) {
     228                 :          0 :             throw DatabaseOpeningError(file + ": Can't open a stub database listing multiple databases as a WritableDatabase");
     229                 :            :         }
     230                 :            : 
     231         [ +  + ]:         16 :         if (!getline(stub, line)) break;
     232                 :            : 
     233                 :         11 :         ++line_no;
     234 [ +  - ][ +  + ]:         11 :         if (line.empty() || line[0] == '#')
                 [ +  + ]
     235                 :          6 :             continue;
     236                 :          5 :         string::size_type space = line.find(' ');
     237         [ +  + ]:          5 :         if (space == string::npos) space = line.size();
     238                 :            : 
     239                 :          5 :         string type(line, 0, space);
     240                 :          5 :         line.erase(0, space + 1);
     241                 :            : 
     242         [ +  + ]:          5 :         if (type == "auto") {
     243                 :          3 :             resolve_relative_path(line, file);
     244                 :          3 :             db.add_database(WritableDatabase(line, action));
     245                 :          3 :             continue;
     246                 :            :         }
     247                 :            : 
     248                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     249         [ -  + ]:          2 :         if (type == "chert") {
     250                 :          0 :             resolve_relative_path(line, file);
     251                 :          0 :             db.add_database(Chert::open(line, action));
     252                 :          0 :             continue;
     253                 :            :         }
     254                 :            : #endif
     255                 :            : 
     256                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     257         [ -  + ]:          2 :         if (type == "flint") {
     258                 :          0 :             resolve_relative_path(line, file);
     259                 :          0 :             db.add_database(Flint::open(line, action));
     260                 :          0 :             continue;
     261                 :            :         }
     262                 :            : #endif
     263                 :            : 
     264                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     265         [ -  + ]:          2 :         if (type == "brass") {
     266                 :          0 :             resolve_relative_path(line, file);
     267                 :          0 :             db.add_database(Brass::open(line, action));
     268                 :          0 :             continue;
     269                 :            :         }
     270                 :            : #endif
     271                 :            : 
     272                 :            : #ifdef XAPIAN_HAS_REMOTE_BACKEND
     273         [ -  + ]:          2 :         if (type == "remote") {
     274                 :          0 :             string::size_type colon = line.find(':');
     275         [ #  # ]:          0 :             if (colon == 0) {
     276                 :            :                 // prog
     277                 :            :                 // FIXME: timeouts
     278                 :            :                 // Is it a security risk?
     279                 :          0 :                 space = line.find(' ');
     280                 :          0 :                 string args;
     281         [ #  # ]:          0 :                 if (space != string::npos) {
     282                 :          0 :                     args.assign(line, space + 1, string::npos);
     283                 :          0 :                     line.assign(line, 1, space - 1);
     284                 :            :                 } else {
     285                 :          0 :                     line.erase(0, 1);
     286                 :            :                 }
     287                 :          0 :                 db.add_database(Remote::open_writable(line, args));
     288         [ #  # ]:          0 :             } else if (colon != string::npos) {
     289                 :            :                 // tcp
     290                 :            :                 // FIXME: timeouts
     291                 :          0 :                 unsigned int port = atoi(line.c_str() + colon + 1);
     292                 :          0 :                 line.erase(colon);
     293                 :          0 :                 db.add_database(Remote::open_writable(line, port));
     294                 :            :             }
     295                 :          0 :             continue;
     296                 :            :         }
     297                 :            : #endif
     298                 :            : 
     299                 :            : #ifdef XAPIAN_HAS_INMEMORY_BACKEND
     300 [ +  - ][ +  - ]:          2 :         if (type == "inmemory" && line.empty()) {
                 [ +  - ]
     301                 :          2 :             db.add_database(InMemory::open());
     302                 :          2 :             continue;
     303                 :            :         }
     304                 :            : #endif
     305                 :            : 
     306                 :            :         // Don't include the line itself - that might help an attacker
     307                 :            :         // by revealing part of a sensitive file's contents if they can
     308                 :            :         // arrange for it to be read as a stub database via infelicities in
     309                 :            :         // an application which uses Xapian.  The line number is enough
     310                 :            :         // information to identify the problem line.
     311                 :          0 :         throw DatabaseOpeningError(file + ':' + str(line_no) + ": Bad line");
     312                 :            :     }
     313                 :            : 
     314         [ -  + ]:          5 :     if (db.internal.empty()) {
     315                 :          0 :         throw DatabaseOpeningError(file + ": No databases listed");
     316                 :          5 :     }
     317                 :          5 : }
     318                 :            : 
     319                 :            : Database
     320                 :         31 : Auto::open_stub(const string &file)
     321                 :            : {
     322                 :            :     LOGCALL_STATIC(API, Database, "Auto::open_stub", file);
     323                 :         31 :     Database db;
     324                 :         31 :     open_stub(db, file);
     325                 :         18 :     RETURN(db);
     326                 :            : }
     327                 :            : 
     328                 :            : WritableDatabase
     329                 :          4 : Auto::open_stub(const string &file, int action)
     330                 :            : {
     331                 :            :     LOGCALL_STATIC(API, WritableDatabase, "Auto::open_stub", file | action);
     332                 :          4 :     WritableDatabase db;
     333                 :          4 :     open_stub(db, file, action);
     334                 :          0 :     RETURN(db);
     335                 :            : }
     336                 :            : 
     337                 :       4331 : Database::Database(const string &path)
     338                 :            : {
     339                 :            :     LOGCALL_CTOR(API, "Database", path);
     340                 :            : 
     341                 :            :     struct stat statbuf;
     342   [ +  +  #  # ]:       4331 :     if (stat(path, &statbuf) == -1) {
     343                 :         40 :         throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
     344                 :            :     }
     345                 :            : 
     346 [ +  + ][ #  # ]:       4291 :     if (S_ISREG(statbuf.st_mode)) {
     347                 :            :         // The path is a file, so assume it is a stub database file.
     348                 :        521 :         open_stub(*this, path);
     349                 :        503 :         return;
     350                 :            :     }
     351                 :            : 
     352 [ -  + ][ #  # ]:       3770 :     if (rare(!S_ISDIR(statbuf.st_mode))) {
     353                 :          0 :         throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
     354                 :            :     }
     355                 :            : 
     356                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     357 [ +  + ][ #  # ]:       3770 :     if (file_exists(path + "/iamchert")) {
     358                 :       1267 :         internal.push_back(new ChertDatabase(path));
     359                 :       1267 :         return;
     360                 :            :     }
     361                 :            : #endif
     362                 :            : 
     363                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     364 [ +  + ][ #  # ]:       2503 :     if (file_exists(path + "/iamflint")) {
     365                 :       1194 :         internal.push_back(new FlintDatabase(path));
     366                 :       1192 :         return;
     367                 :            :     }
     368                 :            : #endif
     369                 :            : 
     370                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     371 [ +  + ][ #  # ]:       1310 :     if (file_exists(path + "/iambrass")) {
     372                 :       1218 :         internal.push_back(new BrassDatabase(path));
     373                 :       1218 :         return;
     374                 :            :     }
     375                 :            : #endif
     376                 :            : 
     377                 :            :     // Check for "stub directories".
     378                 :         92 :     string stub_file = path;
     379                 :         92 :     stub_file += "/XAPIANDB";
     380   [ -  +  #  # ]:         92 :     if (rare(!file_exists(stub_file))) {
     381                 :          0 :         throw DatabaseOpeningError("Couldn't detect type of database");
     382                 :            :     }
     383                 :            : 
     384                 :         92 :     open_stub(*this, stub_file);
     385                 :       4331 : }
     386                 :            : 
     387                 :            : #if defined XAPIAN_HAS_FLINT_BACKEND || \
     388                 :            :     defined XAPIAN_HAS_CHERT_BACKEND || \
     389                 :            :     defined XAPIAN_HAS_BRASS_BACKEND
     390                 :            : #define HAVE_DISK_BACKEND
     391                 :            : #endif
     392                 :            : 
     393                 :       1041 : WritableDatabase::WritableDatabase(const std::string &path, int action)
     394                 :       1041 :     : Database()
     395                 :            : {
     396                 :            :     LOGCALL_CTOR(API, "WritableDatabase", path | action);
     397                 :            : #ifdef HAVE_DISK_BACKEND
     398                 :            :     enum {
     399                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     400                 :            :         CHERT,
     401                 :            : #endif
     402                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     403                 :            :         FLINT,
     404                 :            : #endif
     405                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     406                 :            :         BRASS,
     407                 :            : #endif
     408                 :            :         UNSET
     409                 :       1041 :     } type = UNSET;
     410                 :            : #endif
     411                 :            :     struct stat statbuf;
     412   [ +  +  #  # ]:       1041 :     if (stat(path, &statbuf) == -1) {
     413                 :            :         // ENOENT probably just means that we need to create the directory.
     414 [ -  + ][ #  # ]:         24 :         if (errno != ENOENT)
     415                 :          0 :             throw DatabaseOpeningError("Couldn't stat '" + path + "'", errno);
     416                 :            :     } else {
     417                 :            :         // File or directory already exists.
     418                 :            : 
     419 [ +  + ][ #  # ]:       1017 :         if (S_ISREG(statbuf.st_mode)) {
     420                 :            :             // The path is a file, so assume it is a stub database file.
     421                 :          1 :             open_stub(*this, path, action);
     422                 :          1 :             return;
     423                 :            :         }
     424                 :            : 
     425 [ -  + ][ #  # ]:       1016 :         if (rare(!S_ISDIR(statbuf.st_mode))) {
     426                 :          0 :             throw DatabaseOpeningError("Not a regular file or directory: '" + path + "'");
     427                 :            :         }
     428                 :            : 
     429 [ +  + ][ #  # ]:       1016 :         if (file_exists(path + "/iamchert")) {
     430                 :            :             // Existing chert DB.
     431                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     432                 :        343 :             type = CHERT;
     433                 :            : #else
     434                 :            :             throw FeatureUnavailableError("Chert backend disabled");
     435                 :            : #endif
     436 [ +  + ][ #  # ]:        673 :         } else if (file_exists(path + "/iamflint")) {
     437                 :            :             // Existing flint DB.
     438                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     439                 :        336 :             type = FLINT;
     440                 :            : #else
     441                 :            :             throw FeatureUnavailableError("Flint backend disabled");
     442                 :            : #endif
     443 [ +  - ][ #  # ]:        337 :         } else if (file_exists(path + "/iambrass")) {
     444                 :            :             // Existing brass DB.
     445                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     446                 :        337 :             type = BRASS;
     447                 :            : #else
     448                 :            :             throw FeatureUnavailableError("Brass backend disabled");
     449                 :            : #endif
     450                 :            :         } else {
     451                 :            :             // Check for "stub directories".
     452                 :          0 :             string stub_file = path;
     453                 :          0 :             stub_file += "/XAPIANDB";
     454   [ #  #  #  # ]:          0 :             if (usual(file_exists(stub_file))) {
     455                 :          0 :                 open_stub(*this, stub_file, action);
     456                 :            :                 return;
     457 [ #  # ][ #  # ]:          0 :             }
     458                 :            :         }
     459                 :            :     }
     460                 :            : 
     461                 :            : #ifdef HAVE_DISK_BACKEND
     462   [ +  +  +  + ]:       1040 :     switch (type) {
           [ #  #  #  # ]
     463                 :            :         case UNSET: {
     464                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     465                 :            :             // If only brass is enabled, there's no point checking the
     466                 :            :             // environmental variable.
     467                 :            : # if defined XAPIAN_HAS_CHERT_BACKEND || defined XAPIAN_HAS_FLINT_BACKEND
     468                 :            :             // If $XAPIAN_PREFER_BRASS is set to a non-empty value, prefer brass
     469                 :            :             // if there's no existing database.
     470                 :         24 :             const char *p = getenv("XAPIAN_PREFER_BRASS");
     471 [ -  +  #  #  # :         24 :             if (p && *p)
              # ][ #  # ]
     472                 :          0 :                 goto brass;
     473                 :            : #endif
     474                 :            : #endif
     475                 :            :         }
     476                 :            :         // Fall through to first enabled case, so order the remaining cases
     477                 :            :         // by preference.
     478                 :            : #ifdef XAPIAN_HAS_CHERT_BACKEND
     479                 :            :         case CHERT:
     480                 :        368 :             internal.push_back(new ChertWritableDatabase(path, action, 8192));
     481                 :        366 :             break;
     482                 :            : #endif
     483                 :            : #ifdef XAPIAN_HAS_FLINT_BACKEND
     484                 :            :         case FLINT:
     485                 :        338 :             internal.push_back(new FlintWritableDatabase(path, action, 8192));
     486                 :        334 :             break;
     487                 :            : #endif
     488                 :            : #ifdef XAPIAN_HAS_BRASS_BACKEND
     489                 :            :         case BRASS:
     490                 :            : brass:
     491                 :       1038 :             internal.push_back(new BrassWritableDatabase(path, action, 8192));
     492                 :            :             break;
     493                 :            : #endif
     494                 :            :     }
     495                 :            : #else
     496                 :            :     throw FeatureUnavailableError("No disk-based writable backend is enabled");
     497                 :            : #endif
     498                 :       1041 : }
     499                 :            : 
     500                 :            : }

Generated by: LCOV version 1.8