LCOV - code coverage report
Current view: top level - backends/honey - honey_table.cc (source / functions) Hit Total Coverage
Test: Test Coverage for xapian-core 4ba52dacf4fb Lines: 129 224 57.6 %
Date: 2019-05-20 14:58:19 Functions: 8 8 100.0 %
Branches: 103 337 30.6 %

           Branch data     Line data    Source code
       1                 :            : /** @file honey_table.cc
       2                 :            :  * @brief HoneyTable class
       3                 :            :  */
       4                 :            : /* Copyright (C) 2017,2018 Olly Betts
       5                 :            :  *
       6                 :            :  * This program is free software; you can redistribute it and/or modify
       7                 :            :  * it under the terms of the GNU General Public License as published by
       8                 :            :  * the Free Software Foundation; either version 2 of the License, or
       9                 :            :  * (at your option) any later version.
      10                 :            :  *
      11                 :            :  * This program is distributed in the hope that it will be useful,
      12                 :            :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13                 :            :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14                 :            :  * GNU General Public License for more details.
      15                 :            :  *
      16                 :            :  * You should have received a copy of the GNU General Public License
      17                 :            :  * along with this program; if not, write to the Free Software
      18                 :            :  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
      19                 :            :  */
      20                 :            : 
      21                 :            : #include <config.h>
      22                 :            : 
      23                 :            : //#define DEBUGGING
      24                 :            : 
      25                 :            : #include "honey_table.h"
      26                 :            : 
      27                 :            : #include "honey_cursor.h"
      28                 :            : #include "stringutils.h"
      29                 :            : 
      30                 :            : #include "unicode/description_append.h"
      31                 :            : 
      32                 :            : #include <cerrno>
      33                 :            : 
      34                 :            : #ifdef DEBUGGING
      35                 :            : # include <iostream>
      36                 :            : #endif
      37                 :            : 
      38                 :            : using Honey::RootInfo;
      39                 :            : 
      40                 :            : using namespace std;
      41                 :            : 
      42                 :            : void
      43                 :       1172 : HoneyTable::create_and_open(int flags_, const RootInfo& root_info)
      44                 :            : {
      45                 :            :     Assert(!single_file());
      46                 :       1172 :     flags = flags_;
      47                 :       1172 :     compress_min = root_info.get_compress_min();
      48         [ -  + ]:       1172 :     if (read_only) {
      49                 :          0 :         num_entries = root_info.get_num_entries();
      50                 :          0 :         root = root_info.get_root();
      51                 :            :         // FIXME: levels
      52                 :            :     }
      53         [ -  + ]:       1172 :     if (!store.open(path, read_only))
      54 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseOpeningError("Failed to open HoneyTable", errno);
      55                 :       1172 : }
      56                 :            : 
      57                 :            : void
      58                 :       1838 : HoneyTable::open(int flags_, const RootInfo& root_info, honey_revision_number_t)
      59                 :            : {
      60                 :       1838 :     flags = flags_;
      61                 :       1838 :     compress_min = root_info.get_compress_min();
      62                 :       1838 :     num_entries = root_info.get_num_entries();
      63                 :       1838 :     offset = root_info.get_offset();
      64                 :       1838 :     root = root_info.get_root();
      65 [ +  + ][ +  + ]:       1838 :     if (!single_file() && !store.open(path, read_only)) {
                 [ +  + ]
      66         [ -  + ]:        624 :         if (!lazy)
      67                 :            :             throw Xapian::DatabaseOpeningError("Failed to open HoneyTable",
      68 [ #  # ][ #  # ]:          0 :                                                errno);
      69                 :            :     }
      70                 :       1838 :     store.set_pos(offset);
      71                 :       1838 : }
      72                 :            : 
      73                 :            : void
      74                 :    1041058 : HoneyTable::add(const std::string& key,
      75                 :            :                 const char* val,
      76                 :            :                 size_t val_size,
      77                 :            :                 bool compressed)
      78                 :            : {
      79         [ -  + ]:    1041058 :     if (rare(val_size == 0))
      80 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("HoneyTable::add() passed empty value");
                 [ #  # ]
      81         [ -  + ]:    1041058 :     if (store.was_forced_closed())
      82         [ #  # ]:          0 :         throw_database_closed();
      83 [ +  + ][ +  + ]:    1041058 :     if (!compressed && compress_min > 0 && val_size > compress_min) {
                 [ +  + ]
      84                 :      16297 :         size_t compressed_size = val_size;
      85                 :      16297 :         CompressionStream comp_stream; // FIXME: reuse
      86         [ +  - ]:      16297 :         const char* p = comp_stream.compress(val, &compressed_size);
      87         [ +  + ]:      16297 :         if (p) {
      88         [ +  - ]:       9361 :             add(key, p, compressed_size, true);
      89         [ +  + ]:    1057355 :             return;
      90                 :      16297 :         }
      91                 :            :     }
      92                 :            : 
      93         [ -  + ]:    1031697 :     if (read_only)
      94 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidOperationError("add() on read-only HoneyTable");
                 [ #  # ]
      95 [ +  - ][ -  + ]:    1031697 :     if (key.size() == 0 || key.size() > HONEY_MAX_KEY_LENGTH)
                 [ -  + ]
      96 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidArgumentError("Invalid key size: " +
      97 [ #  # ][ #  # ]:          0 :                                            str(key.size()));
      98 [ +  - ][ -  + ]:    1031697 :     if (key <= last_key)
      99 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidOperationError("New key <= previous key");
                 [ #  # ]
     100         [ +  - ]:    1031697 :     size_t reuse = common_prefix_length(last_key, key);
     101                 :            : 
     102                 :            : #ifdef SSTINDEX_ARRAY
     103         [ +  + ]:    1031697 :     if (reuse == 0) {
     104         [ +  - ]:      12530 :         index.maybe_add_entry(key, store.get_pos());
     105                 :            :     }
     106                 :            : #elif defined SSTINDEX_BINARY_CHOP
     107                 :            :     // For a binary chop index, the index point is before the key info - the
     108                 :            :     // index key must have the same N first bytes as the previous key, where
     109                 :            :     // N >= the keep length.
     110                 :            :     index.maybe_add_entry(key, store.get_pos());
     111                 :            : #elif defined SSTINDEX_SKIPLIST
     112                 :            :     // Handled below.
     113                 :            : #else
     114                 :            : # error "SSTINDEX type not specified"
     115                 :            : #endif
     116                 :            : 
     117         [ +  - ]:    1031697 :     store.write(static_cast<unsigned char>(reuse));
     118         [ +  - ]:    1031697 :     store.write(static_cast<unsigned char>(key.size() - reuse));
     119         [ +  - ]:    1031697 :     store.write(key.data() + reuse, key.size() - reuse);
     120                 :    1031697 :     ++num_entries;
     121                 :            : 
     122                 :            : #ifdef SSTINDEX_SKIPLIST
     123                 :            :     // For a skiplist index, the index provides the full key, so the index
     124                 :            :     // point is after the key at the level below.
     125                 :            :     index.maybe_add_entry(key, store.get_pos());
     126                 :            : #endif
     127                 :            : 
     128                 :            :     // Encode "compressed?" flag in bottom bit.
     129                 :            :     // FIXME: Don't do this if a table is uncompressed?  That saves a byte
     130                 :            :     // for each item where the extra bit pushes the length up by a byte.
     131                 :    1031697 :     size_t val_size_enc = (val_size << 1) | compressed;
     132         [ +  - ]:    1031697 :     std::string val_len;
     133         [ +  - ]:    1031697 :     pack_uint(val_len, val_size_enc);
     134                 :            :     // FIXME: pass together so we can potentially writev() both?
     135         [ +  - ]:    1031697 :     store.write(val_len.data(), val_len.size());
     136         [ +  - ]:    1031697 :     store.write(val, val_size);
     137         [ +  - ]:    1031697 :     last_key = key;
     138                 :            : }
     139                 :            : 
     140                 :            : void
     141                 :       1180 : HoneyTable::commit(honey_revision_number_t, RootInfo* root_info)
     142                 :            : {
     143         [ -  + ]:       1180 :     if (root < 0)
     144 [ #  # ][ #  # ]:          0 :         throw Xapian::InvalidOperationError("root not set");
                 [ #  # ]
     145                 :            : 
     146                 :       1180 :     root_info->set_num_entries(num_entries);
     147                 :            :     // offset should already be set.
     148                 :       1180 :     root_info->set_root(root);
     149                 :            :     // Not really meaningful.
     150                 :            :     // root_info->set_free_list(std::string());
     151                 :            : 
     152                 :       1180 :     read_only = true;
     153                 :       1180 :     store.rewind(offset);
     154         [ +  - ]:       1180 :     last_key = string();
     155                 :       1180 : }
     156                 :            : 
     157                 :            : bool
     158                 :   17593945 : HoneyTable::read_key(std::string& key,
     159                 :            :                      size_t& val_size,
     160                 :            :                      bool& compressed) const
     161                 :            : {
     162                 :            : #ifdef DEBUGGING
     163                 :            :     {
     164                 :            :         string desc;
     165                 :            :         description_append(desc, key);
     166                 :            :         cerr << "HoneyTable::read_key(" << desc << ", ...) for path=" << path << endl;
     167                 :            :     }
     168                 :            : #endif
     169         [ -  + ]:   17593945 :     if (!read_only) {
     170                 :          0 :         return false;
     171                 :            :     }
     172                 :            : 
     173                 :            :     AssertRel(store.get_pos(), >=, offset);
     174         [ +  + ]:   17593945 :     if (store.get_pos() >= root) {
     175                 :            :         AssertEq(store.get_pos(), root);
     176                 :          5 :         return false;
     177                 :            :     }
     178         [ +  - ]:   17593940 :     int ch = store.read();
     179         [ -  + ]:   17593940 :     if (ch == EOF) return false;
     180                 :            : 
     181                 :   17593940 :     size_t reuse = ch;
     182         [ -  + ]:   17593940 :     if (reuse > last_key.size()) {
     183 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseCorruptError("Reuse > previous key size");
                 [ #  # ]
     184                 :            :     }
     185         [ +  - ]:   17593940 :     ch = store.read();
     186         [ -  + ]:   17593940 :     if (ch == EOF) {
     187                 :            :         throw Xapian::DatabaseError("EOF/error while reading key length",
     188 [ #  # ][ #  # ]:          0 :                                     errno);
     189                 :            :     }
     190                 :   17593940 :     size_t key_size = ch;
     191                 :            :     char buf[256];
     192         [ +  - ]:   17593940 :     store.read(buf, key_size);
     193         [ +  - ]:   17593940 :     key.assign(last_key, 0, reuse);
     194         [ +  - ]:   17593940 :     key.append(buf, key_size);
     195         [ +  - ]:   17593940 :     last_key = key;
     196                 :            : 
     197                 :            : #ifdef DEBUGGING
     198                 :            :     if (false) {
     199                 :            :         std::string esc;
     200                 :            :         description_append(esc, key);
     201                 :            :         std::cout << "K:" << esc << std::endl;
     202                 :            :     }
     203                 :            : #endif
     204                 :            : 
     205                 :            :     int r;
     206                 :            :     {
     207                 :            :         // FIXME: rework to take advantage of buffering that's happening anyway?
     208                 :   17593940 :         char * p = buf;
     209         [ +  - ]:   18344475 :         for (int i = 0; i < 8; ++i) {
     210         [ +  - ]:   18344475 :             int ch2 = store.read();
     211         [ -  + ]:   18344475 :             if (ch2 == EOF) {
     212                 :          0 :                 break;
     213                 :            :             }
     214                 :   18344475 :             *p++ = char(ch2);
     215         [ +  + ]:   18344475 :             if (ch2 < 128) break;
     216                 :            :         }
     217                 :   17593940 :         r = p - buf;
     218                 :            :     }
     219                 :   17593940 :     const char* p = buf;
     220                 :   17593940 :     const char* end = p + r;
     221         [ -  + ]:   17593940 :     if (!unpack_uint(&p, end, &val_size)) {
     222 [ #  # ][ #  # ]:          0 :         throw Xapian::DatabaseError("val_size unpack_uint invalid");
                 [ #  # ]
     223                 :            :     }
     224                 :   17593940 :     compressed = val_size & 1;
     225                 :   17593940 :     val_size >>= 1;
     226                 :            :     Assert(p == end);
     227                 :   17593945 :     return true;
     228                 :            : }
     229                 :            : 
     230                 :            : void
     231                 :     115381 : HoneyTable::read_val(std::string& val, size_t val_size) const
     232                 :            : {
     233                 :            :     AssertRel(store.get_pos() + val_size, <=, size_t(root));
     234                 :     115381 :     val.resize(val_size);
     235                 :     115381 :     store.read(&(val[0]), val_size);
     236                 :            : #ifdef DEBUGGING
     237                 :            :     if (false) {
     238                 :            :         std::string esc;
     239                 :            :         description_append(esc, val);
     240                 :            :         std::cout << "V:" << esc << std::endl;
     241                 :            :     }
     242                 :            : #endif
     243                 :     115381 : }
     244                 :            : 
     245                 :            : bool
     246                 :     116228 : HoneyTable::get_exact_entry(const std::string& key, std::string* tag) const
     247                 :            : {
     248         [ -  + ]:     116228 :     if (!read_only) std::abort();
     249         [ +  + ]:     116228 :     if (rare(!store.is_open())) {
     250         [ +  - ]:         12 :         if (store.was_forced_closed())
     251         [ -  + ]:         12 :             throw_database_closed();
     252                 :          0 :         return false;
     253                 :            :     }
     254                 :     116216 :     store.rewind(root);
     255         [ -  + ]:     116216 :     if (rare(key.empty()))
     256                 :          0 :         return false;
     257                 :     116216 :     bool exact_match = false;
     258                 :     116216 :     bool compressed = false;
     259                 :     116216 :     size_t val_size = 0;
     260         [ +  - ]:     116216 :     int index_type = store.read();
     261   [ -  +  -  -  :     116216 :     switch (index_type) {
                      - ]
     262                 :            :         case EOF:
     263                 :          0 :             return false;
     264                 :            :         case 0x00: {
     265                 :            :             unsigned char first =
     266         [ +  - ]:     116216 :                 static_cast<unsigned char>(key[0] - store.read());
     267         [ +  - ]:     116216 :             unsigned char range = store.read();
     268         [ +  + ]:     116216 :             if (first > range)
     269                 :         23 :                 return false;
     270                 :     116193 :             store.skip(first * 4); // FIXME: pointer width
     271         [ +  - ]:     116193 :             off_t jump = store.read_uint4_be();
     272                 :     116193 :             store.rewind(jump);
     273                 :            :             // The jump point will be an entirely new key (because it is the
     274                 :            :             // first key with that initial character), and we drop in as if
     275                 :            :             // this was the first key so set last_key to be empty.
     276 [ +  - ][ +  - ]:     116193 :             last_key = string();
     277                 :     116193 :             break;
     278                 :            :         }
     279                 :            :         case 0x01: {
     280         [ #  # ]:          0 :             size_t j = store.read_uint4_be();
     281         [ #  # ]:          0 :             if (j == 0)
     282                 :          0 :                 return false;
     283                 :          0 :             off_t base = store.get_pos();
     284                 :            :             char kkey[SSTINDEX_BINARY_CHOP_KEY_SIZE];
     285                 :          0 :             size_t kkey_len = 0;
     286                 :          0 :             size_t i = 0;
     287         [ #  # ]:          0 :             while (j - i > 1) {
     288                 :          0 :                 size_t k = i + (j - i) / 2;
     289         [ #  # ]:          0 :                 store.set_pos(base + k * SSTINDEX_BINARY_CHOP_ENTRY_SIZE);
     290         [ #  # ]:          0 :                 store.read(kkey, SSTINDEX_BINARY_CHOP_KEY_SIZE);
     291                 :          0 :                 kkey_len = 4;
     292 [ #  # ][ #  # ]:          0 :                 while (kkey_len > 0 && kkey[kkey_len - 1] == '\0') --kkey_len;
     293         [ #  # ]:          0 :                 int r = key.compare(0, SSTINDEX_BINARY_CHOP_KEY_SIZE, kkey, kkey_len);
     294         [ #  # ]:          0 :                 if (r < 0) {
     295                 :          0 :                     j = k;
     296                 :            :                 } else {
     297                 :          0 :                     i = k;
     298         [ #  # ]:          0 :                     if (r == 0) {
     299                 :          0 :                         break;
     300                 :            :                     }
     301                 :            :                 }
     302                 :            :             }
     303         [ #  # ]:          0 :             store.set_pos(base + i * SSTINDEX_BINARY_CHOP_ENTRY_SIZE);
     304         [ #  # ]:          0 :             store.read(kkey, SSTINDEX_BINARY_CHOP_KEY_SIZE);
     305                 :          0 :             kkey_len = 4;
     306 [ #  # ][ #  # ]:          0 :             while (kkey_len > 0 && kkey[kkey_len - 1] == '\0') --kkey_len;
     307         [ #  # ]:          0 :             off_t jump = store.read_uint4_be();
     308                 :          0 :             store.rewind(jump);
     309                 :            :             // The jump point is to the first key with prefix kkey, so will
     310                 :            :             // work if we set last key to kkey.  Unless we're jumping to the
     311                 :            :             // start of the table, in which case last_key needs to be empty.
     312 [ #  # ][ #  # ]:          0 :             last_key.assign(kkey, jump == 0 ? 0 : kkey_len);
     313                 :          0 :             break;
     314                 :            :         }
     315                 :            :         case 0x02: {
     316                 :            :             // FIXME: If "close" just seek forwards?  Or consider seeking from
     317                 :            :             // current index pos?
     318                 :            :             // off_t pos = store.get_pos();
     319 [ #  # ][ #  # ]:          0 :             string index_key, prev_index_key;
     320                 :          0 :             make_unsigned<off_t>::type ptr = 0;
     321                 :          0 :             int cmp0 = 1;
     322                 :            :             while (true) {
     323         [ #  # ]:          0 :                 int reuse = store.read();
     324         [ #  # ]:          0 :                 if (reuse == EOF) break;
     325         [ #  # ]:          0 :                 int len = store.read();
     326         [ #  # ]:          0 :                 if (len == EOF) abort(); // FIXME
     327         [ #  # ]:          0 :                 index_key.resize(reuse + len);
     328 [ #  # ][ #  # ]:          0 :                 store.read(&index_key[reuse], len);
     329                 :            : 
     330                 :            : #ifdef DEBUGGING
     331                 :            :                 {
     332                 :            :                     string desc;
     333                 :            :                     description_append(desc, index_key);
     334                 :            :                     cerr << "Index key: " << desc << endl;
     335                 :            :                 }
     336                 :            : #endif
     337                 :            : 
     338         [ #  # ]:          0 :                 cmp0 = index_key.compare(key);
     339         [ #  # ]:          0 :                 if (cmp0 > 0) {
     340         [ #  # ]:          0 :                     index_key = prev_index_key;
     341                 :          0 :                     break;
     342                 :            :                 }
     343                 :            :                 char buf[8];
     344                 :          0 :                 char* e = buf;
     345                 :            :                 while (true) {
     346         [ #  # ]:          0 :                     int b = store.read();
     347                 :          0 :                     *e++ = b;
     348         [ #  # ]:          0 :                     if ((b & 0x80) == 0) break;
     349                 :            :                 }
     350                 :          0 :                 const char* p = buf;
     351 [ #  # ][ #  # ]:          0 :                 if (!unpack_uint(&p, e, &ptr) || p != e) abort(); // FIXME
                 [ #  # ]
     352                 :            : #ifdef DEBUGGING
     353                 :            :                 {
     354                 :            :                     cerr << " -> " << ptr << endl;
     355                 :            :                 }
     356                 :            : #endif
     357         [ #  # ]:          0 :                 if (cmp0 == 0)
     358                 :          0 :                     break;
     359         [ #  # ]:          0 :                 prev_index_key = index_key;
     360                 :          0 :             }
     361                 :            : #ifdef DEBUGGING
     362                 :            :             {
     363                 :            :                 cerr << " cmp0 = " << cmp0 << ", going to " << ptr << endl;
     364                 :            :             }
     365                 :            : #endif
     366         [ #  # ]:          0 :             store.set_pos(ptr);
     367                 :            : 
     368         [ #  # ]:          0 :             if (ptr != 0) {
     369         [ #  # ]:          0 :                 last_key = index_key;
     370                 :            :                 char buf[8];
     371                 :            :                 int r;
     372                 :            :                 {
     373                 :            :                     // FIXME: rework to take advantage of buffering that's happening anyway?
     374                 :          0 :                     char * p = buf;
     375         [ #  # ]:          0 :                     for (int i = 0; i < 8; ++i) {
     376         [ #  # ]:          0 :                         int ch2 = store.read();
     377         [ #  # ]:          0 :                         if (ch2 == EOF) {
     378                 :          0 :                             break;
     379                 :            :                         }
     380                 :          0 :                         *p++ = ch2;
     381         [ #  # ]:          0 :                         if (ch2 < 128) break;
     382                 :            :                     }
     383                 :          0 :                     r = p - buf;
     384                 :            :                 }
     385                 :          0 :                 const char* p = buf;
     386                 :          0 :                 const char* end = p + r;
     387         [ #  # ]:          0 :                 if (!unpack_uint(&p, end, &val_size)) {
     388 [ #  # ][ #  # ]:          0 :                     throw Xapian::DatabaseError("val_size unpack_uint invalid");
                 [ #  # ]
     389                 :            :                 }
     390                 :          0 :                 compressed = val_size & 1;
     391                 :          0 :                 val_size >>= 1;
     392                 :            :                 Assert(p == end);
     393                 :            :             } else {
     394 [ #  # ][ #  # ]:          0 :                 last_key = string();
     395                 :            :             }
     396                 :            : 
     397         [ #  # ]:          0 :             if (cmp0 == 0) {
     398                 :          0 :                 exact_match = true;
     399                 :          0 :                 break;
     400                 :            :             }
     401                 :            : 
     402                 :            : #ifdef DEBUGGING
     403                 :            :             {
     404                 :            :                 string desc;
     405                 :            :                 description_append(desc, last_key);
     406                 :            :                 cerr << "Dropped to data layer on key: " << desc << endl;
     407                 :            :             }
     408                 :            : #endif
     409                 :            : 
     410                 :          0 :             break;
     411                 :            :         }
     412                 :            :         default: {
     413         [ #  # ]:          0 :             string m = "HoneyTable: Unknown index type ";
     414 [ #  # ][ #  # ]:          0 :             m += str(index_type);
     415 [ #  # ][ #  # ]:          0 :             throw Xapian::DatabaseCorruptError(m);
     416                 :            :         }
     417                 :            :     }
     418                 :            : 
     419         [ +  - ]:     116193 :     std::string k;
     420                 :            :     int cmp;
     421         [ +  - ]:     116193 :     if (!exact_match) {
     422         [ +  + ]:   17593940 :         do {
     423         [ +  + ]:   17593945 :             if (val_size) {
     424                 :            :                 // Skip val data we've not looked at.
     425                 :   17477752 :                 store.skip(val_size);
     426                 :   17477752 :                 val_size = 0;
     427                 :            :             }
     428 [ +  - ][ +  + ]:   17593945 :             if (!read_key(k, val_size, compressed)) return false;
     429         [ +  - ]:   17593940 :             cmp = k.compare(key);
     430                 :            :         } while (cmp < 0);
     431         [ +  + ]:     116188 :         if (cmp > 0) return false;
     432                 :            :     }
     433         [ +  + ]:     115532 :     if (tag != NULL) {
     434         [ +  + ]:     115381 :         if (compressed) {
     435         [ +  - ]:       1758 :             std::string v;
     436         [ +  - ]:       1758 :             read_val(v, val_size);
     437                 :       3516 :             CompressionStream comp_stream;
     438         [ +  - ]:       1758 :             comp_stream.decompress_start();
     439         [ +  - ]:       1758 :             tag->resize(0);
     440 [ +  - ][ -  + ]:       1758 :             if (!comp_stream.decompress_chunk(v.data(), v.size(), *tag)) {
     441                 :            :                 // Decompression didn't complete.
     442                 :          0 :                 abort();
     443                 :       1758 :             }
     444                 :            :         } else {
     445         [ +  - ]:     115381 :             read_val(*tag, val_size);
     446                 :            :         }
     447                 :            :     }
     448                 :     116216 :     return true;
     449                 :            : }
     450                 :            : 
     451                 :            : HoneyCursor*
     452                 :      30346 : HoneyTable::cursor_get() const
     453                 :            : {
     454         [ +  + ]:      30346 :     if (rare(!store.is_open())) {
     455         [ +  - ]:          5 :         if (store.was_forced_closed())
     456                 :          5 :             throw_database_closed();
     457                 :          0 :         return NULL;
     458                 :            :     }
     459         [ +  - ]:      30341 :     return new HoneyCursor(this);
     460                 :            : }

Generated by: LCOV version 1.11