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