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