]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
cleanup bind
[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 static 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                                         avl_delete( &bdb->bi_idl_tree, (caddr_t) ee,
497                                                     bdb_idl_entry_cmp );
498                                         IDL_LRU_DELETE( bdb, ee );
499                                         i++;
500                                         --bdb->bi_idl_cache_size;
501                                         ch_free( ee->kstr.bv_val );
502                                         ch_free( ee->idl );
503                                         ch_free( ee );
504                                 }
505                         }
506                 }
507                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
508         }
509 #endif
510
511         return rc;
512 }
513
514
515 int
516 bdb_idl_insert_key(
517         BackendDB       *be,
518         DB                      *db,
519         DB_TXN          *tid,
520         DBT                     *key,
521         ID                      id )
522 {
523         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
524         int     rc;
525         DBT data;
526         DBC *cursor;
527         ID lo, hi, tmp;
528         char *err;
529
530         {
531                 char buf[16];
532 #ifdef NEW_LOGGING
533                 LDAP_LOG( INDEX, ARGS,
534                         "bdb_idl_insert_key: %lx %s\n", 
535                         (long) id, bdb_show_key( key, buf ), 0 );
536 #else
537                 Debug( LDAP_DEBUG_ARGS,
538                         "bdb_idl_insert_key: %lx %s\n", 
539                         (long) id, bdb_show_key( key, buf ), 0 );
540 #endif
541         }
542
543         assert( id != NOID );
544
545 #ifdef SLAP_IDL_CACHE
546         if ( bdb->bi_idl_cache_size ) {
547                 bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
548                 DBT2bv( key, &idl_tmp.kstr );
549                 idl_tmp.db = db;
550                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
551                 matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
552                                               bdb_idl_entry_cmp );
553                 if ( matched_idl_entry != NULL ) {
554                         avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
555                                     bdb_idl_entry_cmp );
556                         --bdb->bi_idl_cache_size;
557                         IDL_LRU_DELETE( bdb, matched_idl_entry );
558                         free( matched_idl_entry->kstr.bv_val );
559                         free( matched_idl_entry->idl );
560                         free( matched_idl_entry );
561                 }
562                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
563         }
564 #endif
565
566         DBTzero( &data );
567         data.size = sizeof( ID );
568         data.ulen = data.size;
569         data.flags = DB_DBT_USERMEM;
570
571         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
572         if ( rc != 0 ) {
573 #ifdef NEW_LOGGING
574                 LDAP_LOG( INDEX, ERR, 
575                         "bdb_idl_insert_key: cursor failed: %s (%d)\n", 
576                         db_strerror(rc), rc, 0 );
577 #else
578                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
579                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
580 #endif
581                 return rc;
582         }
583         data.data = &tmp;
584         /* Fetch the first data item for this key, to see if it
585          * exists and if it's a range.
586          */
587         rc = cursor->c_get( cursor, key, &data, DB_SET | DB_RMW );
588         err = "c_get";
589         if ( rc == 0 ) {
590                 if ( tmp != 0 ) {
591                         /* not a range, count the number of items */
592                         db_recno_t count;
593                         rc = cursor->c_count( cursor, &count, 0 );
594                         if ( rc != 0 ) {
595                                 err = "c_count";
596                                 goto fail;
597                         }
598                         if ( count >= BDB_IDL_DB_MAX ) {
599                         /* No room, convert to a range */
600                                 DBT key2 = *key;
601
602                                 key2.dlen = key2.ulen;
603                                 key2.flags |= DB_DBT_PARTIAL;
604
605                                 lo = tmp;
606                                 data.data = &hi;
607                                 rc = cursor->c_get( cursor, &key2, &data, DB_NEXT_NODUP );
608                                 if ( rc != 0 && rc != DB_NOTFOUND ) {
609                                         err = "c_get next_nodup";
610                                         goto fail;
611                                 }
612                                 if ( rc == DB_NOTFOUND ) {
613                                         rc = cursor->c_get( cursor, key, &data, DB_LAST );
614                                         if ( rc != 0 ) {
615                                                 err = "c_get last";
616                                                 goto fail;
617                                         }
618                                 } else {
619                                         rc = cursor->c_get( cursor, key, &data, DB_PREV );
620                                         if ( rc != 0 ) {
621                                                 err = "c_get prev";
622                                                 goto fail;
623                                         }
624                                 }
625                                 if ( id < lo )
626                                         lo = id;
627                                 else if ( id > hi )
628                                         hi = id;
629                                 rc = db->del( db, tid, key, 0 );
630                                 if ( rc != 0 ) {
631                                         err = "del";
632                                         goto fail;
633                                 }
634                                 data.data = &id;
635                                 id = 0;
636                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
637                                 if ( rc != 0 ) {
638                                         err = "c_put 0";
639                                         goto fail;
640                                 }
641                                 id = lo;
642                                 rc = cursor->c_put( cursor, key, &data, DB_KEYLAST );
643                                 if ( rc != 0 ) {
644                                         err = "c_put lo";
645                                         goto fail;
646                                 }
647                                 id = hi;
648                                 rc = cursor->c_put( cursor, key, &data, DB_KEYLAST );
649                                 if ( rc != 0 ) {
650                                         err = "c_put hi";
651                                         goto fail;
652                                 }
653                         } else {
654                         /* There's room, just store it */
655                                 goto put1;
656                         }
657                 } else {
658                         /* It's a range, see if we need to rewrite
659                          * the boundaries
660                          */
661                         hi = id;
662                         data.data = &lo;
663                         rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
664                         if ( rc != 0 ) {
665                                 err = "c_get lo";
666                                 goto fail;
667                         }
668                         if ( id > lo ) {
669                                 data.data = &hi;
670                                 rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
671                                 if ( rc != 0 ) {
672                                         err = "c_get hi";
673                                         goto fail;
674                                 }
675                         }
676                         if ( id < lo || id > hi ) {
677                                 /* Delete the current lo/hi */
678                                 rc = cursor->c_del( cursor, 0 );
679                                 if ( rc != 0 ) {
680                                         err = "c_del";
681                                         goto fail;
682                                 }
683                                 data.data = &id;
684                                 rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
685                                 if ( rc != 0 ) {
686                                         err = "c_put lo/hi";
687                                         goto fail;
688                                 }
689                         }
690                 }
691         } else if ( rc == DB_NOTFOUND ) {
692 put1:           data.data = &id;
693                 rc = cursor->c_put( cursor, key, &data, DB_NODUPDATA );
694                 /* Don't worry if it's already there */
695                 if ( rc != 0 && rc != DB_KEYEXIST ) {
696                         err = "c_put id";
697                         goto fail;
698                 }
699         } else {
700                 /* initial c_get failed, nothing was done */
701 fail:
702 #ifdef NEW_LOGGING
703                 LDAP_LOG( INDEX, ERR, 
704                         "bdb_idl_insert_key: %s failed: %s (%d)\n", 
705                         err, db_strerror(rc), rc );
706 #else
707                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
708                         "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
709 #endif
710                 cursor->c_close( cursor );
711                 return rc;
712         }
713         rc = cursor->c_close( cursor );
714         if( rc != 0 ) {
715 #ifdef NEW_LOGGING
716                 LDAP_LOG( INDEX, ERR, 
717                         "bdb_idl_insert_key: c_close failed: %s (%d)\n", 
718                         db_strerror(rc), rc, 0 );
719 #else
720                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
721                         "c_close failed: %s (%d)\n",
722                         db_strerror(rc), rc, 0 );
723 #endif
724         }
725         return rc;
726 }
727
728 int
729 bdb_idl_delete_key(
730         BackendDB       *be,
731         DB                      *db,
732         DB_TXN          *tid,
733         DBT                     *key,
734         ID                      id )
735 {
736         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
737         int     rc;
738         DBT data;
739         DBC *cursor;
740         ID lo, hi, tmp;
741         char *err;
742
743         {
744                 char buf[16];
745 #ifdef NEW_LOGGING
746                 LDAP_LOG( INDEX, ARGS,
747                         "bdb_idl_delete_key: %lx %s\n", 
748                         (long) id, bdb_show_key( key, buf ), 0 );
749 #else
750                 Debug( LDAP_DEBUG_ARGS,
751                         "bdb_idl_delete_key: %lx %s\n", 
752                         (long) id, bdb_show_key( key, buf ), 0 );
753 #endif
754         }
755         assert( id != NOID );
756
757 #ifdef SLAP_IDL_CACHE
758         if ( bdb->bi_idl_cache_max_size ) {
759                 bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
760                 DBT2bv( key, &idl_tmp.kstr );
761                 idl_tmp.db = db;
762                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_mutex );
763                 matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
764                                               bdb_idl_entry_cmp );
765                 if ( matched_idl_entry != NULL ) {
766                         avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
767                                     bdb_idl_entry_cmp );
768                         --bdb->bi_idl_cache_size;
769                         IDL_LRU_DELETE( bdb, matched_idl_entry );
770                         free( matched_idl_entry->kstr.bv_val );
771                         free( matched_idl_entry->idl );
772                         free( matched_idl_entry );
773                 }
774                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_mutex );
775         }
776 #endif
777
778         DBTzero( &data );
779         data.data = &tmp;
780         data.size = sizeof( id );
781         data.ulen = data.size;
782         data.flags = DB_DBT_USERMEM;
783
784         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
785         if ( rc != 0 ) {
786 #ifdef NEW_LOGGING
787                 LDAP_LOG( INDEX, ERR, 
788                         "bdb_idl_delete_key: cursor failed: %s (%d)\n", 
789                         db_strerror(rc), rc, 0 );
790 #else
791                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
792                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
793 #endif
794                 return rc;
795         }
796         /* Fetch the first data item for this key, to see if it
797          * exists and if it's a range.
798          */
799         rc = cursor->c_get( cursor, key, &data, DB_SET | DB_RMW );
800         err = "c_get";
801         if ( rc == 0 ) {
802                 if ( tmp != 0 ) {
803                         /* Not a range, just delete it */
804                         if (tmp != id) {
805                                 /* position to correct item */
806                                 tmp = id;
807                                 rc = cursor->c_get( cursor, key, &data, 
808                                         DB_GET_BOTH | DB_RMW  );
809                                 if ( rc != 0 ) {
810                                         err = "c_get id";
811                                         goto fail;
812                                 }
813                         }
814                         rc = cursor->c_del( cursor, 0 );
815                         if ( rc != 0 ) {
816                                 err = "c_del id";
817                                 goto fail;
818                         }
819                 } else {
820                         /* It's a range, see if we need to rewrite
821                          * the boundaries
822                          */
823                         hi = 0;
824                         data.data = &lo;
825                         rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
826                         if ( rc != 0 ) {
827                                 err = "c_get lo";
828                                 goto fail;
829                         }
830                         if ( id > lo ) {
831                                 data.data = &hi;
832                                 rc = cursor->c_get( cursor, key, &data, DB_NEXT_DUP );
833                                 if ( rc != 0 ) {
834                                         err = "c_get hi";
835                                         goto fail;
836                                 }
837                         }
838                         if ( id == lo || id == hi ) {
839                                 if ( id == lo ) {
840                                         id++;
841                                         lo = id;
842                                 } else if ( id == hi ) {
843                                         id--;
844                                         hi = id;
845                                 }
846                                 if ( lo >= hi ) {
847                                 /* The range has collapsed... */
848                                         rc = db->del( db, tid, key, 0 );
849                                         if ( rc != 0 ) {
850                                                 err = "del";
851                                                 goto fail;
852                                         }
853                                 } else {
854                                         rc = cursor->c_del( cursor, 0 );
855                                         if ( rc != 0 ) {
856                                                 err = "c_del";
857                                                 goto fail;
858                                         }
859                                 }
860                                 if ( lo <= hi ) {
861                                         data.data = &id;
862                                         rc = cursor->c_put( cursor, key, &data, DB_KEYFIRST );
863                                         if ( rc != 0 ) {
864                                                 err = "c_put lo/hi";
865                                                 goto fail;
866                                         }
867                                 }
868                         }
869                 }
870         } else {
871                 /* initial c_get failed, nothing was done */
872 fail:
873                 if ( rc != DB_NOTFOUND ) {
874 #ifdef NEW_LOGGING
875                 LDAP_LOG( INDEX, ERR, 
876                         "bdb_idl_delete_key: %s failed: %s (%d)\n", 
877                         err, db_strerror(rc), rc );
878 #else
879                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
880                         "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
881 #endif
882                 }
883                 cursor->c_close( cursor );
884                 return rc;
885         }
886         rc = cursor->c_close( cursor );
887         if( rc != 0 ) {
888 #ifdef NEW_LOGGING
889                 LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: c_close failed: %s (%d)\n", 
890                         db_strerror(rc), rc, 0 );
891 #else
892                 Debug( LDAP_DEBUG_ANY,
893                         "=> bdb_idl_delete_key: c_close failed: %s (%d)\n",
894                         db_strerror(rc), rc, 0 );
895 #endif
896         }
897
898         return rc;
899 }
900
901
902 /*
903  * idl_intersection - return a = a intersection b
904  */
905 int
906 bdb_idl_intersection(
907         ID *a,
908         ID *b )
909 {
910         ID ida, idb;
911         ID idmax, idmin;
912         ID cursora = 0, cursorb = 0, cursorc;
913         int swap = 0;
914
915         if ( BDB_IDL_IS_ZERO( a ) || BDB_IDL_IS_ZERO( b ) ) {
916                 a[0] = 0;
917                 return 0;
918         }
919
920         idmin = IDL_MAX( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
921         idmax = IDL_MIN( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
922         if ( idmin > idmax ) {
923                 a[0] = 0;
924                 return 0;
925         } else if ( idmin == idmax ) {
926                 a[0] = 1;
927                 a[1] = idmin;
928                 return 0;
929         }
930
931         if ( BDB_IDL_IS_RANGE( a ) ) {
932                 if ( BDB_IDL_IS_RANGE(b) ) {
933                 /* If both are ranges, just shrink the boundaries */
934                         a[1] = idmin;
935                         a[2] = idmax;
936                         return 0;
937                 } else {
938                 /* Else swap so that b is the range, a is a list */
939                         ID *tmp = a;
940                         a = b;
941                         b = tmp;
942                         swap = 1;
943                 }
944         }
945
946         /* If a range completely covers the list, the result is
947          * just the list. If idmin to idmax is contiguous, just
948          * turn it into a range.
949          */
950         if ( BDB_IDL_IS_RANGE( b )
951                 && BDB_IDL_FIRST( b ) <= BDB_IDL_FIRST( a )
952                 && BDB_IDL_LAST( b ) >= BDB_IDL_LAST( a ) ) {
953                 if (idmax - idmin + 1 == a[0])
954                 {
955                         a[0] = NOID;
956                         a[1] = idmin;
957                         a[2] = idmax;
958                 }
959                 goto done;
960         }
961
962         /* Fine, do the intersection one element at a time.
963          * First advance to idmin in both IDLs.
964          */
965         cursora = cursorb = idmin;
966         ida = bdb_idl_first( a, &cursora );
967         idb = bdb_idl_first( b, &cursorb );
968         cursorc = 0;
969
970         while( ida <= idmax || idb <= idmax ) {
971                 if( ida == idb ) {
972                         a[++cursorc] = ida;
973                         ida = bdb_idl_next( a, &cursora );
974                         idb = bdb_idl_next( b, &cursorb );
975                 } else if ( ida < idb ) {
976                         ida = bdb_idl_next( a, &cursora );
977                 } else {
978                         idb = bdb_idl_next( b, &cursorb );
979                 }
980         }
981         a[0] = cursorc;
982 done:
983         if (swap)
984                 BDB_IDL_CPY( b, a );
985
986         return 0;
987 }
988
989
990 /*
991  * idl_union - return a = a union b
992  */
993 int
994 bdb_idl_union(
995         ID      *a,
996         ID      *b )
997 {
998         ID ida, idb;
999         ID cursora = 0, cursorb = 0, cursorc;
1000
1001         if ( BDB_IDL_IS_ZERO( b ) ) {
1002                 return 0;
1003         }
1004
1005         if ( BDB_IDL_IS_ZERO( a ) ) {
1006                 BDB_IDL_CPY( a, b );
1007                 return 0;
1008         }
1009
1010         if ( BDB_IDL_IS_RANGE( a ) || BDB_IDL_IS_RANGE(b) ) {
1011 over:           ida = IDL_MIN( BDB_IDL_FIRST(a), BDB_IDL_FIRST(b) );
1012                 idb = IDL_MAX( BDB_IDL_LAST(a), BDB_IDL_LAST(b) );
1013                 a[0] = NOID;
1014                 a[1] = ida;
1015                 a[2] = idb;
1016                 return 0;
1017         }
1018
1019         ida = bdb_idl_first( a, &cursora );
1020         idb = bdb_idl_first( b, &cursorb );
1021
1022         cursorc = b[0];
1023
1024         /* The distinct elements of a are cat'd to b */
1025         while( ida != NOID || idb != NOID ) {
1026                 if ( ida < idb ) {
1027                         if( ++cursorc > BDB_IDL_UM_MAX ) {
1028                                 goto over;
1029                         }
1030                         b[cursorc] = ida;
1031                         ida = bdb_idl_next( a, &cursora );
1032
1033                 } else {
1034                         if ( ida == idb )
1035                                 ida = bdb_idl_next( a, &cursora );
1036                         idb = bdb_idl_next( b, &cursorb );
1037                 }
1038         }
1039
1040         /* b is copied back to a in sorted order */
1041         a[0] = cursorc;
1042         cursora = 1;
1043         cursorb = 1;
1044         cursorc = b[0]+1;
1045         while (cursorb <= b[0] || cursorc <= a[0]) {
1046                 if (cursorc > a[0])
1047                         idb = NOID;
1048                 else
1049                         idb = b[cursorc];
1050                 if (cursorb <= b[0] && b[cursorb] < idb)
1051                         a[cursora++] = b[cursorb++];
1052                 else {
1053                         a[cursora++] = idb;
1054                         cursorc++;
1055                 }
1056         }
1057
1058         return 0;
1059 }
1060
1061
1062 #if 0
1063 /*
1064  * bdb_idl_notin - return a intersection ~b (or a minus b)
1065  */
1066 int
1067 bdb_idl_notin(
1068         ID      *a,
1069         ID      *b,
1070         ID *ids )
1071 {
1072         ID ida, idb;
1073         ID cursora = 0, cursorb = 0;
1074
1075         if( BDB_IDL_IS_ZERO( a ) ||
1076                 BDB_IDL_IS_ZERO( b ) ||
1077                 BDB_IDL_IS_RANGE( b ) )
1078         {
1079                 BDB_IDL_CPY( ids, a );
1080                 return 0;
1081         }
1082
1083         if( BDB_IDL_IS_RANGE( a ) ) {
1084                 BDB_IDL_CPY( ids, a );
1085                 return 0;
1086         }
1087
1088         ida = bdb_idl_first( a, &cursora ),
1089         idb = bdb_idl_first( b, &cursorb );
1090
1091         ids[0] = 0;
1092
1093         while( ida != NOID ) {
1094                 if ( idb == NOID ) {
1095                         /* we could shortcut this */
1096                         ids[++ids[0]] = ida;
1097                         ida = bdb_idl_next( a, &cursora );
1098
1099                 } else if ( ida < idb ) {
1100                         ids[++ids[0]] = ida;
1101                         ida = bdb_idl_next( a, &cursora );
1102
1103                 } else if ( ida > idb ) {
1104                         idb = bdb_idl_next( b, &cursorb );
1105
1106                 } else {
1107                         ida = bdb_idl_next( a, &cursora );
1108                         idb = bdb_idl_next( b, &cursorb );
1109                 }
1110         }
1111
1112         return 0;
1113 }
1114 #endif
1115
1116 ID bdb_idl_first( ID *ids, ID *cursor )
1117 {
1118         ID pos;
1119
1120         if ( ids[0] == 0 ) {
1121                 *cursor = NOID;
1122                 return NOID;
1123         }
1124
1125         if ( BDB_IDL_IS_RANGE( ids ) ) {
1126                 if( *cursor < ids[1] ) {
1127                         *cursor = ids[1];
1128                 }
1129                 return *cursor;
1130         }
1131
1132         if ( *cursor == 0 )
1133                 pos = 1;
1134         else
1135                 pos = bdb_idl_search( ids, *cursor );
1136
1137         if( pos > ids[0] ) {
1138                 return NOID;
1139         }
1140
1141         *cursor = pos;
1142         return ids[pos];
1143 }
1144
1145 ID bdb_idl_next( ID *ids, ID *cursor )
1146 {
1147         if ( BDB_IDL_IS_RANGE( ids ) ) {
1148                 if( ids[2] < ++(*cursor) ) {
1149                         return NOID;
1150                 }
1151                 return *cursor;
1152         }
1153
1154         if ( ++(*cursor) <= ids[0] ) {
1155                 return ids[*cursor];
1156         }
1157
1158         return NOID;
1159 }
1160