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