]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
cleanup & more
[openldap] / servers / slapd / back-bdb / idl.c
1 /* idl.c - ldap id list handling routines */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11 #include <ac/string.h>
12
13 #include "back-bdb.h"
14 #include "idl.h"
15
16 #define IDL_MAX(x,y)    ( x > y ? x : y )
17 #define IDL_MIN(x,y)    ( x < y ? x : y )
18
19 #define IDL_CMP(x,y)    ( x < y ? -1 : ( x > y ? 1 : 0 ) )
20
21 #ifdef SLAP_IDL_CACHE
22 #define IDL_LRU_DELETE( bdb, e ) do {                                   \
23         if ( e->idl_lru_prev != NULL ) {                                \
24                 e->idl_lru_prev->idl_lru_next = e->idl_lru_next;        \
25         } else {                                                        \
26                 bdb->bi_idl_lru_head = e->idl_lru_next;                 \
27         }                                                               \
28         if ( e->idl_lru_next != NULL ) {                                \
29                 e->idl_lru_next->idl_lru_prev = e->idl_lru_prev;        \
30         } else {                                                        \
31                 bdb->bi_idl_lru_tail = e->idl_lru_prev;                 \
32         }                                                               \
33 } while ( 0 )
34
35 #define IDL_LRU_ADD( bdb, e ) do {                                      \
36         e->idl_lru_next = bdb->bi_idl_lru_head;                         \
37         if ( e->idl_lru_next != NULL ) {                                \
38                 e->idl_lru_next->idl_lru_prev = (e);                    \
39         }                                                               \
40         (bdb)->bi_idl_lru_head = (e);                                   \
41         e->idl_lru_prev = NULL;                                         \
42         if ( (bdb)->bi_idl_lru_tail == NULL ) {                         \
43                 (bdb)->bi_idl_lru_tail = (e);                           \
44         }                                                               \
45 } while ( 0 )
46
47 int
48 bdb_idl_entry_cmp( const void *v_idl1, const void *v_idl2 )
49 {
50         const bdb_idl_cache_entry_t *idl1 = v_idl1, *idl2 = v_idl2;
51         int rc;
52
53         if ((rc = idl1->db - idl2->db )) return rc;
54         if ((rc = idl1->kstr.bv_len - idl2->kstr.bv_len )) return rc;
55         return ( memcmp ( idl1->kstr.bv_val, idl2->kstr.bv_val , idl1->kstr.bv_len ) );
56 }
57 #endif
58
59 #if IDL_DEBUG > 0
60 static void idl_check( ID *ids )
61 {
62         if( BDB_IDL_IS_RANGE( ids ) ) {
63                 assert( BDB_IDL_RANGE_FIRST(ids) <= BDB_IDL_RANGE_LAST(ids) );
64         } else {
65                 ID i;
66                 for( i=1; i < ids[0]; i++ ) {
67                         assert( ids[i+1] > ids[i] );
68                 }
69         }
70 }
71
72 #if IDL_DEBUG > 1
73 static void idl_dump( ID *ids )
74 {
75         if( BDB_IDL_IS_RANGE( ids ) ) {
76 #ifdef NEW_LOGGING
77                 LDAP_LOG( INDEX, INFO, "IDL: range (%ld - %ld)\n",
78                         (long) BDB_IDL_RANGE_FIRST( ids ),
79                         (long) BDB_IDL_RANGE_LAST( ids ), 0 );
80 #else
81                 Debug( LDAP_DEBUG_ANY,
82                         "IDL: range ( %ld - %ld )\n",
83                         (long) BDB_IDL_RANGE_FIRST( ids ),
84                         (long) BDB_IDL_RANGE_LAST( ids ) );
85 #endif
86
87         } else {
88                 ID i;
89 #ifdef NEW_LOGGING
90                 LDAP_LOG( INDEX, INFO, "IDL: size %ld", (long) ids[0], 0, 0 );
91 #else
92                 Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0], 0, 0 );
93 #endif
94
95                 for( i=1; i<=ids[0]; i++ ) {
96                         if( i % 16 == 1 ) {
97                                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
98                         }
99 #ifdef NEW_LOGGING
100                         LDAP_LOG( INDEX, INFO, "%02lx",(long)ids[i], 0, 0 );
101 #else
102                         Debug( LDAP_DEBUG_ANY, "  %02lx", (long) ids[i], 0, 0 );
103 #endif
104                 }
105
106                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
107         }
108
109         idl_check( ids );
110 }
111 #endif /* IDL_DEBUG > 1 */
112 #endif /* IDL_DEBUG > 0 */
113
114 unsigned bdb_idl_search( ID *ids, ID id )
115 {
116 #define IDL_BINARY_SEARCH 1
117 #ifdef IDL_BINARY_SEARCH
118         /*
119          * binary search of id in ids
120          * if found, returns position of id
121          * if not found, returns first postion greater than id
122          */
123         unsigned base = 0;
124         unsigned cursor = 0;
125         int val = 0;
126         unsigned n = ids[0];
127
128 #if IDL_DEBUG > 0
129         idl_check( ids );
130 #endif
131
132         while( 0 < n ) {
133                 int pivot = n >> 1;
134                 cursor = base + pivot;
135                 val = IDL_CMP( id, ids[cursor + 1] );
136
137                 if( val < 0 ) {
138                         n = pivot;
139
140                 } else if ( val > 0 ) {
141                         base = cursor + 1;
142                         n -= pivot + 1;
143
144                 } else {
145                         return cursor + 1;
146                 }
147         }
148         
149         if( val > 0 ) {
150                 return cursor + 2;
151         } else {
152                 return cursor + 1;
153         }
154
155 #else
156         /* (reverse) linear search */
157         int i;
158
159 #if IDL_DEBUG > 0
160         idl_check( ids );
161 #endif
162
163         for( i=ids[0]; i; i-- ) {
164                 if( id > ids[i] ) {
165                         break;
166                 }
167         }
168
169         return i+1;
170 #endif
171 }
172
173 int bdb_idl_insert( ID *ids, ID id )
174 {
175         unsigned x;
176
177 #if IDL_DEBUG > 1
178 #ifdef NEW_LOGGING
179         LDAP_LOG( INDEX, DETAIL1, "insert: %04lx at %d\n", (long) id, x, 0 );
180 #else
181         Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x, 0 );
182         idl_dump( ids );
183 #endif
184 #elif IDL_DEBUG > 0
185         idl_check( ids );
186 #endif
187
188         if (BDB_IDL_IS_RANGE( ids )) {
189                 /* if already in range, treat as a dup */
190                 if (id >= BDB_IDL_FIRST(ids) && id <= BDB_IDL_LAST(ids))
191                         return -1;
192                 if (id < BDB_IDL_FIRST(ids))
193                         ids[1] = id;
194                 else if (id > BDB_IDL_LAST(ids))
195                         ids[2] = id;
196                 return 0;
197         }
198
199         x = bdb_idl_search( ids, id );
200         assert( x > 0 );
201
202         if( x < 1 ) {
203                 /* internal error */
204                 return -2;
205         }
206
207         if ( x <= ids[0] && ids[x] == id ) {
208                 /* duplicate */
209                 return -1;
210         }
211
212         if ( ++ids[0] >= BDB_IDL_DB_MAX ) {
213                 if( id < ids[1] ) {
214                         ids[1] = id;
215                         ids[2] = ids[ids[0]-1];
216                 } else if ( ids[ids[0]-1] < id ) {
217                         ids[2] = id;
218                 } else {
219                         ids[2] = ids[ids[0]-1];
220                 }
221                 ids[0] = NOID;
222         
223         } else {
224                 /* insert id */
225                 AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
226                 ids[x] = id;
227         }
228
229 #if IDL_DEBUG > 1
230         idl_dump( ids );
231 #elif IDL_DEBUG > 0
232         idl_check( ids );
233 #endif
234
235         return 0;
236 }
237
238 #if 0   /* unused */
239 static int idl_delete( ID *ids, ID id )
240 {
241         unsigned x = bdb_idl_search( ids, id );
242
243 #if IDL_DEBUG > 1
244 #ifdef NEW_LOGGING
245         LDAP_LOG( INDEX, DETAIL1, "delete: %04lx at %d\n", (long) id, x, 0 );
246 #else
247         Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x, 0 );
248         idl_dump( ids );
249 #endif
250 #elif IDL_DEBUG > 0
251         idl_check( ids );
252 #endif
253
254         assert( x > 0 );
255
256         if( x <= 0 ) {
257                 /* internal error */
258                 return -2;
259         }
260
261         if( x > ids[0] || ids[x] != id ) {
262                 /* not found */
263                 return -1;
264
265         } else if ( --ids[0] == 0 ) {
266                 if( x != 1 ) {
267                         return -3;
268                 }
269
270         } else {
271                 AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
272         }
273
274 #if IDL_DEBUG > 1
275         idl_dump( ids );
276 #elif IDL_DEBUG > 0
277         idl_check( ids );
278 #endif
279
280         return 0;
281 }
282 #endif  /* unused */
283
284 static char *
285 bdb_show_key(
286         DBT             *key,
287         char            *buf )
288 {
289         if ( key->size == sizeof( ID ) ) {
290                 unsigned char *c = key->data;
291                 sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
292                 return buf;
293         } else {
294                 return key->data;
295         }
296 }
297
298 int
299 bdb_idl_fetch_key(
300         BackendDB       *be,
301         DB                      *db,
302         DB_TXN          *tid,
303         DBT                     *key,
304         ID                      *ids )
305 {
306         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
307         int rc;
308         DBT data;
309         DBC *cursor;
310         ID *i;
311         void *ptr;
312         size_t len;
313         int rc2;
314         int flags = bdb->bi_db_opflags | DB_MULTIPLE;
315 #ifdef SLAP_IDL_CACHE
316         bdb_idl_cache_entry_t idl_tmp;
317 #endif
318
319         /* If using BerkeleyDB 4.0, the buf must be large enough to
320          * grab the entire IDL in one get(), otherwise BDB will leak
321          * resources on subsequent get's.  We can safely call get()
322          * twice - once for the data, and once to get the DB_NOTFOUND
323          * result meaning there's no more data. See ITS#2040 for details.
324          * This bug is fixed in BDB 4.1 so a smaller buffer will work if
325          * stack space is too limited.
326          *
327          * configure now requires Berkeley DB 4.1.
328          */
329 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR == 0)
330 #       define BDB_ENOUGH 5
331 #else
332 #       define BDB_ENOUGH 1
333 #endif
334         ID buf[BDB_IDL_DB_SIZE*BDB_ENOUGH];
335
336         char keybuf[16];
337
338 #ifdef NEW_LOGGING
339         LDAP_LOG( INDEX, ARGS,
340                 "bdb_idl_fetch_key: %s\n", 
341                 bdb_show_key( key, keybuf ), 0, 0 );
342 #else
343         Debug( LDAP_DEBUG_ARGS,
344                 "bdb_idl_fetch_key: %s\n", 
345                 bdb_show_key( key, keybuf ), 0, 0 );
346 #endif
347
348         assert( ids != NULL );
349
350 #ifdef SLAP_IDL_CACHE
351         if ( bdb->bi_idl_cache_max_size ) {
352                 bdb_idl_cache_entry_t *matched_idl_entry;
353                 DBT2bv( key, &idl_tmp.kstr );
354                 idl_tmp.db = db;
355                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
356                 matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
357                                               bdb_idl_entry_cmp );
358                 if ( matched_idl_entry != NULL ) {
359                         BDB_IDL_CPY( ids, matched_idl_entry->idl );
360                         IDL_LRU_DELETE( bdb, matched_idl_entry );
361                         IDL_LRU_ADD( bdb, matched_idl_entry );
362                         ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
363                         return LDAP_SUCCESS;
364                 }
365                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
366         }
367 #endif
368
369         DBTzero( &data );
370
371         data.data = buf;
372         data.ulen = sizeof(buf);
373         data.flags = DB_DBT_USERMEM;
374
375         if ( tid ) flags |= DB_RMW;
376
377         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
378         if( rc != 0 ) {
379 #ifdef NEW_LOGGING
380                 LDAP_LOG( INDEX, ERR, 
381                         "bdb_idl_fetch_key: cursor failed: %s (%d)\n", 
382                         db_strerror(rc), rc, 0 );
383 #else
384                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
385                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
386 #endif
387                 return rc;
388         }
389
390         rc = cursor->c_get( cursor, key, &data, flags | DB_SET );
391         if (rc == 0) {
392                 i = ids;
393                 while (rc == 0) {
394                         u_int8_t *j;
395
396                         DB_MULTIPLE_INIT( ptr, &data );
397                         while (ptr) {
398                                 DB_MULTIPLE_NEXT(ptr, &data, j, len);
399                                 if (j) {
400                                         ++i;
401                                         AC_MEMCPY( i, j, sizeof(ID) );
402                                 }
403                         }
404                         rc = cursor->c_get( cursor, key, &data, flags | DB_NEXT_DUP );
405                 }
406                 if ( rc == DB_NOTFOUND ) rc = 0;
407                 ids[0] = i - ids;
408                 /* On disk, a range is denoted by 0 in the first element */
409                 if (ids[1] == 0) {
410                         if (ids[0] != BDB_IDL_RANGE_SIZE) {
411 #ifdef NEW_LOGGING
412                                 LDAP_LOG( INDEX, ERR, 
413                                         "=> bdb_idl_fetch_key: range size mismatch: "
414                                         "expected %ld, got %ld\n", 
415                                         BDB_IDL_RANGE_SIZE, ids[0], 0 );
416 #else
417                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
418                                         "range size mismatch: expected %d, got %ld\n",
419                                         BDB_IDL_RANGE_SIZE, ids[0], 0 );
420 #endif
421                                 cursor->c_close( cursor );
422                                 return -1;
423                         }
424                         BDB_IDL_RANGE( ids, ids[2], ids[3] );
425                 }
426                 data.size = BDB_IDL_SIZEOF(ids);
427         }
428
429         rc2 = cursor->c_close( cursor );
430         if (rc2) {
431 #ifdef NEW_LOGGING
432                 LDAP_LOG( INDEX, ERR, 
433                         "bdb_idl_fetch_key: close failed: %s (%d)\n", 
434                         db_strerror(rc2), rc2, 0 );
435 #else
436                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
437                         "close failed: %s (%d)\n", db_strerror(rc2), rc2, 0 );
438 #endif
439                 return rc2;
440         }
441
442         if( rc == DB_NOTFOUND ) {
443                 return rc;
444
445         } else if( rc != 0 ) {
446 #ifdef NEW_LOGGING
447                 LDAP_LOG( INDEX, ERR, 
448                         "bdb_idl_fetch_key: get failed: %s (%d)\n", 
449                         db_strerror(rc), rc, 0 );
450 #else
451                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
452                         "get failed: %s (%d)\n",
453                         db_strerror(rc), rc, 0 );
454 #endif
455                 return rc;
456
457         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
458                 /* size not multiple of ID size */
459 #ifdef NEW_LOGGING
460                 LDAP_LOG( INDEX, ERR, 
461                         "bdb_idl_fetch_key: odd size: expected %ld multiple, got %ld\n", 
462                         (long) sizeof( ID ), (long) data.size, 0 );
463 #else
464                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
465                         "odd size: expected %ld multiple, got %ld\n",
466                         (long) sizeof( ID ), (long) data.size, 0 );
467 #endif
468                 return -1;
469
470         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
471                 /* size mismatch */
472 #ifdef NEW_LOGGING
473                 LDAP_LOG( INDEX, ERR, 
474                         "bdb_idl_fetch_key: get size mismatch: expected %ld, got %ld\n", 
475                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
476 #else
477                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
478                         "get size mismatch: expected %ld, got %ld\n",
479                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
480 #endif
481                 return -1;
482         }
483
484 #ifdef SLAP_IDL_CACHE
485         if ( bdb->bi_idl_cache_max_size ) {
486                 bdb_idl_cache_entry_t *ee;
487                 ee = (bdb_idl_cache_entry_t *) ch_malloc(
488                         sizeof( bdb_idl_cache_entry_t ) );
489                 ee->db = db;
490                 ee->idl = (ID*) ch_malloc( BDB_IDL_SIZEOF ( ids ) );
491                 ee->idl_lru_prev = NULL;
492                 ee->idl_lru_next = NULL;
493                 BDB_IDL_CPY( ee->idl, ids );
494                 ber_dupbv( &ee->kstr, &idl_tmp.kstr );
495                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
496                 if ( avl_insert( &bdb->bi_idl_tree, (caddr_t) ee,
497                         bdb_idl_entry_cmp, avl_dup_error ))
498                 {
499                         ch_free( ee->kstr.bv_val );
500                         ch_free( ee->idl );
501                         ch_free( ee );
502                 } else {
503                         IDL_LRU_ADD( bdb, ee );
504                         if ( ++bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
505                                 int i = 0;
506                                 while ( bdb->bi_idl_lru_tail != NULL && i < 10 ) {
507                                         ee = bdb->bi_idl_lru_tail;
508                                         if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) ee,
509                                                     bdb_idl_entry_cmp ) == NULL ) {
510 #ifdef NEW_LOGGING
511                                                 LDAP_LOG( INDEX, ERR, 
512                                                         "bdb_idl_fetch_key: AVL delete failed\n", 
513                                                         0, 0, 0 );
514 #else
515                                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
516                                                         "AVL delete failed\n",
517                                                         0, 0, 0 );
518 #endif
519                                         }
520                                         IDL_LRU_DELETE( bdb, ee );
521                                         i++;
522                                         --bdb->bi_idl_cache_size;
523                                         ch_free( ee->kstr.bv_val );
524                                         ch_free( ee->idl );
525                                         ch_free( ee );
526                                 }
527                         }
528                 }
529                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
530         }
531 #endif
532
533         return rc;
534 }
535
536
537 int
538 bdb_idl_insert_key(
539         BackendDB       *be,
540         DB                      *db,
541         DB_TXN          *tid,
542         DBT                     *key,
543         ID                      id )
544 {
545         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
546         int     rc;
547         DBT data;
548         DBC *cursor;
549         ID lo, hi, tmp;
550         char *err;
551
552         {
553                 char buf[16];
554 #ifdef NEW_LOGGING
555                 LDAP_LOG( INDEX, ARGS,
556                         "bdb_idl_insert_key: %lx %s\n", 
557                         (long) id, bdb_show_key( key, buf ), 0 );
558 #else
559                 Debug( LDAP_DEBUG_ARGS,
560                         "bdb_idl_insert_key: %lx %s\n", 
561                         (long) id, bdb_show_key( key, buf ), 0 );
562 #endif
563         }
564
565         assert( id != NOID );
566
567 #ifdef SLAP_IDL_CACHE
568         if ( bdb->bi_idl_cache_size ) {
569                 bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
570                 DBT2bv( key, &idl_tmp.kstr );
571                 idl_tmp.db = db;
572                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
573                 matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
574                                               bdb_idl_entry_cmp );
575                 if ( matched_idl_entry != NULL ) {
576                         if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
577                                             bdb_idl_entry_cmp ) == NULL ) {
578 #ifdef NEW_LOGGING
579                                 LDAP_LOG( INDEX, ERR, 
580                                         "bdb_idl_fetch_key: AVL delete failed\n", 
581                                         0, 0, 0 );
582 #else
583                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
584                                         "AVL delete failed\n",
585                                         0, 0, 0 );
586 #endif
587                         }
588                         --bdb->bi_idl_cache_size;
589                         IDL_LRU_DELETE( bdb, matched_idl_entry );
590                         free( matched_idl_entry->kstr.bv_val );
591                         free( matched_idl_entry->idl );
592                         free( matched_idl_entry );
593                 }
594                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
595         }
596 #endif
597
598         DBTzero( &data );
599         data.size = sizeof( ID );
600         data.ulen = data.size;
601         data.flags = DB_DBT_USERMEM;
602
603         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
604         if ( rc != 0 ) {
605 #ifdef NEW_LOGGING
606                 LDAP_LOG( INDEX, ERR, 
607                         "bdb_idl_insert_key: cursor failed: %s (%d)\n", 
608                         db_strerror(rc), rc, 0 );
609 #else
610                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
611                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
612 #endif
613                 return rc;
614         }
615         data.data = &tmp;
616         /* Fetch the first data item for this key, to see if it
617          * exists and if it's a range.
618          */
619         rc = cursor->c_get( cursor, key, &data, DB_SET | DB_RMW );
620         err = "c_get";
621         if ( rc == 0 ) {
622                 if ( tmp != 0 ) {
623                         /* not a range, count the number of items */
624                         db_recno_t count;
625                         rc = cursor->c_count( cursor, &count, 0 );
626                         if ( rc != 0 ) {
627                                 err = "c_count";
628                                 goto fail;
629                         }
630                         if ( count >= BDB_IDL_DB_MAX ) {
631                         /* No room, convert to a range */
632                                 DBT key2 = *key;
633
634                                 key2.dlen = key2.ulen;
635                                 key2.flags |= DB_DBT_PARTIAL;
636
637                                 lo = tmp;
638                                 data.data = &hi;
639                                 rc = cursor->c_get( cursor, &key2, &data, DB_NEXT_NODUP );
640                                 if ( rc != 0 && rc != DB_NOTFOUND ) {
641                                         err = "c_get next_nodup";
642                                         goto fail;
643                                 }
644                                 if ( rc == DB_NOTFOUND ) {
645                                         rc = cursor->c_get( cursor, key, &data, DB_LAST );
646                                         if ( rc != 0 ) {
647                                                 err = "c_get last";
648                                                 goto fail;
649                                         }
650                                 } else {
651                                         rc = cursor->c_get( cursor, key, &data, DB_PREV );
652                                         if ( rc != 0 ) {
653                                                 err = "c_get prev";
654                                                 goto fail;
655                                         }
656                                 }
657                                 if ( id < lo )
658                                         lo = id;
659                                 else if ( id > hi )
660                                         hi = id;
661                                 rc = db->del( db, tid, key, 0 );
662                                 if ( rc != 0 ) {
663                                         err = "del";
664                                         goto fail;
665                                 }
666                                 data.data = &id;
667                                 id = 0;
668                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
669                                 if ( rc != 0 ) {
670                                         err = "c_put 0";
671                                         goto fail;
672                                 }
673                                 id = lo;
674                                 rc = cursor->c_put( cursor, key, &data, DB_KEYLAST );
675                                 if ( rc != 0 ) {
676                                         err = "c_put lo";
677                                         goto fail;
678                                 }
679                                 id = hi;
680                                 rc = cursor->c_put( cursor, key, &data, DB_KEYLAST );
681                                 if ( rc != 0 ) {
682                                         err = "c_put hi";
683                                         goto fail;
684                                 }
685                         } else {
686                         /* There's room, just store it */
687                                 goto put1;
688                         }
689                 } else {
690                         /* It's a range, see if we need to rewrite
691                          * the boundaries
692                          */
693                         hi = id;
694                         data.data = &lo;
695                         rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
696                         if ( rc != 0 ) {
697                                 err = "c_get lo";
698                                 goto fail;
699                         }
700                         if ( id > lo ) {
701                                 data.data = &hi;
702                                 rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
703                                 if ( rc != 0 ) {
704                                         err = "c_get hi";
705                                         goto fail;
706                                 }
707                         }
708                         if ( id < lo || id > hi ) {
709                                 /* Delete the current lo/hi */
710                                 rc = cursor->c_del( cursor, 0 );
711                                 if ( rc != 0 ) {
712                                         err = "c_del";
713                                         goto fail;
714                                 }
715                                 data.data = &id;
716                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
717                                 if ( rc != 0 ) {
718                                         err = "c_put lo/hi";
719                                         goto fail;
720                                 }
721                         }
722                 }
723         } else if ( rc == DB_NOTFOUND ) {
724 put1:           data.data = &id;
725                 rc = cursor->c_put( cursor, key, &data, DB_NODUPDATA );
726                 /* Don't worry if it's already there */
727                 if ( rc != 0 && rc != DB_KEYEXIST ) {
728                         err = "c_put id";
729                         goto fail;
730                 }
731         } else {
732                 /* initial c_get failed, nothing was done */
733 fail:
734 #ifdef NEW_LOGGING
735                 LDAP_LOG( INDEX, ERR, 
736                         "bdb_idl_insert_key: %s failed: %s (%d)\n", 
737                         err, db_strerror(rc), rc );
738 #else
739                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
740                         "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
741 #endif
742                 cursor->c_close( cursor );
743                 return rc;
744         }
745         rc = cursor->c_close( cursor );
746         if( rc != 0 ) {
747 #ifdef NEW_LOGGING
748                 LDAP_LOG( INDEX, ERR, 
749                         "bdb_idl_insert_key: c_close failed: %s (%d)\n", 
750                         db_strerror(rc), rc, 0 );
751 #else
752                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
753                         "c_close failed: %s (%d)\n",
754                         db_strerror(rc), rc, 0 );
755 #endif
756         }
757         return rc;
758 }
759
760 int
761 bdb_idl_delete_key(
762         BackendDB       *be,
763         DB                      *db,
764         DB_TXN          *tid,
765         DBT                     *key,
766         ID                      id )
767 {
768         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
769         int     rc;
770         DBT data;
771         DBC *cursor;
772         ID lo, hi, tmp;
773         char *err;
774
775         {
776                 char buf[16];
777 #ifdef NEW_LOGGING
778                 LDAP_LOG( INDEX, ARGS,
779                         "bdb_idl_delete_key: %lx %s\n", 
780                         (long) id, bdb_show_key( key, buf ), 0 );
781 #else
782                 Debug( LDAP_DEBUG_ARGS,
783                         "bdb_idl_delete_key: %lx %s\n", 
784                         (long) id, bdb_show_key( key, buf ), 0 );
785 #endif
786         }
787         assert( id != NOID );
788
789 #ifdef SLAP_IDL_CACHE
790         if ( bdb->bi_idl_cache_max_size ) {
791                 bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
792                 DBT2bv( key, &idl_tmp.kstr );
793                 idl_tmp.db = db;
794                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
795                 matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
796                                               bdb_idl_entry_cmp );
797                 if ( matched_idl_entry != NULL ) {
798                         if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
799                                             bdb_idl_entry_cmp ) == NULL ) {
800 #ifdef NEW_LOGGING
801                                 LDAP_LOG( INDEX, ERR, 
802                                         "bdb_idl_fetch_key: AVL delete failed\n", 
803                                         0, 0, 0 );
804 #else
805                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
806                                         "AVL delete failed\n",
807                                         0, 0, 0 );
808 #endif
809                         }
810                         --bdb->bi_idl_cache_size;
811                         IDL_LRU_DELETE( bdb, matched_idl_entry );
812                         free( matched_idl_entry->kstr.bv_val );
813                         free( matched_idl_entry->idl );
814                         free( matched_idl_entry );
815                 }
816                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
817         }
818 #endif
819
820         DBTzero( &data );
821         data.data = &tmp;
822         data.size = sizeof( id );
823         data.ulen = data.size;
824         data.flags = DB_DBT_USERMEM;
825
826         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
827         if ( rc != 0 ) {
828 #ifdef NEW_LOGGING
829                 LDAP_LOG( INDEX, ERR, 
830                         "bdb_idl_delete_key: cursor failed: %s (%d)\n", 
831                         db_strerror(rc), rc, 0 );
832 #else
833                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
834                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
835 #endif
836                 return rc;
837         }
838         /* Fetch the first data item for this key, to see if it
839          * exists and if it's a range.
840          */
841         rc = cursor->c_get( cursor, key, &data, DB_SET | DB_RMW );
842         err = "c_get";
843         if ( rc == 0 ) {
844                 if ( tmp != 0 ) {
845                         /* Not a range, just delete it */
846                         if (tmp != id) {
847                                 /* position to correct item */
848                                 tmp = id;
849                                 rc = cursor->c_get( cursor, key, &data, 
850                                         DB_GET_BOTH | DB_RMW  );
851                                 if ( rc != 0 ) {
852                                         err = "c_get id";
853                                         goto fail;
854                                 }
855                         }
856                         rc = cursor->c_del( cursor, 0 );
857                         if ( rc != 0 ) {
858                                 err = "c_del id";
859                                 goto fail;
860                         }
861                 } else {
862                         /* It's a range, see if we need to rewrite
863                          * the boundaries
864                          */
865                         data.data = &lo;
866                         rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
867                         if ( rc != 0 ) {
868                                 err = "c_get lo";
869                                 goto fail;
870                         }
871                         data.data = &hi;
872                         rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
873                         if ( rc != 0 ) {
874                                 err = "c_get hi";
875                                 goto fail;
876                         }
877                         if ( id == lo || id == hi ) {
878                                 if ( id == lo ) {
879                                         id++;
880                                         lo = id;
881                                 } else if ( id == hi ) {
882                                         id--;
883                                         hi = id;
884                                 }
885                                 if ( lo >= hi ) {
886                                 /* The range has collapsed... */
887                                         rc = db->del( db, tid, key, 0 );
888                                         if ( rc != 0 ) {
889                                                 err = "del";
890                                                 goto fail;
891                                         }
892                                 } else {
893                                         if ( id == lo ) {
894                                                 /* reposition on lo slot */
895                                                 data.data = &lo;
896                                                 cursor->c_get( cursor, key, &data, DB_PREV );
897                                                 lo = id;
898                                         }
899                                         rc = cursor->c_del( cursor, 0 );
900                                         if ( rc != 0 ) {
901                                                 err = "c_del";
902                                                 goto fail;
903                                         }
904                                 }
905                                 if ( lo <= hi ) {
906                                         data.data = &id;
907                                         rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
908                                         if ( rc != 0 ) {
909                                                 err = "c_put lo/hi";
910                                                 goto fail;
911                                         }
912                                 }
913                         }
914                 }
915         } else {
916                 /* initial c_get failed, nothing was done */
917 fail:
918                 if ( rc != DB_NOTFOUND ) {
919 #ifdef NEW_LOGGING
920                 LDAP_LOG( INDEX, ERR, 
921                         "bdb_idl_delete_key: %s failed: %s (%d)\n", 
922                         err, db_strerror(rc), rc );
923 #else
924                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
925                         "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
926 #endif
927                 }
928                 cursor->c_close( cursor );
929                 return rc;
930         }
931         rc = cursor->c_close( cursor );
932         if( rc != 0 ) {
933 #ifdef NEW_LOGGING
934                 LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: c_close failed: %s (%d)\n", 
935                         db_strerror(rc), rc, 0 );
936 #else
937                 Debug( LDAP_DEBUG_ANY,
938                         "=> bdb_idl_delete_key: c_close failed: %s (%d)\n",
939                         db_strerror(rc), rc, 0 );
940 #endif
941         }
942
943         return rc;
944 }
945
946
947 /*
948  * idl_intersection - return a = a intersection b
949  */
950 int
951 bdb_idl_intersection(
952         ID *a,
953         ID *b )
954 {
955         ID ida, idb;
956         ID idmax, idmin;
957         ID cursora = 0, cursorb = 0, cursorc;
958         int swap = 0;
959
960         if ( BDB_IDL_IS_ZERO( a ) || BDB_IDL_IS_ZERO( b ) ) {
961                 a[0] = 0;
962                 return 0;
963         }
964
965         idmin = IDL_MAX( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
966         idmax = IDL_MIN( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
967         if ( idmin > idmax ) {
968                 a[0] = 0;
969                 return 0;
970         } else if ( idmin == idmax ) {
971                 a[0] = 1;
972                 a[1] = idmin;
973                 return 0;
974         }
975
976         if ( BDB_IDL_IS_RANGE( a ) ) {
977                 if ( BDB_IDL_IS_RANGE(b) ) {
978                 /* If both are ranges, just shrink the boundaries */
979                         a[1] = idmin;
980                         a[2] = idmax;
981                         return 0;
982                 } else {
983                 /* Else swap so that b is the range, a is a list */
984                         ID *tmp = a;
985                         a = b;
986                         b = tmp;
987                         swap = 1;
988                 }
989         }
990
991         /* If a range completely covers the list, the result is
992          * just the list. If idmin to idmax is contiguous, just
993          * turn it into a range.
994          */
995         if ( BDB_IDL_IS_RANGE( b )
996                 && BDB_IDL_FIRST( b ) <= BDB_IDL_FIRST( a )
997                 && BDB_IDL_LAST( b ) >= BDB_IDL_LAST( a ) ) {
998                 if (idmax - idmin + 1 == a[0])
999                 {
1000                         a[0] = NOID;
1001                         a[1] = idmin;
1002                         a[2] = idmax;
1003                 }
1004                 goto done;
1005         }
1006
1007         /* Fine, do the intersection one element at a time.
1008          * First advance to idmin in both IDLs.
1009          */
1010         cursora = cursorb = idmin;
1011         ida = bdb_idl_first( a, &cursora );
1012         idb = bdb_idl_first( b, &cursorb );
1013         cursorc = 0;
1014
1015         while( ida <= idmax || idb <= idmax ) {
1016                 if( ida == idb ) {
1017                         a[++cursorc] = ida;
1018                         ida = bdb_idl_next( a, &cursora );
1019                         idb = bdb_idl_next( b, &cursorb );
1020                 } else if ( ida < idb ) {
1021                         ida = bdb_idl_next( a, &cursora );
1022                 } else {
1023                         idb = bdb_idl_next( b, &cursorb );
1024                 }
1025         }
1026         a[0] = cursorc;
1027 done:
1028         if (swap)
1029                 BDB_IDL_CPY( b, a );
1030
1031         return 0;
1032 }
1033
1034
1035 /*
1036  * idl_union - return a = a union b
1037  */
1038 int
1039 bdb_idl_union(
1040         ID      *a,
1041         ID      *b )
1042 {
1043         ID ida, idb;
1044         ID cursora = 0, cursorb = 0, cursorc;
1045
1046         if ( BDB_IDL_IS_ZERO( b ) ) {
1047                 return 0;
1048         }
1049
1050         if ( BDB_IDL_IS_ZERO( a ) ) {
1051                 BDB_IDL_CPY( a, b );
1052                 return 0;
1053         }
1054
1055         if ( BDB_IDL_IS_RANGE( a ) || BDB_IDL_IS_RANGE(b) ) {
1056 over:           ida = IDL_MIN( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
1057                 idb = IDL_MAX( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
1058                 a[0] = NOID;
1059                 a[1] = ida;
1060                 a[2] = idb;
1061                 return 0;
1062         }
1063
1064         ida = bdb_idl_first( a, &cursora );
1065         idb = bdb_idl_first( b, &cursorb );
1066
1067         cursorc = b[0];
1068
1069         /* The distinct elements of a are cat'd to b */
1070         while( ida != NOID || idb != NOID ) {
1071                 if ( ida < idb ) {
1072                         if( ++cursorc > BDB_IDL_UM_MAX ) {
1073                                 goto over;
1074                         }
1075                         b[cursorc] = ida;
1076                         ida = bdb_idl_next( a, &cursora );
1077
1078                 } else {
1079                         if ( ida == idb )
1080                                 ida = bdb_idl_next( a, &cursora );
1081                         idb = bdb_idl_next( b, &cursorb );
1082                 }
1083         }
1084
1085         /* b is copied back to a in sorted order */
1086         a[0] = cursorc;
1087         cursora = 1;
1088         cursorb = 1;
1089         cursorc = b[0]+1;
1090         while (cursorb <= b[0] || cursorc <= a[0]) {
1091                 if (cursorc > a[0])
1092                         idb = NOID;
1093                 else
1094                         idb = b[cursorc];
1095                 if (cursorb <= b[0] && b[cursorb] < idb)
1096                         a[cursora++] = b[cursorb++];
1097                 else {
1098                         a[cursora++] = idb;
1099                         cursorc++;
1100                 }
1101         }
1102
1103         return 0;
1104 }
1105
1106
1107 #if 0
1108 /*
1109  * bdb_idl_notin - return a intersection ~b (or a minus b)
1110  */
1111 int
1112 bdb_idl_notin(
1113         ID      *a,
1114         ID      *b,
1115         ID *ids )
1116 {
1117         ID ida, idb;
1118         ID cursora = 0, cursorb = 0;
1119
1120         if( BDB_IDL_IS_ZERO( a ) ||
1121                 BDB_IDL_IS_ZERO( b ) ||
1122                 BDB_IDL_IS_RANGE( b ) )
1123         {
1124                 BDB_IDL_CPY( ids, a );
1125                 return 0;
1126         }
1127
1128         if( BDB_IDL_IS_RANGE( a ) ) {
1129                 BDB_IDL_CPY( ids, a );
1130                 return 0;
1131         }
1132
1133         ida = bdb_idl_first( a, &cursora ),
1134         idb = bdb_idl_first( b, &cursorb );
1135
1136         ids[0] = 0;
1137
1138         while( ida != NOID ) {
1139                 if ( idb == NOID ) {
1140                         /* we could shortcut this */
1141                         ids[++ids[0]] = ida;
1142                         ida = bdb_idl_next( a, &cursora );
1143
1144                 } else if ( ida < idb ) {
1145                         ids[++ids[0]] = ida;
1146                         ida = bdb_idl_next( a, &cursora );
1147
1148                 } else if ( ida > idb ) {
1149                         idb = bdb_idl_next( b, &cursorb );
1150
1151                 } else {
1152                         ida = bdb_idl_next( a, &cursora );
1153                         idb = bdb_idl_next( b, &cursorb );
1154                 }
1155         }
1156
1157         return 0;
1158 }
1159 #endif
1160
1161 ID bdb_idl_first( ID *ids, ID *cursor )
1162 {
1163         ID pos;
1164
1165         if ( ids[0] == 0 ) {
1166                 *cursor = NOID;
1167                 return NOID;
1168         }
1169
1170         if ( BDB_IDL_IS_RANGE( ids ) ) {
1171                 if( *cursor < ids[1] ) {
1172                         *cursor = ids[1];
1173                 }
1174                 return *cursor;
1175         }
1176
1177         if ( *cursor == 0 )
1178                 pos = 1;
1179         else
1180                 pos = bdb_idl_search( ids, *cursor );
1181
1182         if( pos > ids[0] ) {
1183                 return NOID;
1184         }
1185
1186         *cursor = pos;
1187         return ids[pos];
1188 }
1189
1190 ID bdb_idl_next( ID *ids, ID *cursor )
1191 {
1192         if ( BDB_IDL_IS_RANGE( ids ) ) {
1193                 if( ids[2] < ++(*cursor) ) {
1194                         return NOID;
1195                 }
1196                 return *cursor;
1197         }
1198
1199         if ( ++(*cursor) <= ids[0] ) {
1200                 return ids[*cursor];
1201         }
1202
1203         return NOID;
1204 }
1205