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