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