LCOV - code coverage report
Current view: top level - lib/dbwrap - dbwrap_rbt.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 230 278 82.7 %
Date: 2024-04-21 15:09:00 Functions: 14 18 77.8 %

          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   123349042 : static struct db_rbt_node *db_rbt2node(struct rb_node *node)
      53             : {
      54   123349042 :         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   124620192 : static int db_rbt_compare(TDB_DATA a, TDB_DATA b)
      63             : {
      64     1271150 :         int res;
      65             : 
      66   124620192 :         res = memcmp(a.dptr, b.dptr, MIN(a.dsize, b.dsize));
      67             : 
      68   124620192 :         if ((res < 0) || ((res == 0) && (a.dsize < b.dsize))) {
      69    44525756 :                 return -1;
      70             :         }
      71    79487591 :         if ((res > 0) || ((res == 0) && (a.dsize > b.dsize))) {
      72    51404446 :                 return 1;
      73             :         }
      74    28018190 :         return 0;
      75             : }
      76             : 
      77             : /*
      78             :  * dissect a db_rbt_node into its implicit key and value parts
      79             :  */
      80             : 
      81   128266347 : static void db_rbt_parse_node(struct db_rbt_node *node,
      82             :                               TDB_DATA *key, TDB_DATA *value)
      83             : {
      84     1319803 :         size_t key_offset, value_offset;
      85             : 
      86   128266347 :         key_offset = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
      87   128266347 :         key->dptr = ((uint8_t *)node) + key_offset;
      88   128266347 :         key->dsize = node->keysize;
      89             : 
      90   128266347 :         value_offset = DBWRAP_RBT_ALIGN(node->keysize);
      91   128266347 :         value->dptr = key->dptr + value_offset;
      92   128266347 :         value->dsize = node->valuesize;
      93   126946544 : }
      94             : 
      95     3557625 : static ssize_t db_rbt_reclen(size_t keylen, size_t valuelen)
      96             : {
      97       44081 :         size_t len, tmp;
      98             : 
      99     3557625 :         len = DBWRAP_RBT_ALIGN(sizeof(struct db_rbt_node));
     100             : 
     101     3557625 :         tmp = DBWRAP_RBT_ALIGN(keylen);
     102     3557625 :         if (tmp < keylen) {
     103           0 :                 goto overflow;
     104             :         }
     105             : 
     106     3557625 :         len += tmp;
     107     3557625 :         if (len < tmp) {
     108           0 :                 goto overflow;
     109             :         }
     110             : 
     111     3557625 :         len += valuelen;
     112     3557625 :         if (len < valuelen) {
     113           0 :                 goto overflow;
     114             :         }
     115             : 
     116     3557625 :         return len;
     117       44081 : overflow:
     118           0 :         return -1;
     119             : }
     120             : 
     121     3596100 : static NTSTATUS db_rbt_storev(struct db_record *rec,
     122             :                               const TDB_DATA *dbufs, int num_dbufs, int flag)
     123             : {
     124     3596100 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     125             :                 rec->db->private_data, struct db_rbt_ctx);
     126     3596100 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     127       44847 :         struct db_rbt_node *node;
     128             : 
     129       44847 :         struct rb_node ** p;
     130     3596100 :         struct rb_node *parent = NULL;
     131     3596100 :         struct db_rbt_node *parent_node = NULL;
     132             : 
     133       44847 :         ssize_t reclen;
     134       44847 :         TDB_DATA data, this_key, this_val;
     135     3596100 :         void *to_free = NULL;
     136             : 
     137     3596100 :         if (db_ctx->traverse_read > 0) {
     138           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     139             :         }
     140             : 
     141     3596100 :         if ((flag == TDB_INSERT) && (rec_priv->node != NULL)) {
     142           1 :                 return NT_STATUS_OBJECT_NAME_COLLISION;
     143             :         }
     144             : 
     145     3596099 :         if ((flag == TDB_MODIFY) && (rec_priv->node == NULL)) {
     146           1 :                 return NT_STATUS_OBJECT_NAME_NOT_FOUND;
     147             :         }
     148             : 
     149     3596098 :         if (num_dbufs == 1) {
     150     3596098 :                 data = dbufs[0];
     151             :         } else {
     152           0 :                 NTSTATUS status;
     153             : 
     154           0 :                 data = (TDB_DATA) {0};
     155           0 :                 status = dbwrap_merge_dbufs(&data, rec, dbufs, num_dbufs);
     156           0 :                 if (!NT_STATUS_IS_OK(status)) {
     157           0 :                         return status;
     158             :                 }
     159           0 :                 to_free = data.dptr;
     160             :         }
     161             : 
     162     3596098 :         if (rec_priv->node != NULL) {
     163             : 
     164             :                 /*
     165             :                  * The record was around previously
     166             :                  */
     167             : 
     168       38737 :                 db_rbt_parse_node(rec_priv->node, &this_key, &this_val);
     169             : 
     170       38737 :                 SMB_ASSERT(this_key.dsize == rec->key.dsize);
     171       38737 :                 SMB_ASSERT(memcmp(this_key.dptr, rec->key.dptr,
     172             :                                   this_key.dsize) == 0);
     173             : 
     174       38737 :                 if (this_val.dsize >= data.dsize) {
     175             :                         /*
     176             :                          * The new value fits into the old space
     177             :                          */
     178       38473 :                         memcpy(this_val.dptr, data.dptr, data.dsize);
     179       38473 :                         rec_priv->node->valuesize = data.dsize;
     180       38473 :                         TALLOC_FREE(to_free);
     181       38473 :                         return NT_STATUS_OK;
     182             :                 }
     183             :         }
     184             : 
     185     3557625 :         reclen = db_rbt_reclen(rec->key.dsize, data.dsize);
     186     3557625 :         if (reclen == -1) {
     187           0 :                 TALLOC_FREE(to_free);
     188           0 :                 return NT_STATUS_INSUFFICIENT_RESOURCES;
     189             :         }
     190             : 
     191     3557625 :         node = talloc_zero_size(db_ctx, reclen);
     192     3557625 :         if (node == NULL) {
     193           0 :                 TALLOC_FREE(to_free);
     194           0 :                 return NT_STATUS_NO_MEMORY;
     195             :         }
     196             : 
     197     3557625 :         if (rec_priv->node != NULL) {
     198         264 :                 if (db_ctx->traverse_nextp != NULL) {
     199           0 :                         if (*db_ctx->traverse_nextp == rec_priv->node) {
     200           0 :                                 *db_ctx->traverse_nextp = node;
     201             :                         }
     202             :                 }
     203             : 
     204             :                 /*
     205             :                  * We need to delete the key from the tree and start fresh,
     206             :                  * there's not enough space in the existing record
     207             :                  */
     208             : 
     209         264 :                 rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     210         264 :                 DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     211             : 
     212             :                 /*
     213             :                  * Keep the existing node around for a while: If the record
     214             :                  * existed before, we reference the key data in there.
     215             :                  */
     216             :         }
     217             : 
     218     3557625 :         node->keysize = rec->key.dsize;
     219     3557625 :         node->valuesize = data.dsize;
     220             : 
     221     3557625 :         db_rbt_parse_node(node, &this_key, &this_val);
     222             : 
     223     3557625 :         memcpy(this_key.dptr, rec->key.dptr, node->keysize);
     224     3557625 :         TALLOC_FREE(rec_priv->node);
     225     3557625 :         rec_priv->node = node;
     226             : 
     227     3557625 :         if (node->valuesize > 0) {
     228     2057593 :                 memcpy(this_val.dptr, data.dptr, node->valuesize);
     229             :         }
     230             : 
     231     3557625 :         parent = NULL;
     232     3557625 :         p = &db_ctx->tree.rb_node;
     233             : 
     234    25740237 :         while (*p) {
     235      374038 :                 struct db_rbt_node *r;
     236      374038 :                 TDB_DATA search_key, search_val;
     237      374038 :                 int res;
     238             : 
     239    22182612 :                 r = db_rbt2node(*p);
     240             : 
     241    22182612 :                 parent = (*p);
     242    22182612 :                 parent_node = r;
     243             : 
     244    22182612 :                 db_rbt_parse_node(r, &search_key, &search_val);
     245             : 
     246    22182612 :                 res = db_rbt_compare(this_key, search_key);
     247             : 
     248    22182612 :                 if (res == -1) {
     249    10154101 :                         p = &(*p)->rb_left;
     250             :                 }
     251    12028511 :                 else if (res == 1) {
     252    12028511 :                         p = &(*p)->rb_right;
     253             :                 }
     254             :                 else {
     255           0 :                         smb_panic("someone messed with the tree");
     256             :                 }
     257             :         }
     258             : 
     259     3557625 :         rb_link_node(&node->rb_node, parent, p);
     260     3557625 :         DLIST_ADD_AFTER(db_ctx->nodes, node, parent_node);
     261     3557625 :         rb_insert_color(&node->rb_node, &db_ctx->tree);
     262             : 
     263     3557625 :         TALLOC_FREE(to_free);
     264             : 
     265     3557625 :         return NT_STATUS_OK;
     266             : }
     267             : 
     268      241233 : static NTSTATUS db_rbt_delete(struct db_record *rec)
     269             : {
     270      241233 :         struct db_rbt_ctx *db_ctx = talloc_get_type_abort(
     271             :                 rec->db->private_data, struct db_rbt_ctx);
     272      241233 :         struct db_rbt_rec *rec_priv = (struct db_rbt_rec *)rec->private_data;
     273             : 
     274      241233 :         if (db_ctx->traverse_read > 0) {
     275           0 :                 return NT_STATUS_MEDIA_WRITE_PROTECTED;
     276             :         }
     277             : 
     278      241233 :         if (rec_priv->node == NULL) {
     279        4370 :                 return NT_STATUS_OK;
     280             :         }
     281             : 
     282      236863 :         if (db_ctx->traverse_nextp != NULL) {
     283       45581 :                 if (*db_ctx->traverse_nextp == rec_priv->node) {
     284           0 :                         *db_ctx->traverse_nextp = rec_priv->node->next;
     285             :                 }
     286             :         }
     287             : 
     288      236863 :         rb_erase(&rec_priv->node->rb_node, &db_ctx->tree);
     289      236863 :         DLIST_REMOVE(db_ctx->nodes, rec_priv->node);
     290      236863 :         TALLOC_FREE(rec_priv->node);
     291             : 
     292      236863 :         return NT_STATUS_OK;
     293             : }
     294             : 
     295             : struct db_rbt_search_result {
     296             :         TDB_DATA key;
     297             :         TDB_DATA val;
     298             :         struct db_rbt_node* node;
     299             : };
     300             : 
     301    35608957 : static bool db_rbt_search_internal(struct db_context *db, TDB_DATA key,
     302             :                                    struct db_rbt_search_result *result)
     303             : {
     304    35608957 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     305             :                 db->private_data, struct db_rbt_ctx);
     306             : 
     307      193547 :         struct rb_node *n;
     308    35608957 :         bool found = false;
     309    35608957 :         struct db_rbt_node *r = NULL;
     310    35608957 :         TDB_DATA search_key = { 0 };
     311    35608957 :         TDB_DATA search_val = { 0 };
     312             : 
     313    35608957 :         n = ctx->tree.rb_node;
     314             : 
     315   109963392 :         while (n != NULL) {
     316      897112 :                 int res;
     317             : 
     318   102437580 :                 r = db_rbt2node(n);
     319             : 
     320   102437580 :                 db_rbt_parse_node(r, &search_key, &search_val);
     321             : 
     322   102437580 :                 res = db_rbt_compare(key, search_key);
     323             : 
     324   102437580 :                 if (res == -1) {
     325    34978500 :                         n = n->rb_left;
     326             :                 }
     327    67459080 :                 else if (res == 1) {
     328    39375935 :                         n = n->rb_right;
     329             :                 }
     330             :                 else {
     331    28018190 :                         found = true;
     332    28018190 :                         break;
     333             :                 }
     334             :         }
     335    35608957 :         if (result != NULL) {
     336    34697998 :                 if (found) {
     337    27630138 :                         result->key = search_key;
     338    27630138 :                         result->val = search_val;
     339    27630138 :                         result->node = r;
     340             :                 } else {
     341     7067860 :                         ZERO_STRUCT(*result);
     342             :                 }
     343             :         }
     344    35608957 :         return found;
     345             : }
     346             : 
     347    20767673 : static struct db_record *db_rbt_fetch_locked(struct db_context *db_ctx,
     348             :                                              TALLOC_CTX *mem_ctx,
     349             :                                              TDB_DATA key)
     350             : {
     351       52407 :         struct db_rbt_rec *rec_priv;
     352       52407 :         struct db_record *result;
     353       52407 :         size_t size;
     354       52407 :         bool found;
     355       52407 :         struct db_rbt_search_result res;
     356             : 
     357    20767673 :         found = db_rbt_search_internal(db_ctx, key, &res);
     358             : 
     359             :         /*
     360             :          * In this low-level routine, play tricks to reduce the number of
     361             :          * tallocs to one. Not recommended for general use, but here it pays
     362             :          * off.
     363             :          */
     364             : 
     365    20767673 :         size = DBWRAP_RBT_ALIGN(sizeof(struct db_record))
     366             :                 + sizeof(struct db_rbt_rec);
     367             : 
     368    20767673 :         if (!found) {
     369             :                 /*
     370             :                  * We need to keep the key around for later store
     371             :                  */
     372     3561731 :                 size += key.dsize;
     373             :         }
     374             : 
     375    20767673 :         result = (struct db_record *)talloc_size(mem_ctx, size);
     376    20767673 :         if (result == NULL) {
     377           0 :                 return NULL;
     378             :         }
     379             : 
     380    20767673 :         rec_priv = (struct db_rbt_rec *)
     381             :                 ((char *)result + DBWRAP_RBT_ALIGN(sizeof(struct db_record)));
     382             : 
     383    20767673 :         result->storev = db_rbt_storev;
     384    20767673 :         result->delete_rec = db_rbt_delete;
     385    20767673 :         result->private_data = rec_priv;
     386             : 
     387    20767673 :         rec_priv->node = res.node;
     388    20767673 :         result->value  = res.val;
     389    20767673 :         result->value_valid = true;
     390             : 
     391    20767673 :         if (found) {
     392    17205942 :                 result->key = res.key;
     393             :         }
     394             :         else {
     395     3561731 :                 result->key.dptr = (uint8_t *)
     396             :                         ((char *)rec_priv + sizeof(*rec_priv));
     397     3561731 :                 result->key.dsize = key.dsize;
     398     3561731 :                 memcpy(result->key.dptr, key.dptr, key.dsize);
     399             :         }
     400             : 
     401    20715266 :         return result;
     402             : }
     403             : 
     404      910959 : static int db_rbt_exists(struct db_context *db, TDB_DATA key)
     405             : {
     406      910959 :         return db_rbt_search_internal(db, key, NULL);
     407             : }
     408             : 
     409           0 : static int db_rbt_wipe(struct db_context *db)
     410             : {
     411           0 :         struct db_rbt_ctx *old_ctx = talloc_get_type_abort(
     412             :                 db->private_data, struct db_rbt_ctx);
     413           0 :         struct db_rbt_ctx *new_ctx = talloc_zero(db, struct db_rbt_ctx);
     414           0 :         if (new_ctx == NULL) {
     415           0 :                 return -1;
     416             :         }
     417           0 :         db->private_data = new_ctx;
     418           0 :         talloc_free(old_ctx);
     419           0 :         return 0;
     420             : }
     421             : 
     422    13930325 : static NTSTATUS db_rbt_parse_record(struct db_context *db, TDB_DATA key,
     423             :                                     void (*parser)(TDB_DATA key, TDB_DATA data,
     424             :                                                    void *private_data),
     425             :                                     void *private_data)
     426             : {
     427      141140 :         struct db_rbt_search_result res;
     428    13930325 :         bool found = db_rbt_search_internal(db, key, &res);
     429             : 
     430    13930325 :         if (!found) {
     431     3506129 :                 return NT_STATUS_NOT_FOUND;
     432             :         }
     433    10424196 :         parser(res.key, res.val, private_data);
     434    10424196 :         return NT_STATUS_OK;
     435             : }
     436             : 
     437       74408 : static int db_rbt_traverse_internal(struct db_context *db,
     438             :                                     int (*f)(struct db_record *db,
     439             :                                              void *private_data),
     440             :                                     void *private_data, uint32_t* count,
     441             :                                     bool rw)
     442             : {
     443       74408 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     444             :                 db->private_data, struct db_rbt_ctx);
     445       74408 :         struct db_rbt_node *cur = NULL;
     446       74408 :         struct db_rbt_node *next = NULL;
     447        1649 :         int ret;
     448             : 
     449      124201 :         for (cur = ctx->nodes; cur != NULL; cur = next) {
     450        3544 :                 struct db_record rec;
     451        3544 :                 struct db_rbt_rec rec_priv;
     452             : 
     453       49793 :                 rec_priv.node = cur;
     454       49793 :                 next = rec_priv.node->next;
     455             : 
     456       49793 :                 ZERO_STRUCT(rec);
     457       49793 :                 rec.db = db;
     458       49793 :                 rec.private_data = &rec_priv;
     459       49793 :                 rec.storev = db_rbt_storev;
     460       49793 :                 rec.delete_rec = db_rbt_delete;
     461       49793 :                 db_rbt_parse_node(rec_priv.node, &rec.key, &rec.value);
     462       49793 :                 rec.value_valid = true;
     463             : 
     464       49793 :                 if (rw) {
     465       48793 :                         ctx->traverse_nextp = &next;
     466             :                 }
     467       49793 :                 ret = f(&rec, private_data);
     468       49793 :                 (*count) ++;
     469       49793 :                 if (rw) {
     470       48793 :                         ctx->traverse_nextp = NULL;
     471             :                 }
     472       49793 :                 if (ret != 0) {
     473           0 :                         return ret;
     474             :                 }
     475       49793 :                 if (rec_priv.node != NULL) {
     476        4212 :                         next = rec_priv.node->next;
     477             :                 }
     478             :         }
     479             : 
     480       72759 :         return 0;
     481             : }
     482             : 
     483           2 : static int db_rbt_traverse_read(struct db_context *db,
     484             :                                 int (*f)(struct db_record *db,
     485             :                                          void *private_data),
     486             :                                 void *private_data)
     487             : {
     488           2 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     489             :                 db->private_data, struct db_rbt_ctx);
     490           2 :         uint32_t count = 0;
     491           2 :         int ret;
     492             : 
     493           2 :         ctx->traverse_read++;
     494           2 :         ret = db_rbt_traverse_internal(db,
     495             :                                        f, private_data, &count,
     496             :                                        false /* rw */);
     497           2 :         ctx->traverse_read--;
     498           2 :         if (ret != 0) {
     499           0 :                 return -1;
     500             :         }
     501           2 :         if (count > INT_MAX) {
     502           0 :                 return -1;
     503             :         }
     504           0 :         return count;
     505             : }
     506             : 
     507       74406 : static int db_rbt_traverse(struct db_context *db,
     508             :                            int (*f)(struct db_record *db,
     509             :                                     void *private_data),
     510             :                            void *private_data)
     511             : {
     512       74406 :         struct db_rbt_ctx *ctx = talloc_get_type_abort(
     513             :                 db->private_data, struct db_rbt_ctx);
     514       74406 :         uint32_t count = 0;
     515        1647 :         int ret;
     516             : 
     517       74406 :         if (ctx->traverse_nextp != NULL) {
     518           0 :                 return -1;
     519        1647 :         };
     520             : 
     521       74406 :         if (ctx->traverse_read > 0) {
     522           0 :                 return db_rbt_traverse_read(db, f, private_data);
     523             :         }
     524             : 
     525       74406 :         ret = db_rbt_traverse_internal(db,
     526             :                                        f, private_data, &count,
     527             :                                        true /* rw */);
     528       74406 :         if (ret != 0) {
     529           0 :                 return -1;
     530             :         }
     531       74406 :         if (count > INT_MAX) {
     532           0 :                 return -1;
     533             :         }
     534       72759 :         return count;
     535             : }
     536             : 
     537           0 : static int db_rbt_get_seqnum(struct db_context *db)
     538             : {
     539           0 :         return 0;
     540             : }
     541             : 
     542           0 : static int db_rbt_trans_dummy(struct db_context *db)
     543             : {
     544             :         /*
     545             :          * Transactions are pretty pointless in-memory, just return success.
     546             :          */
     547           0 :         return 0;
     548             : }
     549             : 
     550           0 : static size_t db_rbt_id(struct db_context *db, uint8_t *id, size_t idlen)
     551             : {
     552           0 :         if (idlen >= sizeof(struct db_context *)) {
     553           0 :                 memcpy(id, &db, sizeof(struct db_context *));
     554             :         }
     555           0 :         return sizeof(struct db_context *);
     556             : }
     557             : 
     558     4571424 : struct db_context *db_open_rbt(TALLOC_CTX *mem_ctx)
     559             : {
     560        9107 :         struct db_context *result;
     561             : 
     562     4571424 :         result = talloc_zero(mem_ctx, struct db_context);
     563             : 
     564     4571424 :         if (result == NULL) {
     565           0 :                 return NULL;
     566             :         }
     567             : 
     568     4571424 :         result->private_data = talloc_zero(result, struct db_rbt_ctx);
     569             : 
     570     4571424 :         if (result->private_data == NULL) {
     571           0 :                 TALLOC_FREE(result);
     572           0 :                 return NULL;
     573             :         }
     574             : 
     575     4571424 :         result->fetch_locked = db_rbt_fetch_locked;
     576     4571424 :         result->traverse = db_rbt_traverse;
     577     4571424 :         result->traverse_read = db_rbt_traverse_read;
     578     4571424 :         result->get_seqnum = db_rbt_get_seqnum;
     579     4571424 :         result->transaction_start = db_rbt_trans_dummy;
     580     4571424 :         result->transaction_commit = db_rbt_trans_dummy;
     581     4571424 :         result->transaction_cancel = db_rbt_trans_dummy;
     582     4571424 :         result->exists = db_rbt_exists;
     583     4571424 :         result->wipe = db_rbt_wipe;
     584     4571424 :         result->parse_record = db_rbt_parse_record;
     585     4571424 :         result->id = db_rbt_id;
     586     4571424 :         result->name = "dbwrap rbt";
     587             : 
     588     4571424 :         return result;
     589             : }

Generated by: LCOV version 1.14