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