LCOV - code coverage report
Current view: top level - lib/dbwrap - dbwrap_rbt.c (source / functions) Hit Total Coverage
Test: coverage report for abartlet/fix-coverage dd10fb34 Lines: 206 251 82.1 %
Date: 2021-09-23 10:06:22 Functions: 15 19 78.9 %

          Line data    Source code
       1             : /*
       2             :    Unix SMB/CIFS implementation.
       3             :    Database interface wrapper around red-black trees
       4             :    Copyright (C) Volker Lendecke 2007, 2008
       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 3 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, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "dbwrap/dbwrap.h"
      22             : #include "dbwrap/dbwrap_private.h"
      23             : #include "dbwrap/dbwrap_rbt.h"
      24             : #include "../lib/util/rbtree.h"
      25             : #include "../lib/util/dlinklist.h"
      26             : 
      27             : #define DBWRAP_RBT_ALIGN(_size_) (((_size_)+15)&~15)
      28             : 
      29             : struct db_rbt_ctx {
      30             :         struct rb_root tree;
      31             :         struct db_rbt_node *nodes;
      32             :         size_t traverse_read;
      33             :         struct db_rbt_node **traverse_nextp;
      34             : };
      35             : 
      36             : struct db_rbt_rec {
      37             :         struct db_rbt_node *node;
      38             : };
      39             : 
      40             : /* The structure that ends up in the tree */
      41             : 
      42             : struct db_rbt_node {
      43             :         struct rb_node rb_node;
      44             :         size_t keysize, valuesize;
      45             :         struct db_rbt_node *prev, *next;
      46             : };
      47             : 
      48             : /*
      49             :  * Hide the ugly pointer calculations in a function
      50             :  */
      51             : 
      52    51698293 : static struct db_rbt_node *db_rbt2node(struct rb_node *node)
      53             : {
      54    51698293 :         return (struct db_rbt_node *)
      55             :                 ((char *)node - offsetof(struct db_rbt_node, rb_node));
      56             : }
      57             : 
      58             : /*
      59             :  * Compare two keys
      60             :  */
      61             : 
      62    51810547 : static int db_rbt_compare(TDB_DATA a, TDB_DATA b)
      63             : {
      64             :         int res;
      65             : 
      66    51810547 :         res = memcmp(a.dptr, b.dptr, MIN(a.dsize, b.dsize));
      67             : 
      68    51810547 :         if ((res < 0) || ((res == 0) && (a.dsize < b.dsize))) {
      69    18909654 :                 return -1;
      70             :         }
      71    32876153 :         if ((res > 0) || ((res == 0) && (a.dsize > b.dsize))) {
      72    22566129 :                 return 1;
      73             :         }
      74    10283210 :         return 0;
      75             : }
      76             : 
      77             : /*
      78             :  * dissect a db_rbt_node into its implicit key and value parts
      79             :  */
      80             : 
      81    54298369 : static void db_rbt_parse_node(struct db_rbt_node *node,
      82             :                               TDB_DATA *key, TDB_DATA *value)
      83             : {
      84             :         size_t key_offset, value_offset;
      85             : 
      86    54419682 :         key_offset = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
      87    54419682 :         key->dptr = ((uint8_t *)node) + key_offset;
      88    54419682 :         key->dsize = node->keysize;
      89             : 
      90    54419682 :         value_offset = DBWRAP_RBT_ALIGN(node->keysize);
      91    54419682 :         value->dptr = key->dptr + value_offset;
      92    54419682 :         value->dsize = node->valuesize;
      93    54298369 : }
      94             : 
      95     2524691 : static ssize_t db_rbt_reclen(size_t keylen, size_t valuelen)
      96             : {
      97             :         size_t len, tmp;
      98             : 
      99     2529692 :         len = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
     100             : 
     101     2529692 :         tmp = DBWRAP_RBT_ALIGN(keylen);
     102     2529692 :         if (tmp < keylen) {
     103           0 :                 goto overflow;
     104             :         }
     105             : 
     106     2529692 :         len += tmp;
     107     2529692 :         if (len < tmp) {
     108           0 :                 goto overflow;
     109             :         }
     110             : 
     111     2529692 :         len += valuelen;
     112     2529692 :         if (len < valuelen) {
     113           0 :                 goto overflow;
     114             :         }
     115             : 
     116     2529692 :         return len;
     117        5001 : overflow:
     118           0 :         return -1;
     119             : }
     120             : 
     121     2564561 : static NTSTATUS db_rbt_storev(struct db_record *rec,
     122             :                               const TDB_DATA *dbufs, int num_dbufs, int flag)
     123             : {
     124     2564561 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     125             :                 rec->db->private_data, struct db_rbt_ctx);
     126     2564561 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     127             :         struct db_rbt_node *node;
     128             : 
     129             :         struct rb_node ** p;
     130     2564561 :         struct rb_node *parent = NULL;
     131     2564561 :         struct db_rbt_node *parent_node = NULL;
     132             : 
     133             :         ssize_t reclen;
     134             :         TDB_DATA data, this_key, this_val;
     135     2564561 :         void *to_free = NULL;
     136             : 
     137     2564561 :         if (db_ctx->traverse_read > 0) {
     138           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     139             :         }
     140             : 
     141     2564561 :         if ((flag == TDB_INSERT) && (rec_priv->node != NULL)) {
     142           1 :                 return NT_STATUS_OBJECT_NAME_COLLISION;
     143             :         }
     144             : 
     145     2564560 :         if ((flag == TDB_MODIFY) && (rec_priv->node == NULL)) {
     146           1 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     147             :         }
     148             : 
     149     2564559 :         if (num_dbufs == 1) {
     150     2564559 :                 data = dbufs[0];
     151             :         } else {
     152           0 :                 data = dbwrap_merge_dbufs(rec, dbufs, num_dbufs);
     153           0 :                 if (data.dptr == NULL) {
     154           0 :                         return NT_STATUS_NO_MEMORY;
     155             :                 }
     156           0 :                 to_free = data.dptr;
     157             :         }
     158             : 
     159     2564559 :         if (rec_priv->node != NULL) {
     160             : 
     161             :                 /*
     162             :                  * The record was around previously
     163             :                  */
     164             : 
     165       36169 :                 db_rbt_parse_node(rec_priv->node, &this_key, &this_val);
     166             : 
     167       35144 :                 SMB_ASSERT(this_key.dsize == rec->key.dsize);
     168       35144 :                 SMB_ASSERT(memcmp(this_key.dptr, rec->key.dptr,
     169             :                                   this_key.dsize) == 0);
     170             : 
     171       35144 :                 if (this_val.dsize >= data.dsize) {
     172             :                         /*
     173             :                          * The new value fits into the old space
     174             :                          */
     175       35615 :                         memcpy(this_val.dptr, data.dptr, data.dsize);
     176       34867 :                         rec_priv->node->valuesize = data.dsize;
     177       34867 :                         TALLOC_FREE(to_free);
     178       34867 :                         return NT_STATUS_OK;
     179             :                 }
     180             :         }
     181             : 
     182     2534693 :         reclen = db_rbt_reclen(rec->key.dsize, data.dsize);
     183     2529692 :         if (reclen == -1) {
     184           0 :                 TALLOC_FREE(to_free);
     185           0 :                 return NT_STATUS_INSUFFICIENT_RESOURCES;
     186             :         }
     187             : 
     188     2529692 :         node = talloc_zero_size(db_ctx, reclen);
     189     2529692 :         if (node == NULL) {
     190           0 :                 TALLOC_FREE(to_free);
     191           0 :                 return NT_STATUS_NO_MEMORY;
     192             :         }
     193             : 
     194     2529692 :         if (rec_priv->node != NULL) {
     195         277 :                 if (db_ctx->traverse_nextp != NULL) {
     196           0 :                         if (*db_ctx->traverse_nextp == rec_priv->node) {
     197           0 :                                 *db_ctx->traverse_nextp = node;
     198             :                         }
     199             :                 }
     200             : 
     201             :                 /*
     202             :                  * We need to delete the key from the tree and start fresh,
     203             :                  * there's not enough space in the existing record
     204             :                  */
     205             : 
     206         277 :                 rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     207         277 :                 DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     208             : 
     209             :                 /*
     210             :                  * Keep the existing node around for a while: If the record
     211             :                  * existed before, we reference the key data in there.
     212             :                  */
     213             :         }
     214             : 
     215     2529692 :         node->keysize = rec->key.dsize;
     216     2529692 :         node->valuesize = data.dsize;
     217             : 
     218     2529692 :         db_rbt_parse_node(node, &this_key, &this_val);
     219             : 
     220     2534693 :         memcpy(this_key.dptr, rec->key.dptr, node->keysize);
     221     2529692 :         TALLOC_FREE(rec_priv->node);
     222     2529692 :         rec_priv->node = node;
     223             : 
     224     2529692 :         if (node->valuesize > 0) {
     225     2135030 :                 memcpy(this_val.dptr, data.dptr, node->valuesize);
     226             :         }
     227             : 
     228     2529692 :         parent = NULL;
     229     2529692 :         p = &db_ctx->tree.rb_node;
     230             : 
     231    13535044 :         while (*p) {
     232             :                 struct db_rbt_node *r;
     233             :                 TDB_DATA search_key, search_val;
     234             :                 int res;
     235             : 
     236     8858857 :                 r = db_rbt2node(*p);
     237             : 
     238     8858857 :                 parent = (*p);
     239     8858857 :                 parent_node = r;
     240             : 
     241     8858857 :                 db_rbt_parse_node(r, &search_key, &search_val);
     242             : 
     243     8858857 :                 res = db_rbt_compare(this_key, search_key);
     244             : 
     245     8858857 :                 if (res == -1) {
     246     3637504 :                         p = &(*p)->rb_left;
     247             :                 }
     248     5221353 :                 else if (res == 1) {
     249     5221353 :                         p = &(*p)->rb_right;
     250             :                 }
     251             :                 else {
     252           0 :                         smb_panic("someone messed with the tree");
     253             :                 }
     254             :         }
     255             : 
     256     2529692 :         rb_link_node(&node->rb_node, parent, p);
     257     2529692 :         DLIST_ADD_AFTER(db_ctx->nodes, node, parent_node);
     258     2529692 :         rb_insert_color(&node->rb_node, &db_ctx->tree);
     259             : 
     260     2529692 :         TALLOC_FREE(to_free);
     261             : 
     262     2529692 :         return NT_STATUS_OK;
     263             : }
     264             : 
     265      715994 : static NTSTATUS db_rbt_delete(struct db_record *rec)
     266             : {
     267      715994 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     268             :                 rec->db->private_data, struct db_rbt_ctx);
     269      715994 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     270             : 
     271      715994 :         if (db_ctx->traverse_read > 0) {
     272           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     273             :         }
     274             : 
     275      715994 :         if (rec_priv->node == NULL) {
     276        4180 :                 return NT_STATUS_OK;
     277             :         }
     278             : 
     279      711814 :         if (db_ctx->traverse_nextp != NULL) {
     280       40357 :                 if (*db_ctx->traverse_nextp == rec_priv->node) {
     281           0 :                         *db_ctx->traverse_nextp = rec_priv->node->next;
     282             :                 }
     283             :         }
     284             : 
     285      711814 :         rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     286      711814 :         DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     287      711814 :         TALLOC_FREE(rec_priv->node);
     288             : 
     289      711814 :         return NT_STATUS_OK;
     290             : }
     291             : 
     292             : struct db_rbt_search_result {
     293             :         TDB_DATA key;
     294             :         TDB_DATA val;
     295             :         struct db_rbt_node* node;
     296             : };
     297             : 
     298    14349615 : static bool db_rbt_search_internal(struct db_context *db, TDB_DATA key,
     299             :                                    struct db_rbt_search_result *result)
     300             : {
     301    14349615 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     302             :                 db->private_data, struct db_rbt_ctx);
     303             : 
     304             :         struct rb_node *n;
     305    14349615 :         bool found = false;
     306    14349615 :         struct db_rbt_node *r = NULL;
     307    14349615 :         TDB_DATA search_key = { 0 };
     308    14349615 :         TDB_DATA search_val = { 0 };
     309             : 
     310    14349615 :         n = ctx->tree.rb_node;
     311             : 
     312    59714761 :         while (n != NULL) {
     313             :                 int res;
     314             : 
     315    42951690 :                 r = db_rbt2node(n);
     316             : 
     317    42951690 :                 db_rbt_parse_node(r, &search_key, &search_val);
     318             : 
     319    42951690 :                 res = db_rbt_compare(key, search_key);
     320             : 
     321    42951690 :                 if (res == -1) {
     322    15296890 :                         n = n->rb_left;
     323             :                 }
     324    27654800 :                 else if (res == 1) {
     325    17371590 :                         n = n->rb_right;
     326             :                 }
     327             :                 else {
     328    10222510 :                         found = true;
     329    10222510 :                         break;
     330             :                 }
     331             :         }
     332    14349615 :         if (result != NULL) {
     333    13562408 :                 if (found) {
     334     9891704 :                         result->key = search_key;
     335     9891704 :                         result->val = search_val;
     336     9891704 :                         result->node = r;
     337             :                 } else {
     338     3670704 :                         ZERO_STRUCT(*result);
     339             :                 }
     340             :         }
     341    14349615 :         return found;
     342             : }
     343             : 
     344     3242517 : static struct db_record *db_rbt_fetch_locked(struct db_context *db_ctx,
     345             :                                              TALLOC_CTX *mem_ctx,
     346             :                                              TDB_DATA key)
     347             : {
     348             :         struct db_rbt_rec *rec_priv;
     349             :         struct db_record *result;
     350             :         size_t size;
     351             :         bool found;
     352             :         struct db_rbt_search_result res;
     353             : 
     354     3242517 :         found = db_rbt_search_internal(db_ctx, key, &res);
     355             : 
     356             :         /*
     357             :          * In this low-level routine, play tricks to reduce the number of
     358             :          * tallocs to one. Not recommened for general use, but here it pays
     359             :          * off.
     360             :          */
     361             : 
     362     3242517 :         size = DBWRAP_RBT_ALIGN(sizeof(struct db_record))
     363             :                 + sizeof(struct db_rbt_rec);
     364             : 
     365     3242517 :         if (!found) {
     366             :                 /*
     367             :                  * We need to keep the key around for later store
     368             :                  */
     369     2533595 :                 size += key.dsize;
     370             :         }
     371             : 
     372     3242517 :         result = (struct db_record *)talloc_size(mem_ctx, size);
     373     3242517 :         if (result == NULL) {
     374           0 :                 return NULL;
     375             :         }
     376             : 
     377     3242517 :         rec_priv = (struct db_rbt_rec *)
     378             :                 ((char *)result + DBWRAP_RBT_ALIGN(sizeof(struct db_record)));
     379             : 
     380     3242517 :         result->storev = db_rbt_storev;
     381     3242517 :         result->delete_rec = db_rbt_delete;
     382     3242517 :         result->private_data = rec_priv;
     383             : 
     384     3242517 :         rec_priv->node = res.node;
     385     3242517 :         result->value  = res.val;
     386     3242517 :         result->value_valid = true;
     387             : 
     388     3242517 :         if (found) {
     389      708922 :                 result->key = res.key;
     390             :         }
     391             :         else {
     392     2533595 :                 result->key.dptr = (uint8_t *)
     393             :                         ((char *)rec_priv + sizeof(*rec_priv));
     394     2533595 :                 result->key.dsize = key.dsize;
     395     2533595 :                 memcpy(result->key.dptr, key.dptr, key.dsize);
     396             :         }
     397             : 
     398     3232785 :         return result;
     399             : }
     400             : 
     401      787207 : static int db_rbt_exists(struct db_context *db, TDB_DATA key)
     402             : {
     403      787207 :         return db_rbt_search_internal(db, key, NULL);
     404             : }
     405             : 
     406           0 : static int db_rbt_wipe(struct db_context *db)
     407             : {
     408           0 :         struct db_rbt_ctx *old_ctx = talloc_get_type_abort(
     409             :                 db->private_data, struct db_rbt_ctx);
     410           0 :         struct db_rbt_ctx *new_ctx = talloc_zero(db, struct db_rbt_ctx);
     411           0 :         if (new_ctx == NULL) {
     412           0 :                 return -1;
     413             :         }
     414           0 :         db->private_data = new_ctx;
     415           0 :         talloc_free(old_ctx);
     416           0 :         return 0;
     417             : }
     418             : 
     419    10319891 : static NTSTATUS db_rbt_parse_record(struct db_context *db, TDB_DATA key,
     420             :                                     void (*parser)(TDB_DATA key, TDB_DATA data,
     421             :                                                    void *private_data),
     422             :                                     void *private_data)
     423             : {
     424             :         struct db_rbt_search_result res;
     425    10319891 :         bool found = db_rbt_search_internal(db, key, &res);
     426             : 
     427    10319891 :         if (!found) {
     428     1137109 :                 return NT_STATUS_NOT_FOUND;
     429             :         }
     430     9182782 :         parser(res.key, res.val, private_data);
     431     9182782 :         return NT_STATUS_OK;
     432             : }
     433             : 
     434       59836 : static int db_rbt_traverse_internal(struct db_context *db,
     435             :                                     int (*f)(struct db_record *db,
     436             :                                              void *private_data),
     437             :                                     void *private_data, uint32_t* count,
     438             :                                     bool rw)
     439             : {
     440       59836 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     441             :                 db->private_data, struct db_rbt_ctx);
     442       59836 :         struct db_rbt_node *cur = NULL;
     443       59836 :         struct db_rbt_node *next = NULL;
     444             :         int ret;
     445             : 
     446      104135 :         for (cur = ctx->nodes; cur != NULL; cur = next) {
     447             :                 struct db_record rec;
     448             :                 struct db_rbt_rec rec_priv;
     449             : 
     450       44299 :                 rec_priv.node = cur;
     451       44299 :                 next = rec_priv.node->next;
     452             : 
     453       44299 :                 ZERO_STRUCT(rec);
     454       44299 :                 rec.db = db;
     455       44299 :                 rec.private_data = &rec_priv;
     456       44299 :                 rec.storev = db_rbt_storev;
     457       44299 :                 rec.delete_rec = db_rbt_delete;
     458       47332 :                 db_rbt_parse_node(rec_priv.node, &rec.key, &rec.value);
     459       44299 :                 rec.value_valid = true;
     460             : 
     461       44299 :                 if (rw) {
     462       43299 :                         ctx->traverse_nextp = &next;
     463             :                 }
     464       44299 :                 ret = f(&rec, private_data);
     465       44299 :                 (*count) ++;
     466       44299 :                 if (rw) {
     467       43299 :                         ctx->traverse_nextp = NULL;
     468             :                 }
     469       44299 :                 if (ret != 0) {
     470           0 :                         return ret;
     471             :                 }
     472       44299 :                 if (rec_priv.node != NULL) {
     473        3942 :                         next = rec_priv.node->next;
     474             :                 }
     475             :         }
     476             : 
     477       58789 :         return 0;
     478             : }
     479             : 
     480           2 : static int db_rbt_traverse_read(struct db_context *db,
     481             :                                 int (*f)(struct db_record *db,
     482             :                                          void *private_data),
     483             :                                 void *private_data)
     484             : {
     485           2 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     486             :                 db->private_data, struct db_rbt_ctx);
     487           2 :         uint32_t count = 0;
     488             :         int ret;
     489             : 
     490           2 :         ctx->traverse_read++;
     491           2 :         ret = db_rbt_traverse_internal(db,
     492             :                                        f, private_data, &count,
     493             :                                        false /* rw */);
     494           2 :         ctx->traverse_read--;
     495           2 :         if (ret != 0) {
     496           0 :                 return -1;
     497             :         }
     498           2 :         if (count > INT_MAX) {
     499           0 :                 return -1;
     500             :         }
     501           2 :         return count;
     502             : }
     503             : 
     504       59834 : static int db_rbt_traverse(struct db_context *db,
     505             :                            int (*f)(struct db_record *db,
     506             :                                     void *private_data),
     507             :                            void *private_data)
     508             : {
     509       59834 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     510             :                 db->private_data, struct db_rbt_ctx);
     511       59834 :         uint32_t count = 0;
     512             :         int ret;
     513             : 
     514       59834 :         if (ctx->traverse_nextp != NULL) {
     515           0 :                 return -1;
     516             :         };
     517             : 
     518       59834 :         if (ctx->traverse_read > 0) {
     519           0 :                 return db_rbt_traverse_read(db, f, private_data);
     520             :         }
     521             : 
     522       59834 :         ret = db_rbt_traverse_internal(db,
     523             :                                        f, private_data, &count,
     524             :                                        true /* rw */);
     525       59834 :         if (ret != 0) {
     526           0 :                 return -1;
     527             :         }
     528       59834 :         if (count > INT_MAX) {
     529           0 :                 return -1;
     530             :         }
     531       59834 :         return count;
     532             : }
     533             : 
     534           0 : static int db_rbt_get_seqnum(struct db_context *db)
     535             : {
     536           0 :         return 0;
     537             : }
     538             : 
     539           0 : static int db_rbt_trans_dummy(struct db_context *db)
     540             : {
     541             :         /*
     542             :          * Transactions are pretty pointless in-memory, just return success.
     543             :          */
     544           0 :         return 0;
     545             : }
     546             : 
     547           0 : static size_t db_rbt_id(struct db_context *db, uint8_t *id, size_t idlen)
     548             : {
     549           0 :         if (idlen >= sizeof(struct db_context *)) {
     550           0 :                 memcpy(id, &db, sizeof(struct db_context *));
     551             :         }
     552           0 :         return sizeof(struct db_context *);
     553             : }
     554             : 
     555     2981209 : struct db_context *db_open_rbt(TALLOC_CTX *mem_ctx)
     556             : {
     557             :         struct db_context *result;
     558             : 
     559     2981209 :         result = talloc_zero(mem_ctx, struct db_context);
     560             : 
     561     2981209 :         if (result == NULL) {
     562           0 :                 return NULL;
     563             :         }
     564             : 
     565     2981209 :         result->private_data = talloc_zero(result, struct db_rbt_ctx);
     566             : 
     567     2981209 :         if (result->private_data == NULL) {
     568           0 :                 TALLOC_FREE(result);
     569           0 :                 return NULL;
     570             :         }
     571             : 
     572     2981209 :         result->fetch_locked = db_rbt_fetch_locked;
     573     2981209 :         result->traverse = db_rbt_traverse;
     574     2981209 :         result->traverse_read = db_rbt_traverse_read;
     575     2981209 :         result->get_seqnum = db_rbt_get_seqnum;
     576     2981209 :         result->transaction_start = db_rbt_trans_dummy;
     577     2981209 :         result->transaction_commit = db_rbt_trans_dummy;
     578     2981209 :         result->transaction_cancel = db_rbt_trans_dummy;
     579     2981209 :         result->exists = db_rbt_exists;
     580     2981209 :         result->wipe = db_rbt_wipe;
     581     2981209 :         result->parse_record = db_rbt_parse_record;
     582     2981209 :         result->id = db_rbt_id;
     583     2981209 :         result->name = "dbwrap rbt";
     584             : 
     585     2981209 :         return result;
     586             : }

Generated by: LCOV version 1.13