]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/tools.c
Better IDL cache allocation
[openldap] / servers / slapd / back-bdb / tools.c
1 /* tools.c - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2005 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #define AVL_INTERNAL
23 #include "back-bdb.h"
24 #include "idl.h"
25
26 static DBC *cursor = NULL;
27 static DBT key, data;
28
29 typedef struct dn_id {
30         ID id;
31         struct berval dn;
32 } dn_id;
33
34 #define HOLE_SIZE       4096
35 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
36 static unsigned nhmax = HOLE_SIZE;
37 static unsigned nholes;
38
39 static Avlnode *index_attrs, index_dummy;
40
41 #define bdb_tool_idl_cmp                BDB_SYMBOL(tool_idl_cmp)
42 #define bdb_tool_idl_flush_one          BDB_SYMBOL(tool_idl_flush_one)
43 #define bdb_tool_idl_flush              BDB_SYMBOL(tool_idl_flush)
44
45 static int bdb_tool_idl_flush( BackendDB *be );
46
47 #define IDBLOCK 1024
48
49 static ID *bdb_tool_idls;
50 static int bdb_tool_idl_next;
51
52 int bdb_tool_entry_open(
53         BackendDB *be, int mode )
54 {
55         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
56
57         /* initialize key and data thangs */
58         DBTzero( &key );
59         DBTzero( &data );
60         key.flags = DB_DBT_REALLOC;
61         data.flags = DB_DBT_REALLOC;
62
63         if (cursor == NULL) {
64                 int rc = bdb->bi_id2entry->bdi_db->cursor(
65                         bdb->bi_id2entry->bdi_db, NULL, &cursor,
66                         bdb->bi_db_opflags );
67                 if( rc != 0 ) {
68                         return -1;
69                 }
70         }
71
72         /* Get a block of memory for the IDL cache */
73         if ( !(slapMode & SLAP_TOOL_READONLY ) && bdb->bi_idl_cache_max_size )
74                 bdb_tool_idls = ch_malloc( bdb->bi_idl_cache_max_size *
75                         sizeof( ID ) * IDBLOCK );
76
77         return 0;
78 }
79
80 int bdb_tool_entry_close(
81         BackendDB *be )
82 {
83         assert( be != NULL );
84
85         if( key.data ) {
86                 ch_free( key.data );
87                 key.data = NULL;
88         }
89         if( data.data ) {
90                 ch_free( data.data );
91                 data.data = NULL;
92         }
93
94         if( cursor ) {
95                 cursor->c_close( cursor );
96                 cursor = NULL;
97         }
98
99         bdb_tool_idl_flush( be );
100
101         free( bdb_tool_idls );
102         bdb_tool_idls = NULL;
103         bdb_tool_idl_next = 0;
104
105         if( nholes ) {
106                 unsigned i;
107                 fprintf( stderr, "Error, entries missing!\n");
108                 for (i=0; i<nholes; i++) {
109                         fprintf(stderr, "  entry %ld: %s\n",
110                                 holes[i].id, holes[i].dn.bv_val);
111                 }
112                 return -1;
113         }
114                         
115         return 0;
116 }
117
118 static int bdb_reindex_cmp(const void *a, const void *b) { return 0; }
119
120 ID bdb_tool_entry_next(
121         BackendDB *be )
122 {
123         int rc;
124         ID id;
125         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
126
127         assert( be != NULL );
128         assert( slapMode & SLAP_TOOL_MODE );
129         assert( bdb != NULL );
130         
131         rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
132
133         if( rc != 0 ) {
134                 /* If we're doing linear indexing and there are more attrs to
135                  * index, and we're at the end of the database, start over.
136                  */
137                 if ( bdb->bi_attrs == &index_dummy ) {
138                         if ( index_attrs && rc == DB_NOTFOUND ) {
139                                 /* optional - do a checkpoint here? */
140                                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
141                                 rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
142                         }
143                         if ( rc ) {
144                                 bdb->bi_attrs = NULL;
145                                 return NOID;
146                         }
147                 } else {
148                         return NOID;
149                 }
150         }
151
152         if( data.data == NULL ) {
153                 return NOID;
154         }
155
156         BDB_DISK2ID( key.data, &id );
157         return id;
158 }
159
160 ID bdb_tool_dn2id_get(
161         Backend *be,
162         struct berval *dn
163 )
164 {
165         Operation op = {0};
166         Opheader ohdr = {0};
167         EntryInfo *ei = NULL;
168         int rc;
169
170         if ( BER_BVISEMPTY(dn) )
171                 return 0;
172
173         op.o_hdr = &ohdr;
174         op.o_bd = be;
175         op.o_tmpmemctx = NULL;
176         op.o_tmpmfuncs = &ch_mfuncs;
177
178         rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
179         if ( ei ) bdb_cache_entryinfo_unlock( ei );
180         if ( rc == DB_NOTFOUND )
181                 return NOID;
182         
183         return ei->bei_id;
184 }
185
186 int bdb_tool_id2entry_get(
187         Backend *be,
188         ID id,
189         Entry **e
190 )
191 {
192         int rc = bdb_id2entry( be, NULL, 0, id, e );
193
194         if ( rc == DB_NOTFOUND && id == 0 ) {
195                 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
196                 struct berval gluebv = BER_BVC("glue");
197                 dummy->e_name.bv_val = ch_strdup( "" );
198                 dummy->e_nname.bv_val = ch_strdup( "" );
199                 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
200                 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
201                         &gluebv, NULL );
202                 *e = dummy;
203                 rc = LDAP_SUCCESS;
204         }
205         return rc;
206 }
207
208 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
209 {
210         int rc;
211         Entry *e = NULL;
212         struct berval bv;
213
214         assert( be != NULL );
215         assert( slapMode & SLAP_TOOL_MODE );
216         assert( data.data != NULL );
217
218         DBT2bv( &data, &bv );
219
220 #ifdef SLAP_ZONE_ALLOC
221         /* FIXME: will add ctx later */
222         rc = entry_decode( &bv, &e, NULL );
223 #else
224         rc = entry_decode( &bv, &e );
225 #endif
226
227         if( rc == LDAP_SUCCESS ) {
228                 e->e_id = id;
229         }
230 #ifdef BDB_HIER
231         if ( slapMode & SLAP_TOOL_READONLY ) {
232                 EntryInfo *ei = NULL;
233                 Operation op = {0};
234                 Opheader ohdr = {0};
235
236                 op.o_hdr = &ohdr;
237                 op.o_bd = be;
238                 op.o_tmpmemctx = NULL;
239                 op.o_tmpmfuncs = &ch_mfuncs;
240
241                 rc = bdb_cache_find_parent( &op, NULL, cursor->locker, id, &ei );
242                 if ( rc == LDAP_SUCCESS ) {
243                         bdb_cache_entryinfo_unlock( ei );
244                         e->e_private = ei;
245                         ei->bei_e = e;
246                         bdb_fix_dn( e, 0 );
247                         ei->bei_e = NULL;
248                         e->e_private = NULL;
249                 }
250         }
251 #endif
252         return e;
253 }
254
255 static int bdb_tool_next_id(
256         Operation *op,
257         DB_TXN *tid,
258         Entry *e,
259         struct berval *text,
260         int hole )
261 {
262         struct berval dn = e->e_name;
263         struct berval ndn = e->e_nname;
264         struct berval pdn, npdn;
265         EntryInfo *ei = NULL, eidummy;
266         int rc;
267
268         if (ndn.bv_len == 0) {
269                 e->e_id = 0;
270                 return 0;
271         }
272
273         rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
274         if ( ei ) bdb_cache_entryinfo_unlock( ei );
275         if ( rc == DB_NOTFOUND ) {
276                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
277                         ID eid = e->e_id;
278                         dnParent( &dn, &pdn );
279                         dnParent( &ndn, &npdn );
280                         e->e_name = pdn;
281                         e->e_nname = npdn;
282                         rc = bdb_tool_next_id( op, tid, e, text, 1 );
283                         e->e_name = dn;
284                         e->e_nname = ndn;
285                         if ( rc ) {
286                                 return rc;
287                         }
288                         /* If parent didn't exist, it was created just now
289                          * and its ID is now in e->e_id. Make sure the current
290                          * entry gets added under the new parent ID.
291                          */
292                         if ( eid != e->e_id ) {
293                                 eidummy.bei_id = e->e_id;
294                                 ei = &eidummy;
295                         }
296                 }
297                 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
298                 if ( rc ) {
299                         snprintf( text->bv_val, text->bv_len,
300                                 "next_id failed: %s (%d)",
301                                 db_strerror(rc), rc );
302                 Debug( LDAP_DEBUG_ANY,
303                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
304                         return rc;
305                 }
306                 rc = bdb_dn2id_add( op, tid, ei, e );
307                 if ( rc ) {
308                         snprintf( text->bv_val, text->bv_len, 
309                                 "dn2id_add failed: %s (%d)",
310                                 db_strerror(rc), rc );
311                 Debug( LDAP_DEBUG_ANY,
312                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
313                 } else if ( hole ) {
314                         if ( nholes == nhmax - 1 ) {
315                                 if ( holes == hbuf ) {
316                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
317                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
318                                 } else {
319                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
320                                 }
321                                 nhmax *= 2;
322                         }
323                         ber_dupbv( &holes[nholes].dn, &ndn );
324                         holes[nholes++].id = e->e_id;
325                 }
326         } else if ( !hole ) {
327                 unsigned i;
328
329                 e->e_id = ei->bei_id;
330
331                 for ( i=0; i<nholes; i++) {
332                         if ( holes[i].id == e->e_id ) {
333                                 int j;
334                                 free(holes[i].dn.bv_val);
335                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
336                                 holes[j].id = 0;
337                                 nholes--;
338                                 break;
339                         } else if ( holes[i].id > e->e_id ) {
340                                 break;
341                         }
342                 }
343         }
344         return rc;
345 }
346
347 ID bdb_tool_entry_put(
348         BackendDB *be,
349         Entry *e,
350         struct berval *text )
351 {
352         int rc;
353         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
354         DB_TXN *tid = NULL;
355         Operation op = {0};
356         Opheader ohdr = {0};
357
358         assert( be != NULL );
359         assert( slapMode & SLAP_TOOL_MODE );
360
361         assert( text != NULL );
362         assert( text->bv_val != NULL );
363         assert( text->bv_val[0] == '\0' );      /* overconservative? */
364
365         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
366                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
367
368         if (! (slapMode & SLAP_TOOL_QUICK)) {
369         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
370                 bdb->bi_db_opflags );
371         if( rc != 0 ) {
372                 snprintf( text->bv_val, text->bv_len,
373                         "txn_begin failed: %s (%d)",
374                         db_strerror(rc), rc );
375                 Debug( LDAP_DEBUG_ANY,
376                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
377                          text->bv_val, 0, 0 );
378                 return NOID;
379         }
380         }
381
382         op.o_hdr = &ohdr;
383         op.o_bd = be;
384         op.o_tmpmemctx = NULL;
385         op.o_tmpmfuncs = &ch_mfuncs;
386
387         /* add dn2id indices */
388         rc = bdb_tool_next_id( &op, tid, e, text, 0 );
389         if( rc != 0 ) {
390                 goto done;
391         }
392
393         /* id2entry index */
394         rc = bdb_id2entry_add( be, tid, e );
395         if( rc != 0 ) {
396                 snprintf( text->bv_val, text->bv_len,
397                                 "id2entry_add failed: %s (%d)",
398                                 db_strerror(rc), rc );
399                 Debug( LDAP_DEBUG_ANY,
400                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
401                         text->bv_val, 0, 0 );
402                 goto done;
403         }
404
405         if ( !bdb->bi_linear_index )
406                 rc = bdb_index_entry_add( &op, tid, e );
407         if( rc != 0 ) {
408                 snprintf( text->bv_val, text->bv_len,
409                                 "index_entry_add failed: %s (%d)",
410                                 db_strerror(rc), rc );
411                 Debug( LDAP_DEBUG_ANY,
412                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
413                         text->bv_val, 0, 0 );
414                 goto done;
415         }
416
417 done:
418         if( rc == 0 ) {
419                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
420                 rc = TXN_COMMIT( tid, 0 );
421                 if( rc != 0 ) {
422                         snprintf( text->bv_val, text->bv_len,
423                                         "txn_commit failed: %s (%d)",
424                                         db_strerror(rc), rc );
425                         Debug( LDAP_DEBUG_ANY,
426                                 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
427                                 text->bv_val, 0, 0 );
428                         e->e_id = NOID;
429                 }
430                 }
431
432         } else {
433                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
434                 TXN_ABORT( tid );
435                 snprintf( text->bv_val, text->bv_len,
436                         "txn_aborted! %s (%d)",
437                         db_strerror(rc), rc );
438                 Debug( LDAP_DEBUG_ANY,
439                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
440                         text->bv_val, 0, 0 );
441                 }
442                 e->e_id = NOID;
443         }
444
445         return e->e_id;
446 }
447
448 int bdb_tool_entry_reindex(
449         BackendDB *be,
450         ID id )
451 {
452         struct bdb_info *bi = (struct bdb_info *) be->be_private;
453         int rc;
454         Entry *e;
455         DB_TXN *tid = NULL;
456         Operation op = {0};
457         Opheader ohdr = {0};
458
459         Debug( LDAP_DEBUG_ARGS,
460                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
461                 (long) id, 0, 0 );
462
463         /* No indexes configured, nothing to do. Could return an
464          * error here to shortcut things.
465          */
466         if (!bi->bi_attrs) {
467                 return 0;
468         }
469
470         /* Get the first attribute to index */
471         if (bi->bi_linear_index && !index_attrs && bi->bi_attrs != &index_dummy) {
472                 index_attrs = bi->bi_attrs;
473                 bi->bi_attrs = &index_dummy;
474                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
475         }
476
477         e = bdb_tool_entry_get( be, id );
478
479         if( e == NULL ) {
480                 Debug( LDAP_DEBUG_ANY,
481                         LDAP_XSTRING(bdb_tool_entry_reindex)
482                         ": could not locate id=%ld\n",
483                         (long) id, 0, 0 );
484                 return -1;
485         }
486
487         if (! (slapMode & SLAP_TOOL_QUICK)) {
488         rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
489         if( rc != 0 ) {
490                 Debug( LDAP_DEBUG_ANY,
491                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
492                         "txn_begin failed: %s (%d)\n",
493                         db_strerror(rc), rc, 0 );
494                 goto done;
495         }
496         }
497         
498         /*
499          * just (re)add them for now
500          * assume that some other routine (not yet implemented)
501          * will zap index databases
502          *
503          */
504
505         Debug( LDAP_DEBUG_TRACE,
506                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
507                 (long) id, e->e_dn, 0 );
508
509         op.o_hdr = &ohdr;
510         op.o_bd = be;
511         op.o_tmpmemctx = NULL;
512         op.o_tmpmfuncs = &ch_mfuncs;
513
514         rc = bdb_index_entry_add( &op, tid, e );
515
516 done:
517         if( rc == 0 ) {
518                 if (! (slapMode & SLAP_TOOL_QUICK)) {
519                 rc = TXN_COMMIT( tid, 0 );
520                 if( rc != 0 ) {
521                         Debug( LDAP_DEBUG_ANY,
522                                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
523                                 ": txn_commit failed: %s (%d)\n",
524                                 db_strerror(rc), rc, 0 );
525                         e->e_id = NOID;
526                 }
527                 }
528
529         } else {
530                 if (! (slapMode & SLAP_TOOL_QUICK)) {
531                 TXN_ABORT( tid );
532                 Debug( LDAP_DEBUG_ANY,
533                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
534                         ": txn_aborted! %s (%d)\n",
535                         db_strerror(rc), rc, 0 );
536                 }
537                 e->e_id = NOID;
538         }
539         bdb_entry_release( &op, e, 0 );
540
541         return rc;
542 }
543
544 ID bdb_tool_entry_modify(
545         BackendDB *be,
546         Entry *e,
547         struct berval *text )
548 {
549         int rc;
550         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
551         DB_TXN *tid = NULL;
552         Operation op = {0};
553         Opheader ohdr = {0};
554
555         assert( be != NULL );
556         assert( slapMode & SLAP_TOOL_MODE );
557
558         assert( text != NULL );
559         assert( text->bv_val != NULL );
560         assert( text->bv_val[0] == '\0' );      /* overconservative? */
561
562         assert ( e->e_id != NOID );
563
564         Debug( LDAP_DEBUG_TRACE,
565                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
566                 (long) e->e_id, e->e_dn, 0 );
567
568         if (! (slapMode & SLAP_TOOL_QUICK)) {
569         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
570                 bdb->bi_db_opflags );
571         if( rc != 0 ) {
572                 snprintf( text->bv_val, text->bv_len,
573                         "txn_begin failed: %s (%d)",
574                         db_strerror(rc), rc );
575                 Debug( LDAP_DEBUG_ANY,
576                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
577                          text->bv_val, 0, 0 );
578                 return NOID;
579         }
580         }
581
582         op.o_hdr = &ohdr;
583         op.o_bd = be;
584         op.o_tmpmemctx = NULL;
585         op.o_tmpmfuncs = &ch_mfuncs;
586
587         /* id2entry index */
588         rc = bdb_id2entry_update( be, tid, e );
589         if( rc != 0 ) {
590                 snprintf( text->bv_val, text->bv_len,
591                                 "id2entry_add failed: %s (%d)",
592                                 db_strerror(rc), rc );
593                 Debug( LDAP_DEBUG_ANY,
594                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
595                         text->bv_val, 0, 0 );
596                 goto done;
597         }
598
599 #if 0
600         /* FIXME: this is bogus, we don't have the old values to delete
601          * from the index because the given entry has already been modified.
602          */
603         rc = bdb_index_entry_del( &op, tid, e );
604         if( rc != 0 ) {
605                 snprintf( text->bv_val, text->bv_len,
606                                 "index_entry_del failed: %s (%d)",
607                                 db_strerror(rc), rc );
608                 Debug( LDAP_DEBUG_ANY,
609                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
610                         text->bv_val, 0, 0 );
611                 goto done;
612         }
613 #endif
614
615         rc = bdb_index_entry_add( &op, tid, e );
616         if( rc != 0 ) {
617                 snprintf( text->bv_val, text->bv_len,
618                                 "index_entry_add failed: %s (%d)",
619                                 db_strerror(rc), rc );
620                 Debug( LDAP_DEBUG_ANY,
621                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
622                         text->bv_val, 0, 0 );
623                 goto done;
624         }
625
626 done:
627         if( rc == 0 ) {
628                 if (! (slapMode & SLAP_TOOL_QUICK)) {
629                 rc = TXN_COMMIT( tid, 0 );
630                 if( rc != 0 ) {
631                         snprintf( text->bv_val, text->bv_len,
632                                         "txn_commit failed: %s (%d)",
633                                         db_strerror(rc), rc );
634                         Debug( LDAP_DEBUG_ANY,
635                                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
636                                 "%s\n", text->bv_val, 0, 0 );
637                         e->e_id = NOID;
638                 }
639                 }
640
641         } else {
642                 if (! (slapMode & SLAP_TOOL_QUICK)) {
643                 TXN_ABORT( tid );
644                 snprintf( text->bv_val, text->bv_len,
645                         "txn_aborted! %s (%d)",
646                         db_strerror(rc), rc );
647                 Debug( LDAP_DEBUG_ANY,
648                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
649                         text->bv_val, 0, 0 );
650                 }
651                 e->e_id = NOID;
652         }
653
654         return e->e_id;
655 }
656
657
658 typedef struct bdb_tool_idl_cache {
659         struct berval kstr;
660         ID first, last;
661         int idls[BDB_IDL_DB_SIZE / IDBLOCK];
662         int idn, count;
663 } bdb_tool_idl_cache;
664
665 static int
666 bdb_tool_idl_cmp( const void *v1, const void *v2 )
667 {
668         const bdb_tool_idl_cache *c1 = v1, *c2 = v2;
669         int rc;
670
671         if (( rc = c1->kstr.bv_len - c2->kstr.bv_len )) return rc;
672         return memcmp( c1->kstr.bv_val, c2->kstr.bv_val, c1->kstr.bv_len );
673 }
674
675 static int
676 bdb_tool_idl_flush_one( void *v1, void *arg )
677 {
678         bdb_tool_idl_cache *ic = v1;
679         DB *db = arg;
680         DBC *curs;
681         DBT key, data;
682         int i, rc;
683         ID id, nid;
684
685         if ( !ic->count )
686                 return 0;
687
688         rc = db->cursor( db, NULL, &curs, 0 );
689         if ( rc )
690                 return -1;
691
692         DBTzero( &key );
693         DBTzero( &data );
694
695         bv2DBT( &ic->kstr, &key );
696
697         data.size = data.ulen = sizeof( ID );
698         data.flags = DB_DBT_USERMEM;
699         data.data = &nid;
700
701         rc = curs->c_get( curs, &key, &data, DB_SET );
702         /* If key already exists and we're writing a range... */
703         if ( rc == 0 && ic->idn == -1 ) {
704                 /* If it's not currently a range, must delete old info */
705                 if ( nid ) {
706                         /* Skip lo */
707                         while ( curs->c_get( curs, &key, &data, DB_NEXT_DUP ) == 0 )
708                                 curs->c_del( curs, 0 );
709
710                         nid = 0;
711                         /* Store range marker */
712                         curs->c_put( curs, &key, &data, DB_KEYFIRST );
713                 } else {
714                         
715                         /* Skip lo */
716                         rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
717
718                         /* Get hi */
719                         rc = curs->c_get( curs, &key, &data, DB_NEXT_DUP );
720
721                         /* Delete hi */
722                         curs->c_del( curs, 0 );
723                 }
724                 BDB_ID2DISK( ic->last, &nid );
725                 curs->c_put( curs, &key, &data, DB_KEYLAST );
726                 rc = 0;
727         } else if ( rc && rc != DB_NOTFOUND ) {
728                 rc = -1;
729         } else if ( ic->idn == -1 ) {
730                 /* range, didn't exist before */
731                 nid = 0;
732                 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
733                 if ( rc == 0 ) {
734                         BDB_ID2DISK( ic->first, &nid );
735                         rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
736                         if ( rc == 0 ) {
737                                 BDB_ID2DISK( ic->last, &nid );
738                                 rc = curs->c_put( curs, &key, &data, DB_KEYLAST );
739                         }
740                 }
741                 if ( rc ) {
742                         rc = -1;
743                 }
744         } else {
745                 int n;
746
747                 /* Just a normal write */
748                 rc = 0;
749                 for ( n=0; n<=ic->idn; n++ ) {
750                         ID *ids = bdb_tool_idls + ic->idls[n];
751                         int end = ( n < ic->idn ) ? IDBLOCK : (ic->count & (IDBLOCK-1));
752                         for ( i=0; i<end; i++ ) {
753                                 if ( !ids[i] ) continue;
754                                 BDB_ID2DISK( ids[i], &nid );
755                                 rc = curs->c_put( curs, &key, &data, DB_NODUPDATA );
756                                 if ( rc ) {
757                                         if ( rc == DB_KEYEXIST ) {
758                                                 rc = 0;
759                                                 continue;
760                                         }
761                                         rc = -1;
762                                         break;
763                                 }
764                         }
765                         if ( rc ) {
766                                 rc = -1;
767                                 break;
768                         }
769                 }
770         }
771         ch_free( ic );
772         curs->c_close( curs );
773         return rc;
774 }
775
776 static int
777 bdb_tool_idl_flush( BackendDB *be )
778 {
779         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
780         DB *db;
781         Avlnode *root;
782         int i, rc = 0;
783
784         for ( i=BDB_NDB; i < bdb->bi_ndatabases; i++ ) {
785                 db = bdb->bi_databases[i]->bdi_db;
786                 if ( !db->app_private ) continue;
787                 rc = avl_apply( db->app_private, bdb_tool_idl_flush_one, db, -1,
788                         AVL_INORDER );
789                 avl_free( db->app_private, NULL );
790                 db->app_private = NULL;
791                 if ( rc == -1 )
792                         break;
793                 rc = 0;
794         }
795         if ( !rc ) {
796                 bdb->bi_idl_cache_size = 0;
797                 bdb_tool_idl_next = 0;
798         }
799         return rc;
800 }
801
802 int bdb_tool_idl_add(
803         BackendDB *be,
804         DB *db,
805         DB_TXN *txn,
806         DBT *key,
807         ID id )
808 {
809         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
810         bdb_tool_idl_cache *ic, itmp;
811         ID *ids;
812         Avlnode *root;
813         int rc;
814
815         if ( !bdb->bi_idl_cache_max_size )
816                 return bdb_idl_insert_key( be, db, txn, key, id );
817
818 retry:
819         root = db->app_private;
820
821         DBT2bv( key, &itmp.kstr );
822
823         ic = avl_find( root, &itmp, bdb_tool_idl_cmp );
824
825         /* No entry yet, create one */
826         if ( !ic ) {
827                 DBC *curs;
828                 DBT data;
829                 ID nid;
830                 int rc;
831
832                 ic = ch_malloc( sizeof( bdb_tool_idl_cache ) + itmp.kstr.bv_len );
833                 ic->kstr.bv_len = itmp.kstr.bv_len;
834                 ic->kstr.bv_val = (char *)(ic+1);
835                 AC_MEMCPY( ic->kstr.bv_val, itmp.kstr.bv_val, ic->kstr.bv_len );
836                 ic->idn = -1;
837                 ic->last = 0;
838                 ic->count = 0;
839                 avl_insert( &root, ic, bdb_tool_idl_cmp, avl_dup_error );
840                 db->app_private = root;
841
842                 /* load existing key count here */
843                 rc = db->cursor( db, NULL, &curs, 0 );
844                 if ( rc ) return rc;
845
846                 data.ulen = sizeof( ID );
847                 data.flags = DB_DBT_USERMEM;
848                 data.data = &nid;
849                 rc = curs->c_get( curs, key, &data, DB_SET );
850                 if ( rc == 0 ) {
851                         if ( nid == 0 ) {
852                                 ic->count = BDB_IDL_DB_SIZE+1;
853                         } else {
854                                 db_recno_t count;
855
856                                 curs->c_count( curs, &count, 0 );
857                                 ic->count = count;
858                                 BDB_DISK2ID( &nid, &ic->first );
859                         }
860                 }
861                 curs->c_close( curs );
862         }
863         /* are we a range already? */
864         if ( ic->count > BDB_IDL_DB_SIZE ) {
865                 ic->last = id;
866                 return 0;
867         /* Are we at the limit, and converting to a range? */
868         } else if ( ic->count == BDB_IDL_DB_SIZE ) {
869                 ic->last = id;
870                 ic->count++;
871                 ic->idn = -1;
872                 return 0;
873         }
874         /* No free block, create that too */
875         if ( ic->idn == -1 || ( ic->count & (IDBLOCK-1)) == 0) {
876                 bdb->bi_idl_cache_size++;
877                 if ( bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
878                         rc = bdb_tool_idl_flush( be );
879                         if ( rc )
880                                 return rc;
881                         goto retry;
882                 }
883                 ic->idls[++ic->idn] = bdb_tool_idl_next;
884                 ids = bdb_tool_idls + bdb_tool_idl_next;
885                 bdb_tool_idl_next += IDBLOCK;
886
887                 memset( ids, 0, IDBLOCK * sizeof( ID ));
888                 if ( !ic->count )
889                         ic->first = id;
890         }
891         ids = bdb_tool_idls + ic->idls[ic->idn];
892         ids[ ic->count & (IDBLOCK-1) ] = id;
893         ic->count++;
894
895         return 0;
896 }