LCOV - code coverage report
Current view: top level - backends/honey - honey_version.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 7028d852e609 Lines: 164 235 69.8 %
Date: 2019-02-17 14:59:59 Functions: 13 14 92.9 %
Branches: 105 328 32.0 %

           Branch data     Line data    Source code
       1                 :            : /** @file honey_version.cc
       2                 :            :  * @brief HoneyVersion class
       3                 :            :  */
       4                 :            : /* Copyright (C) 2006,2007,2008,2009,2010,2013,2014,2015,2016,2017,2018 Olly Betts
       5                 :            :  * Copyright (C) 2011 Dan Colish
       6                 :            :  *
       7                 :            :  * This program is free software; you can redistribute it and/or modify
       8                 :            :  * it under the terms of the GNU General Public License as published by
       9                 :            :  * the Free Software Foundation; either version 2 of the License, or
      10                 :            :  * (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 USA
      20                 :            :  */
      21                 :            : 
      22                 :            : #include <config.h>
      23                 :            : 
      24                 :            : #include "honey_version.h"
      25                 :            : 
      26                 :            : #include "debuglog.h"
      27                 :            : #include "fd.h"
      28                 :            : #include "honey_defs.h"
      29                 :            : #include "io_utils.h"
      30                 :            : #include "min_non_zero.h"
      31                 :            : #include "omassert.h"
      32                 :            : #include "pack.h"
      33                 :            : #include "posixy_wrapper.h"
      34                 :            : #include "stringutils.h" // For STRINGIZE() and CONST_STRLEN().
      35                 :            : 
      36                 :            : #include <cerrno>
      37                 :            : #include <cstring> // For memcmp().
      38                 :            : #include <string>
      39                 :            : #include <sys/types.h>
      40                 :            : #include "safesysstat.h"
      41                 :            : #include "safefcntl.h"
      42                 :            : #include "safeunistd.h"
      43                 :            : #include "str.h"
      44                 :            : #include "stringutils.h"
      45                 :            : 
      46                 :            : #include "backends/uuids.h"
      47                 :            : 
      48                 :            : #include "xapian/constants.h"
      49                 :            : #include "xapian/error.h"
      50                 :            : 
      51                 :            : using namespace std;
      52                 :            : 
      53                 :            : /// Honey format version (date of change):
      54                 :            : #define HONEY_FORMAT_VERSION DATE_TO_VERSION(2018,4,3)
      55                 :            : // 2018,4,3   1.5.0 outlaw mixed-wdf terms
      56                 :            : // 2018,3,28        don't special case first entry in SSTable
      57                 :            : // 2018,3,27        new key format for value stats, value chunks, doclen chunks
      58                 :            : // 2018,3,26        use known suffix from spelling B and T keys
      59                 :            : // 2018,3,25        use known prefix from spelling B and H keys
      60                 :            : // 2018,3,15        avoid storing flat wdf
      61                 :            : // 2018,3,14        store per term wdf_max
      62                 :            : // 2018,3,12        binary chop index
      63                 :            : // 2018,3,11        spelling key encoding changed
      64                 :            : // 2018,2,22        index valuestream chunks by last docid in chunk
      65                 :            : // 2018,2,21        index doclen chunks by last docid in chunk
      66                 :            : // 2018,2,20        implement array index
      67                 :            : // 2018,2,19        allow 1,2,3 as well as 4 byte doc length width
      68                 :            : // 2018,2,2         special case tf=2; first_wdf = floor(collfreq/2)
      69                 :            : // 2018,2,1         pack_uint for postlist data
      70                 :            : // 2018,1,31        Special case postlist when termfreq==2
      71                 :            : // 2018,1,30        More compact postlist chunk headers
      72                 :            : // 2018,1,23        Elide last-first for single occurrence terms
      73                 :            : // 2018,1,4         Merge values used and terms used
      74                 :            : // 2018,1,3         Table start offset in RootInfo
      75                 :            : // 2017,12,30       Value stats key changes
      76                 :            : // 2017,12,29       User metadata key changes
      77                 :            : // 2017,12,5        New Honey backend
      78                 :            : 
      79                 :            : /// Convert date <-> version number.  Dates up to 2141-12-31 fit in 2 bytes.
      80                 :            : #define DATE_TO_VERSION(Y,M,D) \
      81                 :            :         ((unsigned(Y) - 2014) << 9 | unsigned(M) << 5 | unsigned(D))
      82                 :            : #define VERSION_TO_YEAR(V) ((unsigned(V) >> 9) + 2014)
      83                 :            : #define VERSION_TO_MONTH(V) ((unsigned(V) >> 5) & 0x0f)
      84                 :            : #define VERSION_TO_DAY(V) (unsigned(V) & 0x1f)
      85                 :            : 
      86                 :            : #define HONEY_VERSION_MAGIC_LEN 14
      87                 :            : #define HONEY_VERSION_MAGIC_AND_VERSION_LEN 16
      88                 :            : 
      89                 :            : static const char HONEY_VERSION_MAGIC[HONEY_VERSION_MAGIC_AND_VERSION_LEN] = {
      90                 :            :     '\x0f', '\x0d', 'X', 'a', 'p', 'i', 'a', 'n', ' ', 'H', 'o', 'n', 'e', 'y',
      91                 :            :     char((HONEY_FORMAT_VERSION >> 8) & 0xff), char(HONEY_FORMAT_VERSION & 0xff)
      92                 :            : };
      93                 :            : 
      94                 :          3 : HoneyVersion::HoneyVersion(int fd_)
      95                 :            :     : rev(0), fd(fd_), offset(0), db_dir(),
      96                 :            :       doccount(0), total_doclen(0), last_docid(0),
      97                 :            :       doclen_lbound(0), doclen_ubound(0),
      98                 :            :       wdf_ubound(0), spelling_wordfreq_ubound(0),
      99                 :            :       oldest_changeset(0),
     100 [ +  - ][ +  + ]:         39 :       uniq_terms_lbound(0), uniq_terms_ubound(0)
         [ +  - ][ +  + ]
         [ +  - ][ +  -  
          #  #  #  #  #  
          #  #  #  #  #  
             #  #  #  # ]
     101                 :            : {
     102                 :          3 :     offset = lseek(fd, 0, SEEK_CUR);
     103         [ -  + ]:          3 :     if (rare(offset == off_t(-1))) {
     104         [ #  # ]:          0 :         string msg = "lseek failed on file descriptor ";
     105 [ #  # ][ #  # ]:          0 :         msg += str(fd);
     106         [ #  # ]:          0 :         throw Xapian::DatabaseOpeningError(msg, errno);
     107                 :            :     }
     108         [ #  # ]:          3 : }
     109                 :            : 
     110 [ +  - ][ +  + ]:       8344 : HoneyVersion::~HoneyVersion()
         [ +  - ][ +  + ]
     111                 :            : {
     112                 :            :     // Either this is a single-file database, or this fd is from opening a new
     113                 :            :     // version file in write(), but sync() was never called.
     114         [ +  + ]:        596 :     if (fd != -1)
     115                 :          3 :         (void)::close(fd);
     116                 :        596 : }
     117                 :            : 
     118                 :            : void
     119                 :        302 : HoneyVersion::read()
     120                 :            : {
     121                 :            :     LOGCALL_VOID(DB, "HoneyVersion::read", NO_ARGS);
     122                 :        302 :     FD close_fd(-1);
     123                 :            :     int fd_in;
     124         [ +  + ]:        302 :     if (single_file()) {
     125         [ -  + ]:          1 :         if (rare(lseek(fd, offset, SEEK_SET) == off_t(-1))) {
     126         [ #  # ]:          0 :             string msg = "Failed to rewind file descriptor ";
     127 [ #  # ][ #  # ]:          0 :             msg += str(fd);
     128         [ #  # ]:          0 :             throw Xapian::DatabaseOpeningError(msg, errno);
     129                 :            :         }
     130                 :          1 :         fd_in = fd;
     131                 :            :     } else {
     132         [ +  - ]:        301 :         string filename = db_dir;
     133         [ +  - ]:        301 :         filename += "/iamhoney";
     134         [ +  - ]:        301 :         fd_in = posixy_open(filename.c_str(), O_RDONLY|O_BINARY);
     135         [ -  + ]:        301 :         if (rare(fd_in < 0)) {
     136         [ #  # ]:          0 :             string msg = filename;
     137         [ #  # ]:          0 :             msg += ": Failed to open honey revision file for reading";
     138         [ #  # ]:          0 :             throw Xapian::DatabaseOpeningError(msg, errno);
     139                 :            :         }
     140         [ +  - ]:        301 :         close_fd = fd_in;
     141                 :            :     }
     142                 :            : 
     143                 :            :     char buf[256];
     144                 :            : 
     145                 :        302 :     const char * p = buf;
     146         [ +  - ]:        302 :     const char * end = p + io_read(fd_in, buf, sizeof(buf), 33);
     147                 :            : 
     148         [ -  + ]:        302 :     if (memcmp(buf, HONEY_VERSION_MAGIC, HONEY_VERSION_MAGIC_LEN) != 0)
     149 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseCorruptError("Rev file magic incorrect");
                 [ #  # ]
     150                 :            : 
     151                 :            :     unsigned version;
     152                 :        302 :     version = static_cast<unsigned char>(buf[HONEY_VERSION_MAGIC_LEN]);
     153                 :        302 :     version <<= 8;
     154                 :        302 :     version |= static_cast<unsigned char>(buf[HONEY_VERSION_MAGIC_LEN + 1]);
     155         [ -  + ]:        302 :     if (version != HONEY_FORMAT_VERSION) {
     156         [ #  # ]:          0 :         string msg;
     157         [ #  # ]:          0 :         if (!single_file()) {
     158         [ #  # ]:          0 :             msg = db_dir;
     159         [ #  # ]:          0 :             msg += ": ";
     160                 :            :         }
     161         [ #  # ]:          0 :         msg += "Database is format version ";
     162         [ #  # ]:          0 :         msg += str(VERSION_TO_YEAR(version) * 10000 +
     163                 :          0 :                    VERSION_TO_MONTH(version) * 100 +
     164         [ #  # ]:          0 :                    VERSION_TO_DAY(version));
     165         [ #  # ]:          0 :         msg += " but I only understand ";
     166         [ #  # ]:          0 :         msg += str(VERSION_TO_YEAR(HONEY_FORMAT_VERSION) * 10000 +
     167                 :            :                    VERSION_TO_MONTH(HONEY_FORMAT_VERSION) * 100 +
     168         [ #  # ]:          0 :                    VERSION_TO_DAY(HONEY_FORMAT_VERSION));
     169 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseVersionError(msg);
     170                 :            :     }
     171                 :            : 
     172                 :        302 :     p += HONEY_VERSION_MAGIC_AND_VERSION_LEN;
     173                 :        302 :     uuid.assign(p);
     174                 :        302 :     p += uuid.BINARY_SIZE;
     175                 :            : 
     176         [ -  + ]:        302 :     if (!unpack_uint(&p, end, &rev)) {
     177                 :            :         throw Xapian::DatabaseCorruptError("Rev file failed to decode "
     178 [ #  # ][ #  # ]:          0 :                                            "revision");
                 [ #  # ]
     179                 :            :     }
     180                 :            : 
     181         [ +  + ]:       2114 :     for (unsigned table_no = 0; table_no < Honey::MAX_; ++table_no) {
     182 [ +  - ][ -  + ]:       1812 :         if (!root[table_no].unserialise(&p, end)) {
     183 [ #  # ][ #  # ]:          0 :             throw Xapian::DatabaseCorruptError("Rev file root_info missing");
                 [ #  # ]
     184                 :            :         }
     185         [ +  - ]:       1812 :         old_root[table_no] = root[table_no];
     186                 :            :     }
     187                 :            : 
     188                 :            :     // For a single-file database, this will assign extra data.  We read
     189                 :            :     // sizeof(buf) above, then skip HONEY_VERSION_MAGIC_AND_VERSION_LEN,
     190                 :            :     // then 16, then the size of the serialised root info.
     191         [ +  - ]:        302 :     serialised_stats.assign(p, end);
     192         [ +  - ]:        302 :     unserialise_stats();
     193                 :        302 : }
     194                 :            : 
     195                 :            : void
     196                 :        294 : HoneyVersion::serialise_stats()
     197                 :            : {
     198                 :        294 :     serialised_stats.resize(0);
     199                 :        294 :     pack_uint(serialised_stats, doccount);
     200                 :            :     // last_docid must always be >= doccount.
     201                 :        294 :     pack_uint(serialised_stats, last_docid - doccount);
     202                 :        294 :     pack_uint(serialised_stats, doclen_lbound);
     203                 :        294 :     pack_uint(serialised_stats, wdf_ubound);
     204                 :            :     // doclen_ubound should always be >= wdf_ubound, so we store the
     205                 :            :     // difference as it may encode smaller.  wdf_ubound is likely to
     206                 :            :     // be larger than doclen_lbound.
     207                 :        294 :     pack_uint(serialised_stats, doclen_ubound - wdf_ubound);
     208                 :        294 :     pack_uint(serialised_stats, oldest_changeset);
     209                 :        294 :     pack_uint(serialised_stats, total_doclen);
     210                 :        294 :     pack_uint(serialised_stats, spelling_wordfreq_ubound);
     211                 :            :     // If total_doclen == 0 then unique_terms is always zero (or there are no
     212                 :            :     // documents at all) so storing these just complicates things because
     213                 :            :     // uniq_terms_lbound could legitimately be zero.
     214         [ +  + ]:        294 :     if (total_doclen != 0) {
     215                 :            :         // We rely on uniq_terms_lbound being non-zero to detect if it's present
     216                 :            :         // for a single file DB.
     217                 :            :         Assert(uniq_terms_lbound != 0);
     218                 :        288 :         pack_uint(serialised_stats, uniq_terms_lbound);
     219                 :        288 :         pack_uint(serialised_stats, uniq_terms_ubound);
     220                 :            :     }
     221                 :        294 : }
     222                 :            : 
     223                 :            : void
     224                 :        302 : HoneyVersion::unserialise_stats()
     225                 :            : {
     226                 :        302 :     const char * p = serialised_stats.data();
     227                 :        302 :     const char * end = p + serialised_stats.size();
     228         [ -  + ]:        302 :     if (p == end) {
     229                 :          0 :         doccount = 0;
     230                 :          0 :         total_doclen = 0;
     231                 :          0 :         last_docid = 0;
     232                 :          0 :         doclen_lbound = 0;
     233                 :          0 :         doclen_ubound = 0;
     234                 :          0 :         wdf_ubound = 0;
     235                 :          0 :         oldest_changeset = 0;
     236                 :          0 :         spelling_wordfreq_ubound = 0;
     237                 :          0 :         uniq_terms_lbound = 0;
     238                 :          0 :         uniq_terms_ubound = 0;
     239                 :        302 :         return;
     240                 :            :     }
     241                 :            : 
     242 [ +  - ][ -  + ]:        906 :     if (!unpack_uint(&p, end, &doccount) ||
     243         [ +  - ]:        604 :         !unpack_uint(&p, end, &last_docid) ||
     244         [ +  - ]:        604 :         !unpack_uint(&p, end, &doclen_lbound) ||
     245         [ +  - ]:        604 :         !unpack_uint(&p, end, &wdf_ubound) ||
     246         [ +  - ]:        604 :         !unpack_uint(&p, end, &doclen_ubound) ||
     247         [ +  - ]:        604 :         !unpack_uint(&p, end, &oldest_changeset) ||
     248 [ +  - ][ -  + ]:        906 :         !unpack_uint(&p, end, &total_doclen) ||
     249                 :        302 :         !unpack_uint(&p, end, &spelling_wordfreq_ubound)) {
     250                 :            :         const char * m = p ?
     251                 :            :             "Bad serialised DB stats (overflowed)" :
     252         [ #  # ]:          0 :             "Bad serialised DB stats (out of data)";
     253 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseCorruptError(m);
                 [ #  # ]
     254                 :            :     }
     255                 :            : 
     256                 :            :     // last_docid must always be >= doccount.
     257                 :        302 :     last_docid += doccount;
     258                 :            :     // doclen_ubound should always be >= wdf_ubound, so we store the
     259                 :            :     // difference as it may encode smaller.  wdf_ubound is likely to
     260                 :            :     // be larger than doclen_lbound.
     261                 :        302 :     doclen_ubound += wdf_ubound;
     262                 :            : 
     263                 :            :     // We don't check if there's undecoded data between p and end - in the
     264                 :            :     // single-file DB case there will be extra zero bytes in serialised_stats,
     265                 :            :     // and more generally it's useful to be able to add new stats when it is
     266                 :            :     // safe for old versions to just ignore them and there are sensible values
     267                 :            :     // to use when a new version reads an old database.
     268                 :            : 
     269                 :            :     // Read bounds on unique_terms if stored.  This test relies on the first
     270                 :            :     // byte of pack_uint(x) being zero if and only if x is zero, and on
     271                 :            :     // uniq_terms_lbound being non-zero.
     272 [ +  + ][ -  + ]:        302 :     if (p == end || *p == '\0') {
     273                 :            :         // No bounds stored so use weak bounds based on other stats.
     274         [ +  - ]:         16 :         if (total_doclen == 0) {
     275                 :          8 :             uniq_terms_lbound = uniq_terms_ubound = 0;
     276                 :            :         } else {
     277                 :            :             Assert(doclen_lbound != 0);
     278                 :            :             Assert(wdf_ubound != 0);
     279                 :          0 :             uniq_terms_lbound = (doclen_lbound - 1) / wdf_ubound + 1;
     280                 :          0 :             uniq_terms_ubound = doclen_ubound;
     281                 :            :         }
     282   [ +  -  -  + ]:        588 :     } else if (!unpack_uint(&p, end, &uniq_terms_lbound) ||
                 [ -  + ]
     283                 :        294 :                !unpack_uint(&p, end, &uniq_terms_ubound)) {
     284                 :            :         const char * m = p ?
     285                 :            :             "Bad serialised unique_terms bounds (overflowed)" :
     286         [ #  # ]:          0 :             "Bad serialised unique_terms bounds (out of data)";
     287 [ #  # ][ #  # ]:        302 :         throw Xapian::DatabaseCorruptError(m);
                 [ #  # ]
     288                 :            :     }
     289                 :            : }
     290                 :            : 
     291                 :            : void
     292                 :         15 : HoneyVersion::merge_stats(const HoneyVersion & o)
     293                 :            : {
     294                 :         15 :     doccount += o.get_doccount();
     295         [ -  + ]:         15 :     if (doccount < o.get_doccount()) {
     296 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("doccount overflowed!");
                 [ #  # ]
     297                 :            :     }
     298                 :            : 
     299         [ +  - ]:         15 :     doclen_lbound = min_non_zero(doclen_lbound, o.get_doclength_lower_bound());
     300                 :         15 :     doclen_ubound = max(doclen_ubound, o.get_doclength_upper_bound());
     301                 :         15 :     wdf_ubound = max(wdf_ubound, o.get_wdf_upper_bound());
     302                 :         15 :     total_doclen += o.get_total_doclen();
     303         [ -  + ]:         15 :     if (total_doclen < o.get_total_doclen()) {
     304 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("Total document length overflowed!");
                 [ #  # ]
     305                 :            :     }
     306                 :            : 
     307                 :            :     // The upper bounds might be on the same word, so we must sum them.
     308                 :         15 :     spelling_wordfreq_ubound += o.get_spelling_wordfreq_upper_bound();
     309                 :            : 
     310                 :            :     uniq_terms_lbound = min_non_zero(uniq_terms_lbound,
     311         [ +  - ]:         15 :                                      o.get_unique_terms_lower_bound());
     312                 :            :     uniq_terms_ubound = max(uniq_terms_ubound,
     313                 :         15 :                             o.get_unique_terms_upper_bound());
     314                 :         15 : }
     315                 :            : 
     316                 :            : void
     317                 :        285 : HoneyVersion::merge_stats(Xapian::doccount o_doccount,
     318                 :            :                           Xapian::termcount o_doclen_lbound,
     319                 :            :                           Xapian::termcount o_doclen_ubound,
     320                 :            :                           Xapian::termcount o_wdf_ubound,
     321                 :            :                           Xapian::totallength o_total_doclen,
     322                 :            :                           Xapian::termcount o_spelling_wordfreq_ubound,
     323                 :            :                           Xapian::termcount o_uniq_terms_lbound,
     324                 :            :                           Xapian::termcount o_uniq_terms_ubound)
     325                 :            : {
     326                 :        285 :     doccount += o_doccount;
     327         [ -  + ]:        285 :     if (doccount < o_doccount) {
     328 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("doccount overflowed!");
                 [ #  # ]
     329                 :            :     }
     330                 :            : 
     331                 :        285 :     doclen_lbound = min_non_zero(doclen_lbound, o_doclen_lbound);
     332                 :        285 :     doclen_ubound = max(doclen_ubound, o_doclen_ubound);
     333                 :        285 :     wdf_ubound = max(wdf_ubound, o_wdf_ubound);
     334                 :        285 :     total_doclen += o_total_doclen;
     335         [ -  + ]:        285 :     if (total_doclen < o_total_doclen) {
     336 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("Total document length overflowed!");
                 [ #  # ]
     337                 :            :     }
     338                 :            : 
     339                 :            :     // The upper bounds might be on the same word, so we must sum them.
     340                 :        285 :     spelling_wordfreq_ubound += o_spelling_wordfreq_ubound;
     341                 :            : 
     342                 :        285 :     uniq_terms_lbound = min_non_zero(uniq_terms_lbound, o_uniq_terms_lbound);
     343                 :        285 :     uniq_terms_ubound = max(uniq_terms_ubound, o_uniq_terms_ubound);
     344                 :        285 : }
     345                 :            : 
     346                 :            : void
     347                 :          0 : HoneyVersion::cancel()
     348                 :            : {
     349                 :            :     LOGCALL_VOID(DB, "HoneyVersion::cancel", NO_ARGS);
     350         [ #  # ]:          0 :     for (unsigned table_no = 0; table_no < Honey::MAX_; ++table_no) {
     351                 :          0 :         root[table_no] = old_root[table_no];
     352                 :            :     }
     353                 :          0 :     unserialise_stats();
     354                 :          0 : }
     355                 :            : 
     356                 :            : const string
     357                 :        294 : HoneyVersion::write(honey_revision_number_t new_rev, int flags)
     358                 :            : {
     359                 :            :     LOGCALL(DB, const string, "HoneyVersion::write", new_rev|flags);
     360                 :            : 
     361         [ +  - ]:        294 :     string s(HONEY_VERSION_MAGIC, HONEY_VERSION_MAGIC_AND_VERSION_LEN);
     362         [ +  - ]:        294 :     s.append(uuid.data(), uuid.BINARY_SIZE);
     363                 :            : 
     364         [ +  - ]:        294 :     pack_uint(s, new_rev);
     365                 :            : 
     366         [ +  + ]:       2058 :     for (unsigned table_no = 0; table_no < Honey::MAX_; ++table_no) {
     367         [ +  - ]:       1764 :         root[table_no].serialise(s);
     368                 :            :     }
     369                 :            : 
     370                 :            :     // Serialise database statistics.
     371         [ +  - ]:        294 :     serialise_stats();
     372         [ +  - ]:        294 :     s += serialised_stats;
     373                 :            : 
     374         [ +  - ]:        294 :     string tmpfile;
     375         [ +  + ]:        294 :     if (!single_file()) {
     376         [ +  - ]:        292 :         tmpfile = db_dir;
     377                 :            :         // In dangerous mode, just write the new version file in place.
     378         [ +  - ]:        292 :         if (flags & Xapian::DB_DANGEROUS)
     379         [ +  - ]:        292 :             tmpfile += "/iamhoney";
     380                 :            :         else
     381         [ #  # ]:          0 :             tmpfile += "/v.tmp";
     382                 :            : 
     383                 :        292 :         int open_flags = O_CREAT|O_TRUNC|O_WRONLY|O_BINARY;
     384         [ +  - ]:        292 :         fd = posixy_open(tmpfile.c_str(), open_flags, 0666);
     385         [ -  + ]:        292 :         if (rare(fd < 0)) {
     386         [ #  # ]:          0 :             string msg = "Couldn't write new rev file: ";
     387         [ #  # ]:          0 :             msg += tmpfile;
     388         [ #  # ]:          0 :             throw Xapian::DatabaseOpeningError(msg, errno);
     389                 :            :         }
     390                 :            : 
     391         [ +  - ]:        292 :         if (flags & Xapian::DB_DANGEROUS)
     392 [ +  - ][ +  - ]:        292 :             tmpfile = string();
     393                 :            :     }
     394                 :            : 
     395                 :            :     try {
     396         [ +  - ]:        294 :         io_write(fd, s.data(), s.size());
     397                 :          0 :     } catch (...) {
     398         [ #  # ]:          0 :         if (!single_file())
     399         [ #  # ]:          0 :             (void)close(fd);
     400                 :          0 :         throw;
     401                 :            :     }
     402                 :            : 
     403                 :        294 :     RETURN(tmpfile);
     404                 :            : }
     405                 :            : 
     406                 :            : bool
     407                 :        294 : HoneyVersion::sync(const string & tmpfile,
     408                 :            :                    honey_revision_number_t new_rev, int flags)
     409                 :            : {
     410                 :            :     Assert(new_rev > rev || rev == 0);
     411                 :            : 
     412         [ +  + ]:        294 :     if (single_file()) {
     413 [ +  - ][ -  +  :          4 :         if ((flags & Xapian::DB_NO_SYNC) == 0 &&
             #  #  -  + ]
     414                 :          2 :             ((flags & Xapian::DB_FULL_SYNC) ?
     415                 :          0 :               !io_full_sync(fd) :
     416                 :          2 :               !io_sync(fd))) {
     417                 :            :             // FIXME what to do?
     418                 :            :         }
     419                 :            :     } else {
     420                 :        292 :         int fd_to_close = fd;
     421                 :        292 :         fd = -1;
     422 [ +  - ][ -  +  :        584 :         if ((flags & Xapian::DB_NO_SYNC) == 0 &&
             #  #  -  + ]
                 [ -  + ]
     423                 :        292 :             ((flags & Xapian::DB_FULL_SYNC) ?
     424                 :          0 :               !io_full_sync(fd_to_close) :
     425                 :        292 :               !io_sync(fd_to_close))) {
     426                 :          0 :             int save_errno = errno;
     427                 :          0 :             (void)close(fd_to_close);
     428         [ #  # ]:          0 :             if (!tmpfile.empty())
     429                 :          0 :                 (void)unlink(tmpfile.c_str());
     430                 :          0 :             errno = save_errno;
     431                 :          0 :             return false;
     432                 :            :         }
     433                 :            : 
     434         [ -  + ]:        292 :         if (close(fd_to_close) != 0) {
     435         [ #  # ]:          0 :             if (!tmpfile.empty()) {
     436                 :          0 :                 int save_errno = errno;
     437                 :          0 :                 (void)unlink(tmpfile.c_str());
     438                 :          0 :                 errno = save_errno;
     439                 :            :             }
     440                 :          0 :             return false;
     441                 :            :         }
     442                 :            : 
     443         [ -  + ]:        292 :         if (!tmpfile.empty()) {
     444 [ #  # ][ #  # ]:          0 :             if (!io_tmp_rename(tmpfile, db_dir + "/iamhoney")) {
     445                 :          0 :                 return false;
     446                 :            :             }
     447                 :            :         }
     448                 :            :     }
     449                 :            : 
     450         [ +  + ]:       2058 :     for (unsigned table_no = 0; table_no < Honey::MAX_; ++table_no) {
     451                 :       1764 :         old_root[table_no] = root[table_no];
     452                 :            :     }
     453                 :            : 
     454                 :        294 :     rev = new_rev;
     455                 :        294 :     return true;
     456                 :            : }
     457                 :            : 
     458                 :            : // Only try to compress tags longer than this many bytes.
     459                 :            : const size_t COMPRESS_MIN = 4;
     460                 :            : 
     461                 :            : static const uint4 compress_min_tab[] = {
     462                 :            :     0, // POSTLIST
     463                 :            :     COMPRESS_MIN, // DOCDATA
     464                 :            :     COMPRESS_MIN, // TERMLIST
     465                 :            :     0, // POSITION
     466                 :            :     COMPRESS_MIN, // SPELLING
     467                 :            :     COMPRESS_MIN  // SYNONYM
     468                 :            : };
     469                 :            : 
     470                 :            : void
     471                 :        294 : HoneyVersion::create()
     472                 :            : {
     473                 :        294 :     uuid.generate();
     474         [ +  + ]:       2058 :     for (unsigned table_no = 0; table_no < Honey::MAX_; ++table_no) {
     475                 :       1764 :         root[table_no].init(compress_min_tab[table_no]);
     476                 :            :     }
     477                 :        294 : }
     478                 :            : 
     479                 :            : namespace Honey {
     480                 :            : 
     481                 :            : void
     482                 :       1764 : RootInfo::init(uint4 compress_min_)
     483                 :            : {
     484                 :       1764 :     offset = 0;
     485                 :       1764 :     root = 0;
     486                 :       1764 :     num_entries = 0;
     487                 :       1764 :     compress_min = compress_min_;
     488                 :       1764 :     fl_serialised.resize(0);
     489                 :       1764 : }
     490                 :            : 
     491                 :            : void
     492                 :       1764 : RootInfo::serialise(string &s) const
     493                 :            : {
     494                 :            :     AssertRel(offset, >=, 0);
     495                 :       1764 :     std::make_unsigned<off_t>::type uoffset = offset;
     496                 :            :     AssertRel(root, >=, offset);
     497                 :       1764 :     pack_uint(s, uoffset);
     498                 :       1764 :     pack_uint(s, root - uoffset);
     499                 :       1764 :     pack_uint(s, 0u);
     500                 :       1764 :     pack_uint(s, num_entries);
     501                 :       1764 :     pack_uint(s, 2048u >> 11);
     502                 :       1764 :     pack_uint(s, compress_min);
     503                 :       1764 :     pack_string(s, fl_serialised);
     504                 :       1764 : }
     505                 :            : 
     506                 :            : bool
     507                 :       1812 : RootInfo::unserialise(const char ** p, const char * end)
     508                 :            : {
     509                 :            :     std::make_unsigned<off_t>::type uoffset, uroot;
     510                 :            :     unsigned dummy_val;
     511                 :            :     unsigned dummy_blocksize;
     512 [ +  - ][ -  + ]:       5436 :     if (!unpack_uint(p, end, &uoffset) ||
     513         [ +  - ]:       3624 :         !unpack_uint(p, end, &uroot) ||
     514         [ +  - ]:       3624 :         !unpack_uint(p, end, &dummy_val) ||
     515         [ +  - ]:       3624 :         !unpack_uint(p, end, &num_entries) ||
     516         [ +  - ]:       3624 :         !unpack_uint(p, end, &dummy_blocksize) ||
     517 [ +  - ][ -  + ]:       5436 :         !unpack_uint(p, end, &compress_min) ||
     518         [ +  - ]:       1812 :         !unpack_string(p, end, fl_serialised)) return false;
     519                 :       1812 :     offset = uoffset;
     520                 :       1812 :     root = uoffset + uroot;
     521                 :            :     // Not meaningful, but still there so that existing honey databases
     522                 :            :     // continue to work.
     523                 :            :     (void)dummy_val;
     524                 :            :     (void)dummy_blocksize;
     525                 :       1812 :     return true;
     526                 :            : }
     527                 :            : 
     528                 :            : }

Generated by: LCOV version 1.11