]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/idl.c
f86b7e68d9a3d6de170e4aab0ef6ae6bcbcd1df2
[openldap] / servers / slapd / back-bdb / idl.c
1 /* idl.c - ldap id list handling routines */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2004 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-bdb.h"
23 #include "idl.h"
24
25 #define IDL_MAX(x,y)    ( x > y ? x : y )
26 #define IDL_MIN(x,y)    ( x < y ? x : y )
27
28 #define IDL_CMP(x,y)    ( x < y ? -1 : ( x > y ? 1 : 0 ) )
29
30 #define IDL_LRU_DELETE( bdb, e ) do {                                   \
31         if ( e->idl_lru_prev != NULL ) {                                \
32                 e->idl_lru_prev->idl_lru_next = e->idl_lru_next;        \
33         } else {                                                        \
34                 bdb->bi_idl_lru_head = e->idl_lru_next;                 \
35         }                                                               \
36         if ( e->idl_lru_next != NULL ) {                                \
37                 e->idl_lru_next->idl_lru_prev = e->idl_lru_prev;        \
38         } else {                                                        \
39                 bdb->bi_idl_lru_tail = e->idl_lru_prev;                 \
40         }                                                               \
41 } while ( 0 )
42
43 #define IDL_LRU_ADD( bdb, e ) do {                                      \
44         e->idl_lru_next = bdb->bi_idl_lru_head;                         \
45         if ( e->idl_lru_next != NULL ) {                                \
46                 e->idl_lru_next->idl_lru_prev = (e);                    \
47         }                                                               \
48         (bdb)->bi_idl_lru_head = (e);                                   \
49         e->idl_lru_prev = NULL;                                         \
50         if ( (bdb)->bi_idl_lru_tail == NULL ) {                         \
51                 (bdb)->bi_idl_lru_tail = (e);                           \
52         }                                                               \
53 } while ( 0 )
54
55 static int
56 bdb_idl_entry_cmp( const void *v_idl1, const void *v_idl2 )
57 {
58         const bdb_idl_cache_entry_t *idl1 = v_idl1, *idl2 = v_idl2;
59         int rc;
60
61         if ((rc = idl1->db - idl2->db )) return rc;
62         if ((rc = idl1->kstr.bv_len - idl2->kstr.bv_len )) return rc;
63         return ( memcmp ( idl1->kstr.bv_val, idl2->kstr.bv_val , idl1->kstr.bv_len ) );
64 }
65
66 #if IDL_DEBUG > 0
67 static void idl_check( ID *ids )
68 {
69         if( BDB_IDL_IS_RANGE( ids ) ) {
70                 assert( BDB_IDL_RANGE_FIRST(ids) <= BDB_IDL_RANGE_LAST(ids) );
71         } else {
72                 ID i;
73                 for( i=1; i < ids[0]; i++ ) {
74                         assert( ids[i+1] > ids[i] );
75                 }
76         }
77 }
78
79 #if IDL_DEBUG > 1
80 static void idl_dump( ID *ids )
81 {
82         if( BDB_IDL_IS_RANGE( ids ) ) {
83 #ifdef NEW_LOGGING
84                 LDAP_LOG( INDEX, INFO, "IDL: range (%ld - %ld)\n",
85                         (long) BDB_IDL_RANGE_FIRST( ids ),
86                         (long) BDB_IDL_RANGE_LAST( ids ), 0 );
87 #else
88                 Debug( LDAP_DEBUG_ANY,
89                         "IDL: range ( %ld - %ld )\n",
90                         (long) BDB_IDL_RANGE_FIRST( ids ),
91                         (long) BDB_IDL_RANGE_LAST( ids ) );
92 #endif
93
94         } else {
95                 ID i;
96 #ifdef NEW_LOGGING
97                 LDAP_LOG( INDEX, INFO, "IDL: size %ld", (long) ids[0], 0, 0 );
98 #else
99                 Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0], 0, 0 );
100 #endif
101
102                 for( i=1; i<=ids[0]; i++ ) {
103                         if( i % 16 == 1 ) {
104                                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
105                         }
106 #ifdef NEW_LOGGING
107                         LDAP_LOG( INDEX, INFO, "%02lx",(long)ids[i], 0, 0 );
108 #else
109                         Debug( LDAP_DEBUG_ANY, "  %02lx", (long) ids[i], 0, 0 );
110 #endif
111                 }
112
113                 Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
114         }
115
116         idl_check( ids );
117 }
118 #endif /* IDL_DEBUG > 1 */
119 #endif /* IDL_DEBUG > 0 */
120
121 unsigned bdb_idl_search( ID *ids, ID id )
122 {
123 #define IDL_BINARY_SEARCH 1
124 #ifdef IDL_BINARY_SEARCH
125         /*
126          * binary search of id in ids
127          * if found, returns position of id
128          * if not found, returns first postion greater than id
129          */
130         unsigned base = 0;
131         unsigned cursor = 0;
132         int val = 0;
133         unsigned n = ids[0];
134
135 #if IDL_DEBUG > 0
136         idl_check( ids );
137 #endif
138
139         while( 0 < n ) {
140                 int pivot = n >> 1;
141                 cursor = base + pivot;
142                 val = IDL_CMP( id, ids[cursor + 1] );
143
144                 if( val < 0 ) {
145                         n = pivot;
146
147                 } else if ( val > 0 ) {
148                         base = cursor + 1;
149                         n -= pivot + 1;
150
151                 } else {
152                         return cursor + 1;
153                 }
154         }
155         
156         if( val > 0 ) {
157                 return cursor + 2;
158         } else {
159                 return cursor + 1;
160         }
161
162 #else
163         /* (reverse) linear search */
164         int i;
165
166 #if IDL_DEBUG > 0
167         idl_check( ids );
168 #endif
169
170         for( i=ids[0]; i; i-- ) {
171                 if( id > ids[i] ) {
172                         break;
173                 }
174         }
175
176         return i+1;
177 #endif
178 }
179
180 int bdb_idl_insert( ID *ids, ID id )
181 {
182         unsigned x;
183
184 #if IDL_DEBUG > 1
185 #ifdef NEW_LOGGING
186         LDAP_LOG( INDEX, DETAIL1, "insert: %04lx at %d\n", (long) id, x, 0 );
187 #else
188         Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x, 0 );
189         idl_dump( ids );
190 #endif
191 #elif IDL_DEBUG > 0
192         idl_check( ids );
193 #endif
194
195         if (BDB_IDL_IS_RANGE( ids )) {
196                 /* if already in range, treat as a dup */
197                 if (id >= BDB_IDL_FIRST(ids) && id <= BDB_IDL_LAST(ids))
198                         return -1;
199                 if (id < BDB_IDL_FIRST(ids))
200                         ids[1] = id;
201                 else if (id > BDB_IDL_LAST(ids))
202                         ids[2] = id;
203                 return 0;
204         }
205
206         x = bdb_idl_search( ids, id );
207         assert( x > 0 );
208
209         if( x < 1 ) {
210                 /* internal error */
211                 return -2;
212         }
213
214         if ( x <= ids[0] && ids[x] == id ) {
215                 /* duplicate */
216                 return -1;
217         }
218
219         if ( ++ids[0] >= BDB_IDL_DB_MAX ) {
220                 if( id < ids[1] ) {
221                         ids[1] = id;
222                         ids[2] = ids[ids[0]-1];
223                 } else if ( ids[ids[0]-1] < id ) {
224                         ids[2] = id;
225                 } else {
226                         ids[2] = ids[ids[0]-1];
227                 }
228                 ids[0] = NOID;
229         
230         } else {
231                 /* insert id */
232                 AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
233                 ids[x] = id;
234         }
235
236 #if IDL_DEBUG > 1
237         idl_dump( ids );
238 #elif IDL_DEBUG > 0
239         idl_check( ids );
240 #endif
241
242         return 0;
243 }
244
245 static int bdb_idl_delete( ID *ids, ID id )
246 {
247         unsigned x;
248
249 #if IDL_DEBUG > 1
250 #ifdef NEW_LOGGING
251         LDAP_LOG( INDEX, DETAIL1, "delete: %04lx at %d\n", (long) id, x, 0 );
252 #else
253         Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x, 0 );
254         idl_dump( ids );
255 #endif
256 #elif IDL_DEBUG > 0
257         idl_check( ids );
258 #endif
259
260         if (BDB_IDL_IS_RANGE( ids )) {
261                 /* if in range, treat as a noop */
262                 if (id > BDB_IDL_FIRST(ids) && id < BDB_IDL_LAST(ids))
263                         return -1;
264                 if (id == BDB_IDL_FIRST(ids))
265                         ids[1] = id+1;
266                 else if (id == BDB_IDL_LAST(ids))
267                         ids[2] = id-1;
268                 /* range collapsed to a single item */
269                 if ( ids[1] == ids[2] )
270                         ids[0] = 1;
271                 return 0;
272         }
273
274         x = bdb_idl_search( ids, id );
275         assert( x > 0 );
276
277         if( x <= 0 ) {
278                 /* internal error */
279                 return -2;
280         }
281
282         if( x > ids[0] || ids[x] != id ) {
283                 /* not found */
284                 return -1;
285
286         } else if ( --ids[0] == 0 ) {
287                 if( x != 1 ) {
288                         return -3;
289                 }
290
291         } else {
292                 AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
293         }
294
295 #if IDL_DEBUG > 1
296         idl_dump( ids );
297 #elif IDL_DEBUG > 0
298         idl_check( ids );
299 #endif
300
301         return 0;
302 }
303
304 static char *
305 bdb_show_key(
306         DBT             *key,
307         char            *buf )
308 {
309         if ( key->size == sizeof( ID ) ) {
310                 unsigned char *c = key->data;
311                 sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
312                 return buf;
313         } else {
314                 return key->data;
315         }
316 }
317
318 /* Find a db/key pair in the IDL cache. If ids is non-NULL,
319  * copy the cached IDL into it, otherwise just return the status.
320  */
321 int
322 bdb_idl_cache_get(
323         struct bdb_info *bdb,
324         DB                      *db,
325         DBT                     *key,
326         ID                      *ids )
327 {
328         bdb_idl_cache_entry_t idl_tmp;
329         bdb_idl_cache_entry_t *matched_idl_entry;
330
331         DBT2bv( key, &idl_tmp.kstr );
332         idl_tmp.db = db;
333         ldap_pvt_thread_rdwr_rlock( &bdb->bi_idl_tree_rwlock );
334         matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
335                                       bdb_idl_entry_cmp );
336         if ( matched_idl_entry != NULL ) {
337                 if ( matched_idl_entry->idl && ids )
338                         BDB_IDL_CPY( ids, matched_idl_entry->idl );
339                 ldap_pvt_thread_rdwr_runlock( &bdb->bi_idl_tree_rwlock );
340                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
341                 IDL_LRU_DELETE( bdb, matched_idl_entry );
342                 IDL_LRU_ADD( bdb, matched_idl_entry );
343                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
344                 if ( matched_idl_entry->idl )
345                         return LDAP_SUCCESS;
346                 else
347                         return DB_NOTFOUND;
348         }
349         ldap_pvt_thread_rdwr_runlock( &bdb->bi_idl_tree_rwlock );
350
351         return LDAP_NO_SUCH_OBJECT;
352 }
353
354 void
355 bdb_idl_cache_put(
356         struct bdb_info *bdb,
357         DB                      *db,
358         DBT                     *key,
359         ID                      *ids,
360         int                     rc )
361 {
362         bdb_idl_cache_entry_t idl_tmp;
363         bdb_idl_cache_entry_t *ee;
364
365         DBT2bv( key, &idl_tmp.kstr );
366
367         ee = (bdb_idl_cache_entry_t *) ch_malloc(
368                 sizeof( bdb_idl_cache_entry_t ) );
369         ee->db = db;
370         if ( rc == DB_NOTFOUND) {
371                 ee->idl = NULL;
372         } else {
373                 ee->idl = (ID*) ch_malloc( BDB_IDL_SIZEOF ( ids ) );
374                 BDB_IDL_CPY( ee->idl, ids );
375         }
376         ee->idl_lru_prev = NULL;
377         ee->idl_lru_next = NULL;
378         ber_dupbv( &ee->kstr, &idl_tmp.kstr );
379         ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
380         if ( avl_insert( &bdb->bi_idl_tree, (caddr_t) ee,
381                 bdb_idl_entry_cmp, avl_dup_error ))
382         {
383                 ch_free( ee->kstr.bv_val );
384                 ch_free( ee->idl );
385                 ch_free( ee );
386                 ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
387                 return;
388         }
389         ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
390         IDL_LRU_ADD( bdb, ee );
391         if ( ++bdb->bi_idl_cache_size > bdb->bi_idl_cache_max_size ) {
392                 int i = 0;
393                 while ( bdb->bi_idl_lru_tail != NULL && i < 10 ) {
394                         ee = bdb->bi_idl_lru_tail;
395                         if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) ee,
396                                     bdb_idl_entry_cmp ) == NULL ) {
397 #ifdef NEW_LOGGING
398                                 LDAP_LOG( INDEX, ERR, 
399                                         "bdb_idl_cache_put: AVL delete failed\n", 
400                                         0, 0, 0 );
401 #else
402                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_cache_put: "
403                                         "AVL delete failed\n",
404                                         0, 0, 0 );
405 #endif
406                         }
407                         IDL_LRU_DELETE( bdb, ee );
408                         i++;
409                         --bdb->bi_idl_cache_size;
410                         ch_free( ee->kstr.bv_val );
411                         ch_free( ee->idl );
412                         ch_free( ee );
413                 }
414         }
415
416         ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
417         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
418 }
419
420 void
421 bdb_idl_cache_del(
422         struct bdb_info *bdb,
423         DB                      *db,
424         DBT                     *key )
425 {
426         bdb_idl_cache_entry_t *matched_idl_entry, idl_tmp;
427         DBT2bv( key, &idl_tmp.kstr );
428         idl_tmp.db = db;
429         ldap_pvt_thread_rdwr_wlock( &bdb->bi_idl_tree_rwlock );
430         matched_idl_entry = avl_find( bdb->bi_idl_tree, &idl_tmp,
431                                       bdb_idl_entry_cmp );
432         if ( matched_idl_entry != NULL ) {
433                 if ( avl_delete( &bdb->bi_idl_tree, (caddr_t) matched_idl_entry,
434                                     bdb_idl_entry_cmp ) == NULL ) {
435 #ifdef NEW_LOGGING
436                         LDAP_LOG( INDEX, ERR, 
437                                 "bdb_idl_cache_del: AVL delete failed\n", 
438                                 0, 0, 0 );
439 #else
440                         Debug( LDAP_DEBUG_ANY, "=> bdb_idl_cache_del: "
441                                 "AVL delete failed\n",
442                                 0, 0, 0 );
443 #endif
444                 }
445                 --bdb->bi_idl_cache_size;
446                 ldap_pvt_thread_mutex_lock( &bdb->bi_idl_tree_lrulock );
447                 IDL_LRU_DELETE( bdb, matched_idl_entry );
448                 ldap_pvt_thread_mutex_unlock( &bdb->bi_idl_tree_lrulock );
449                 free( matched_idl_entry->kstr.bv_val );
450                 if ( matched_idl_entry->idl )
451                         free( matched_idl_entry->idl );
452                 free( matched_idl_entry );
453         }
454         ldap_pvt_thread_rdwr_wunlock( &bdb->bi_idl_tree_rwlock );
455 }
456
457 int
458 bdb_idl_fetch_key(
459         BackendDB       *be,
460         DB                      *db,
461         DB_TXN          *tid,
462         DBT                     *key,
463         ID                      *ids )
464 {
465         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
466         int rc;
467         DBT data;
468         DBC *cursor;
469         ID *i;
470         void *ptr;
471         size_t len;
472         int rc2;
473         int flags = bdb->bi_db_opflags | DB_MULTIPLE;
474
475         /* If using BerkeleyDB 4.0, the buf must be large enough to
476          * grab the entire IDL in one get(), otherwise BDB will leak
477          * resources on subsequent get's.  We can safely call get()
478          * twice - once for the data, and once to get the DB_NOTFOUND
479          * result meaning there's no more data. See ITS#2040 for details.
480          * This bug is fixed in BDB 4.1 so a smaller buffer will work if
481          * stack space is too limited.
482          *
483          * configure now requires Berkeley DB 4.1.
484          */
485 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR == 0)
486 #       define BDB_ENOUGH 5
487 #else
488 #       define BDB_ENOUGH 1
489 #endif
490         ID buf[BDB_IDL_DB_SIZE*BDB_ENOUGH];
491
492         char keybuf[16];
493
494 #ifdef NEW_LOGGING
495         LDAP_LOG( INDEX, ARGS,
496                 "bdb_idl_fetch_key: %s\n", 
497                 bdb_show_key( key, keybuf ), 0, 0 );
498 #else
499         Debug( LDAP_DEBUG_ARGS,
500                 "bdb_idl_fetch_key: %s\n", 
501                 bdb_show_key( key, keybuf ), 0, 0 );
502 #endif
503
504         assert( ids != NULL );
505
506         if ( bdb->bi_idl_cache_size ) {
507                 rc = bdb_idl_cache_get( bdb, db, key, ids );
508                 if ( rc != LDAP_NO_SUCH_OBJECT ) return rc;
509         }
510
511         DBTzero( &data );
512
513         data.data = buf;
514         data.ulen = sizeof(buf);
515         data.flags = DB_DBT_USERMEM;
516
517         if ( tid ) flags |= DB_RMW;
518
519         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
520         if( rc != 0 ) {
521 #ifdef NEW_LOGGING
522                 LDAP_LOG( INDEX, ERR, 
523                         "bdb_idl_fetch_key: cursor failed: %s (%d)\n", 
524                         db_strerror(rc), rc, 0 );
525 #else
526                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
527                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
528 #endif
529                 return rc;
530         }
531
532         rc = cursor->c_get( cursor, key, &data, flags | DB_SET );
533         if (rc == 0) {
534                 i = ids;
535                 while (rc == 0) {
536                         u_int8_t *j;
537
538                         DB_MULTIPLE_INIT( ptr, &data );
539                         while (ptr) {
540                                 DB_MULTIPLE_NEXT(ptr, &data, j, len);
541                                 if (j) {
542                                         ++i;
543                                         AC_MEMCPY( i, j, sizeof(ID) );
544                                 }
545                         }
546                         rc = cursor->c_get( cursor, key, &data, flags | DB_NEXT_DUP );
547                 }
548                 if ( rc == DB_NOTFOUND ) rc = 0;
549                 ids[0] = i - ids;
550                 /* On disk, a range is denoted by 0 in the first element */
551                 if (ids[1] == 0) {
552                         if (ids[0] != BDB_IDL_RANGE_SIZE) {
553 #ifdef NEW_LOGGING
554                                 LDAP_LOG( INDEX, ERR, 
555                                         "=> bdb_idl_fetch_key: range size mismatch: "
556                                         "expected %ld, got %ld\n", 
557                                         BDB_IDL_RANGE_SIZE, ids[0], 0 );
558 #else
559                                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
560                                         "range size mismatch: expected %d, got %ld\n",
561                                         BDB_IDL_RANGE_SIZE, ids[0], 0 );
562 #endif
563                                 cursor->c_close( cursor );
564                                 return -1;
565                         }
566                         BDB_IDL_RANGE( ids, ids[2], ids[3] );
567                 }
568                 data.size = BDB_IDL_SIZEOF(ids);
569         }
570
571         rc2 = cursor->c_close( cursor );
572         if (rc2) {
573 #ifdef NEW_LOGGING
574                 LDAP_LOG( INDEX, ERR, 
575                         "bdb_idl_fetch_key: close failed: %s (%d)\n", 
576                         db_strerror(rc2), rc2, 0 );
577 #else
578                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
579                         "close failed: %s (%d)\n", db_strerror(rc2), rc2, 0 );
580 #endif
581                 return rc2;
582         }
583
584         if ( rc == DB_NOTFOUND ) {
585                 /* no-op */
586         } else if( rc != 0 ) {
587 #ifdef NEW_LOGGING
588                 LDAP_LOG( INDEX, ERR, 
589                         "bdb_idl_fetch_key: get failed: %s (%d)\n", 
590                         db_strerror(rc), rc, 0 );
591 #else
592                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
593                         "get failed: %s (%d)\n",
594                         db_strerror(rc), rc, 0 );
595 #endif
596                 return rc;
597
598         } else if ( data.size == 0 || data.size % sizeof( ID ) ) {
599                 /* size not multiple of ID size */
600 #ifdef NEW_LOGGING
601                 LDAP_LOG( INDEX, ERR, 
602                         "bdb_idl_fetch_key: odd size: expected %ld multiple, got %ld\n", 
603                         (long) sizeof( ID ), (long) data.size, 0 );
604 #else
605                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
606                         "odd size: expected %ld multiple, got %ld\n",
607                         (long) sizeof( ID ), (long) data.size, 0 );
608 #endif
609                 return -1;
610
611         } else if ( data.size != BDB_IDL_SIZEOF(ids) ) {
612                 /* size mismatch */
613 #ifdef NEW_LOGGING
614                 LDAP_LOG( INDEX, ERR, 
615                         "bdb_idl_fetch_key: get size mismatch: expected %ld, got %ld\n", 
616                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
617 #else
618                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_fetch_key: "
619                         "get size mismatch: expected %ld, got %ld\n",
620                         (long) ((1 + ids[0]) * sizeof( ID )), (long) data.size, 0 );
621 #endif
622                 return -1;
623         }
624
625         if ( bdb->bi_idl_cache_max_size ) {
626                 bdb_idl_cache_put( bdb, db, key, ids, rc );
627         }
628
629         return rc;
630 }
631
632
633 int
634 bdb_idl_insert_key(
635         BackendDB       *be,
636         DB                      *db,
637         DB_TXN          *tid,
638         DBT                     *key,
639         ID                      id )
640 {
641         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
642         int     rc;
643         DBT data;
644         ID lo, hi, tmp, idl[BDB_IDL_DB_SIZE];
645         char *err;
646         int wasrange = 0, isrange = 0;
647
648         {
649                 char buf[16];
650 #ifdef NEW_LOGGING
651                 LDAP_LOG( INDEX, ARGS,
652                         "bdb_idl_insert_key: %lx %s\n", 
653                         (long) id, bdb_show_key( key, buf ), 0 );
654 #else
655                 Debug( LDAP_DEBUG_ARGS,
656                         "bdb_idl_insert_key: %lx %s\n", 
657                         (long) id, bdb_show_key( key, buf ), 0 );
658 #endif
659         }
660
661         assert( id != NOID );
662
663         BDB_IDL_ZERO( idl );
664         rc = bdb_idl_fetch_key( be, db, tid, key, idl );
665         if ( rc != DB_NOTFOUND ) {
666                 wasrange = BDB_IDL_IS_RANGE( idl );
667                 if ( wasrange ) {
668                         lo = idl[1];
669                         hi = idl[2];
670                 }
671                 rc = bdb_idl_insert( idl, id );
672
673                 /* Don't need to do anything */
674                 if ( rc == -1 ) return 0;
675
676                 isrange = BDB_IDL_IS_RANGE( idl );
677         }
678
679         DBTzero( &data );
680         data.size = sizeof( ID );
681         data.ulen = data.size;
682         data.flags = DB_DBT_USERMEM;
683         data.data = &tmp;
684
685         if ( isrange ) {
686                 while ( !wasrange ) {
687                         /* Delete vector, rewrite as range */
688                         rc = db->del( db, tid, key, 0 );
689                         if ( rc != 0 ) {
690                                 err = "del";
691                                 break;
692                         }
693                         tmp = 0;
694                         rc = db->put( db, tid, key, &data, 0 );
695                         if ( rc != 0 ) {
696                                 err = "put1";
697                                 break;
698                         }
699                         tmp = idl[1];
700                         rc = db->put( db, tid, key, &data, 0 );
701                         if ( rc != 0 ) {
702                                 err = "put2";
703                                 break;
704                         }
705                         tmp = idl[2];
706                         rc = db->put( db, tid, key, &data, 0 );
707                         if ( rc != 0 ) {
708                                 err = "put3";
709                                 break;
710                         }
711                         break;
712                 }
713                 while( wasrange ) {
714                         DBC *cursor;
715                         /* Update range boundaries */
716                         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
717                         if ( rc != 0 ) {
718                                 err = "cursor";
719                                 break;
720                         }
721                         data.data = &tmp;
722
723                         tmp = (id == idl[1]) ? lo : hi;
724                         rc = cursor->c_get( cursor, key, &data, DB_GET_BOTH );
725                         if ( rc != 0 ) {
726                                 cursor->c_close( cursor );
727                                 err = "c_get";
728                                 break;
729                         }
730                         rc = cursor->c_del( cursor, 0 );
731                         if ( rc != 0 ) {
732                                 cursor->c_close( cursor );
733                                 err = "c_del";
734                                 break;
735                         }
736                         tmp = id;
737                         rc = cursor->c_put( cursor, key, &data, DB_CURRENT );
738                         if ( rc != 0 ) {
739                                 cursor->c_close( cursor );
740                                 err = "c_put";
741                                 break;
742                         }
743                         rc = cursor->c_close( cursor );
744                         if ( rc != 0 ) {
745                                 err = "c_close";
746                                 break;
747                         }
748                         break;
749                 }
750         } else {
751                 tmp = id;
752                 rc = db->put( db, tid, key, &data, DB_NODUPDATA );
753                 if ( rc != 0 ) {
754                         err = "put4";
755                 }
756         }
757         if ( rc && rc != DB_KEYEXIST ) {
758 #ifdef NEW_LOGGING
759                 LDAP_LOG( INDEX, ERR, 
760                         "bdb_idl_insert_key: %s failed: %s (%d)\n", 
761                         err, db_strerror(rc), rc );
762 #else
763                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_insert_key: "
764                         "%s failed: %s (%d)\n", err, db_strerror(rc), rc );
765 #endif
766                 return rc;
767         }
768
769         if ( bdb->bi_idl_cache_max_size ) {
770                 bdb_idl_cache_put( bdb, db, key, idl, 0 );
771         }
772         return rc;
773 }
774
775 int
776 bdb_idl_delete_key(
777         BackendDB       *be,
778         DB                      *db,
779         DB_TXN          *tid,
780         DBT                     *key,
781         ID                      id )
782 {
783         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
784         int     rc;
785         DBT data;
786         DBC *cursor;
787         ID lo, hi, tmp, idl[BDB_IDL_DB_SIZE];
788         int wasrange, isrange;
789         char *err;
790
791         {
792                 char buf[16];
793 #ifdef NEW_LOGGING
794                 LDAP_LOG( INDEX, ARGS,
795                         "bdb_idl_delete_key: %lx %s\n", 
796                         (long) id, bdb_show_key( key, buf ), 0 );
797 #else
798                 Debug( LDAP_DEBUG_ARGS,
799                         "bdb_idl_delete_key: %lx %s\n", 
800                         (long) id, bdb_show_key( key, buf ), 0 );
801 #endif
802         }
803         assert( id != NOID );
804
805         BDB_IDL_ZERO( idl );
806         rc = bdb_idl_fetch_key( be, db, tid, key, idl );
807         if ( rc != DB_NOTFOUND ) {
808                 wasrange = BDB_IDL_IS_RANGE( idl );
809                 if ( wasrange ) {
810                         lo = idl[1];
811                         hi = idl[2];
812                 }
813                 rc = bdb_idl_delete( idl, id );
814
815                 /* Don't need to do anything */
816                 if ( rc == -1 ) return 0;
817
818                 isrange = BDB_IDL_IS_RANGE( idl );
819         }
820
821         DBTzero( &data );
822         data.data = &tmp;
823         data.size = sizeof( id );
824         data.ulen = data.size;
825         data.flags = DB_DBT_USERMEM;
826
827         rc = db->cursor( db, tid, &cursor, bdb->bi_db_opflags );
828         if ( rc != 0 ) {
829 #ifdef NEW_LOGGING
830                 LDAP_LOG( INDEX, ERR, 
831                         "bdb_idl_delete_key: cursor failed: %s (%d)\n", 
832                         db_strerror(rc), rc, 0 );
833 #else
834                 Debug( LDAP_DEBUG_ANY, "=> bdb_idl_delete_key: "
835                         "cursor failed: %s (%d)\n", db_strerror(rc), rc, 0 );
836 #endif
837                 return rc;
838         }
839
840         if ( wasrange && !isrange ) {
841                 rc = db->del( db, tid, key, 0 );
842                 if ( rc != 0 ) {
843                         err = "del";
844                 } else {
845                         tmp = idl[1];
846                         rc = db->put( db, tid, key, &data, 0 );
847                         if ( rc != 0 ) {
848                                 err = "put";
849                         }
850                 }
851         } else {
852                 tmp = id;
853                 rc = cursor->c_get( cursor, key, &data, DB_GET_BOTH );
854                 if ( rc != 0 ) {
855                         err = "c_get";
856                 } else {
857                         rc = cursor->c_del( cursor, 0 );
858                         if ( rc != 0 ) {
859                                 err = "c_del";
860                         }
861                 }
862         }
863         if ( isrange && rc == 0 ) {
864                 tmp = ( id == lo ) ? idl[1] : idl[2];
865                 rc = cursor->c_put( cursor, key, &data, DB_CURRENT );
866                 if ( rc != 0 ) {
867                         err = "c_put";
868                 }
869         }
870         if ( rc && 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                 cursor->c_close( cursor );
880                 return rc;
881         }
882         rc = cursor->c_close( cursor );
883         if( rc != 0 ) {
884 #ifdef NEW_LOGGING
885                 LDAP_LOG( INDEX, ERR, "bdb_idl_delete_key: c_close failed: %s (%d)\n", 
886                         db_strerror(rc), rc, 0 );
887 #else
888                 Debug( LDAP_DEBUG_ANY,
889                         "=> bdb_idl_delete_key: c_close failed: %s (%d)\n",
890                         db_strerror(rc), rc, 0 );
891 #endif
892                 return rc;
893         }
894         if ( bdb->bi_idl_cache_max_size ) {
895                 bdb_idl_cache_put( bdb, db, key, idl, 0 );
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