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