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