]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/idl.c
New dn2id format with base/one/subtree indices (ldbm/bdb2)
[openldap] / servers / slapd / back-ldbm / idl.c
1 /* idl.c - ldap id list handling routines */
2 /*
3  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <stdio.h>
10
11 #include <ac/string.h>
12 #include <ac/socket.h>
13
14 #include "ldap_defaults.h"
15 #include "slap.h"
16 #include "back-ldbm.h"
17
18 static ID_BLOCK* idl_dup( ID_BLOCK *idl );
19
20 /* Allocate an ID_BLOCK with room for nids ids */
21 ID_BLOCK *
22 idl_alloc( unsigned int nids )
23 {
24         ID_BLOCK        *new;
25
26         /* nmax + nids + space for the ids */
27         new = (ID_BLOCK *) ch_calloc( (ID_BLOCK_IDS_OFFSET + nids), sizeof(ID) );
28         ID_BLOCK_NMAX(new) = nids;
29         ID_BLOCK_NIDS(new) = 0;
30
31         return( new );
32 }
33
34
35 /* Allocate an empty ALLIDS ID_BLOCK */
36 ID_BLOCK        *
37 idl_allids( Backend *be )
38 {
39         ID_BLOCK        *idl;
40
41         idl = idl_alloc( 0 );
42         ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;
43         ID_BLOCK_NIDS(idl) = next_id_get( be );
44
45         return( idl );
46 }
47
48 /* Free an ID_BLOCK */
49 void
50 idl_free( ID_BLOCK *idl )
51 {
52         if ( idl == NULL ) {
53                 Debug( LDAP_DEBUG_TRACE,
54                         "idl_free: called with NULL pointer\n",
55                         0, 0, 0 );
56                 return;
57         }
58
59         free( (char *) idl );
60 }
61
62
63 /* Fetch an single ID_BLOCK from the cache */
64 static ID_BLOCK *
65 idl_fetch_one(
66     Backend             *be,
67     DBCache     *db,
68     Datum               key
69 )
70 {
71         Datum   data;
72         ID_BLOCK        *idl;
73
74         /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
75
76         data = ldbm_cache_fetch( db, key );
77
78         if( data.dptr == NULL ) {
79                 return NULL;
80         }
81
82         idl = idl_dup( (ID_BLOCK *) data.dptr);
83         ldbm_datum_free( db->dbc_db, data );
84
85         return( idl );
86 }
87
88
89 /* Fetch a set of ID_BLOCKs from the cache
90  *      if not INDIRECT
91  *              if block return is an ALLIDS block,
92  *                      return an new ALLIDS block
93  *              otherwise
94  *                      return block
95  *      construct super block from all blocks referenced by INDIRECT block
96  *      return super block
97  */
98 ID_BLOCK *
99 idl_fetch(
100     Backend             *be,
101     DBCache     *db,
102     Datum               key
103 )
104 {
105         Datum   data;
106         ID_BLOCK        *idl;
107         ID_BLOCK        **tmp;
108         char    *kstr;
109         int     i, nids;
110
111         idl = idl_fetch_one( be, db, key );
112
113         if ( idl == NULL ) {
114                 return NULL;
115         }
116
117         if ( ID_BLOCK_ALLIDS(idl) ) {
118                 /* all ids block */
119                 /* make sure we have the current value of highest id */
120                 idl_free( idl );
121                 idl = idl_allids( be );
122
123                 return( idl );
124         }
125
126         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
127                 /* regular block */
128                 return( idl );
129         }
130
131         /*
132          * this is an indirect block which points to other blocks.
133          * we need to read in all the blocks it points to and construct
134          * a big id list containing all the ids, which we will return.
135          */
136
137         /* count the number of blocks & allocate space for pointers to them */
138         for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ )
139                 ;       /* NULL */
140         tmp = (ID_BLOCK **) ch_malloc( (i + 1) * sizeof(ID_BLOCK *) );
141
142         /* read in all the blocks */
143         kstr = (char *) ch_malloc( key.dsize + CONT_SIZE );
144         nids = 0;
145         for ( i = 0; !ID_BLOCK_NOID(idl, i); i++ ) {
146                 ldbm_datum_init( data );
147
148                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
149                         ID_BLOCK_ID(idl, i), key.dptr );
150
151                 data.dptr = kstr;
152                 data.dsize = strlen( kstr ) + 1;
153
154                 if ( (tmp[i] = idl_fetch_one( be, db, data )) == NULL ) {
155                         Debug( LDAP_DEBUG_ANY,
156                             "idl_fetch of (%s) returns NULL\n", data.dptr, 0, 0 );
157                         continue;
158                 }
159
160                 nids += ID_BLOCK_NIDS(tmp[i]);
161         }
162         tmp[i] = NULL;
163         free( kstr );
164         idl_free( idl );
165
166         /* allocate space for the big block */
167         idl = idl_alloc( nids );
168         ID_BLOCK_NIDS(idl) = nids;
169         nids = 0;
170
171         /* copy in all the ids from the component blocks */
172         for ( i = 0; tmp[i] != NULL; i++ ) {
173                 if ( tmp[i] == NULL ) {
174                         continue;
175                 }
176
177                 SAFEMEMCPY(
178                         (char *) &ID_BLOCK_ID(idl, nids),
179                         (char *) &ID_BLOCK_ID(tmp[i], 0),
180                         ID_BLOCK_NIDS(tmp[i]) * sizeof(ID) );
181                 nids += ID_BLOCK_NIDS(tmp[i]);
182
183                 idl_free( tmp[i] );
184         }
185         free( (char *) tmp );
186
187         Debug( LDAP_DEBUG_TRACE, "<= idl_fetch %ld ids (%ld max)\n",
188                ID_BLOCK_NIDS(idl), ID_BLOCK_NMAX(idl), 0 );
189         return( idl );
190 }
191
192
193 /* store a single block */
194 static int
195 idl_store(
196     Backend             *be,
197     DBCache     *db,
198     Datum               key, 
199     ID_BLOCK            *idl
200 )
201 {
202         int     rc, flags;
203         Datum   data;
204         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
205
206         ldbm_datum_init( data );
207
208         /* Debug( LDAP_DEBUG_TRACE, "=> idl_store\n", 0, 0, 0 ); */
209
210         data.dptr = (char *) idl;
211         data.dsize = (ID_BLOCK_IDS_OFFSET + ID_BLOCK_NMAX(idl)) * sizeof(ID);
212         
213 #ifdef LDBM_DEBUG
214         Statslog( LDAP_DEBUG_STATS, "<= idl_store(): rc=%d\n",
215                 rc, 0, 0, 0, 0 );
216 #endif
217
218         flags = LDBM_REPLACE;
219         if( li->li_dbcachewsync ) flags |= LDBM_SYNC;
220         rc = ldbm_cache_store( db, key, data, flags );
221
222         /* Debug( LDAP_DEBUG_TRACE, "<= idl_store %d\n", rc, 0, 0 ); */
223         return( rc );
224 }
225
226 /* split the block at id 
227  *      locate ID greater than or equal to id.
228  */
229 static void
230 idl_split_block(
231     ID_BLOCK    *b,
232     ID          id,
233     ID_BLOCK    **right,
234     ID_BLOCK    **left
235 )
236 {
237         unsigned int    nr, nl;
238
239         /* find where to split the block *//* XXX linear search XXX */
240         for ( nr = 0; nr < ID_BLOCK_NIDS(b) && id > ID_BLOCK_ID(b, nr); nr++ )
241                 ;       /* NULL */
242
243         nl = ID_BLOCK_NIDS(b) - nr;
244
245         *right = idl_alloc( nr == 0 ? 1 : nr );
246         *left = idl_alloc( nl + (nr == 0 ? 0 : 1));
247
248         /*
249          * everything before the id being inserted in the first block
250          * unless there is nothing, in which case the id being inserted
251          * goes there.
252          */
253         if ( nr == 0 ) {
254                 ID_BLOCK_NIDS(*right) = 1;
255                 ID_BLOCK_ID(*right, 0) = id;
256         } else {
257                 SAFEMEMCPY(
258                         (char *) &ID_BLOCK_ID(*right, 0),
259                         (char *) &ID_BLOCK_ID(b, 0),
260                         nr * sizeof(ID) );
261                 ID_BLOCK_NIDS(*right) = nr;
262                 ID_BLOCK_ID(*left, 0) = id;
263         }
264
265         /* the id being inserted & everything after in the second block */
266         SAFEMEMCPY(
267                 (char *) &ID_BLOCK_ID(*left, (nr == 0 ? 0 : 1)),
268             (char *) &ID_BLOCK_ID(b, nr),
269                 nl * sizeof(ID) );
270         ID_BLOCK_NIDS(*left) = nl + (nr == 0 ? 0 : 1);
271 }
272
273
274 /*
275  * idl_change_first - called when an indirect block's first key has
276  * changed, meaning it needs to be stored under a new key, and the
277  * header block pointing to it needs updating.
278  */
279 static int
280 idl_change_first(
281     Backend             *be,
282     DBCache     *db,
283     Datum               hkey,           /* header block key     */
284     ID_BLOCK            *h,             /* header block         */
285     int                 pos,            /* pos in h to update   */
286     Datum               bkey,           /* data block key       */
287     ID_BLOCK            *b              /* data block           */
288 )
289 {
290         int     rc;
291
292         /* Debug( LDAP_DEBUG_TRACE, "=> idl_change_first\n", 0, 0, 0 ); */
293
294         /* delete old key block */
295         if ( (rc = ldbm_cache_delete( db, bkey )) != 0 ) {
296                 Debug( LDAP_DEBUG_ANY,
297                     "ldbm_delete of (%s) returns %d\n", bkey.dptr, rc,
298                     0 );
299                 return( rc );
300         }
301
302         /* write block with new key */
303         sprintf( bkey.dptr, "%c%ld%s", CONT_PREFIX,
304                 ID_BLOCK_ID(b, 0), hkey.dptr );
305
306         bkey.dsize = strlen( bkey.dptr ) + 1;
307         if ( (rc = idl_store( be, db, bkey, b )) != 0 ) {
308                 Debug( LDAP_DEBUG_ANY,
309                     "idl_store of (%s) returns %d\n", bkey.dptr, rc, 0 );
310                 return( rc );
311         }
312
313         /* update + write indirect header block */
314         ID_BLOCK_ID(h, pos) = ID_BLOCK_ID(b, 0);
315         if ( (rc = idl_store( be, db, hkey, h )) != 0 ) {
316                 Debug( LDAP_DEBUG_ANY,
317                     "idl_store of (%s) returns %d\n", hkey.dptr, rc, 0 );
318                 return( rc );
319         }
320
321         return( 0 );
322 }
323
324
325 int
326 idl_insert_key(
327     Backend             *be,
328     DBCache     *db,
329     Datum               key,
330     ID                  id
331 )
332 {
333         int     i, j, first, rc;
334         ID_BLOCK        *idl, *tmp, *tmp2, *tmp3;
335         char    *kstr;
336         Datum   k2;
337
338         ldbm_datum_init( k2 );
339
340         if ( (idl = idl_fetch_one( be, db, key )) == NULL ) {
341 #ifdef LDBM_DEBUG
342                 Statslog( LDAP_DEBUG_STATS, "=> idl_insert_key(): no key yet\n",
343                         0, 0, 0, 0, 0 );
344 #endif
345
346                 idl = idl_alloc( 1 );
347                 ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)++) = id;
348                 rc = idl_store( be, db, key, idl );
349
350                 idl_free( idl );
351                 return( rc );
352         }
353
354         if ( ID_BLOCK_ALLIDS( idl ) ) {
355                 /* ALLIDS */
356                 idl_free( idl );
357                 return 0;
358         }
359
360         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
361                 /* regular block */
362                 switch ( idl_insert( &idl, id, db->dbc_maxids ) ) {
363                 case 0:         /* id inserted - store the updated block */
364                 case 1:
365                         rc = idl_store( be, db, key, idl );
366                         break;
367
368                 case 2:         /* id already there - nothing to do */
369                         rc = 0;
370                         break;
371
372                 case 3:         /* id not inserted - block must be split */
373                         /* check threshold for marking this an all-id block */
374                         if ( db->dbc_maxindirect < 2 ) {
375                                 idl_free( idl );
376                                 idl = idl_allids( be );
377                                 rc = idl_store( be, db, key, idl );
378                                 break;
379                         }
380
381                         idl_split_block( idl, id, &tmp, &tmp2 );
382                         idl_free( idl );
383
384                         /* create the header indirect block */
385                         idl = idl_alloc( 3 );
386                         ID_BLOCK_NMAX(idl) = 3;
387                         ID_BLOCK_NIDS(idl) = ID_BLOCK_INDIRECT_VALUE;
388                         ID_BLOCK_ID(idl, 0) = ID_BLOCK_ID(tmp, 0);
389                         ID_BLOCK_ID(idl, 1) = ID_BLOCK_ID(tmp2, 0);
390                         ID_BLOCK_ID(idl, 2) = NOID;
391
392                         /* store it */
393                         rc = idl_store( be, db, key, idl );
394
395                         /* store the first id block */
396                         kstr = (char *) ch_malloc( key.dsize + CONT_SIZE );
397                         sprintf( kstr, "%c%ld%s", CONT_PREFIX,
398                                 ID_BLOCK_ID(tmp, 0), key.dptr );
399
400                         k2.dptr = kstr;
401                         k2.dsize = strlen( kstr ) + 1;
402                         rc = idl_store( be, db, k2, tmp );
403
404                         /* store the second id block */
405                         sprintf( kstr, "%c%ld%s", CONT_PREFIX,
406                                 ID_BLOCK_ID(tmp2, 0), key.dptr );
407                         k2.dptr = kstr;
408                         k2.dsize = strlen( kstr ) + 1;
409                         rc = idl_store( be, db, k2, tmp2 );
410
411                         free( kstr );
412                         idl_free( tmp );
413                         idl_free( tmp2 );
414                         break;
415                 }
416
417                 idl_free( idl );
418                 return( rc );
419         }
420
421         /*
422          * this is an indirect block which points to other blocks.
423          * we need to read in the block into which the id should be
424          * inserted, then insert the id and store the block.  we might
425          * have to split the block if it is full, which means we also
426          * need to write a new "header" block.
427          */
428
429         /* select the block to try inserting into *//* XXX linear search XXX */
430         for ( i = 0; !ID_BLOCK_NOID(idl, i) && id > ID_BLOCK_ID(idl, i); i++ )
431                 ;       /* NULL */
432         if ( i != 0 ) {
433                 i--;
434                 first = 0;
435         } else {
436                 first = 1;
437         }
438
439         /* get the block */
440         kstr = (char *) ch_malloc( key.dsize + CONT_SIZE );
441         sprintf( kstr, "%c%ld%s", CONT_PREFIX,
442                 ID_BLOCK_ID(idl, i), key.dptr );
443         k2.dptr = kstr;
444         k2.dsize = strlen( kstr ) + 1;
445         if ( (tmp = idl_fetch_one( be, db, k2 )) == NULL ) {
446                 Debug( LDAP_DEBUG_ANY, "nonexistent continuation block (%s)\n",
447                     k2.dptr, 0, 0 );
448                 free( kstr );
449                 idl_free( idl );
450                 return( -1 );
451         }
452
453         /* insert the id */
454         switch ( idl_insert( &tmp, id, db->dbc_maxids ) ) {
455         case 0:         /* id inserted ok */
456                 if ( (rc = idl_store( be, db, k2, tmp )) != 0 ) {
457                         Debug( LDAP_DEBUG_ANY,
458                             "idl_store of (%s) returns %d\n", k2.dptr, rc, 0 );
459                 }
460                 break;
461
462         case 1:         /* id inserted - first id in block has changed */
463                 /*
464                  * key for this block has changed, so we have to
465                  * write the block under the new key, delete the
466                  * old key block + update and write the indirect
467                  * header block.
468                  */
469
470                 rc = idl_change_first( be, db, key, idl, i, k2, tmp );
471                 break;
472
473         case 2:         /* id not inserted - already there */
474                 break;
475
476         case 3:         /* id not inserted - block is full */
477                 /*
478                  * first, see if it will fit in the next block,
479                  * without splitting, unless we're trying to insert
480                  * into the beginning of the first block.
481                  */
482
483                 /* is there a next block? */
484                 if ( !first && !ID_BLOCK_NOID(idl, i + 1) ) {
485                         /* read it in */
486                         sprintf( kstr, "%c%ld%s", CONT_PREFIX,
487                                 ID_BLOCK_ID(idl, i + 1), key.dptr );
488                         k2.dptr = kstr;
489                         k2.dsize = strlen( kstr ) + 1;
490                         if ( (tmp2 = idl_fetch_one( be, db, k2 )) == NULL ) {
491                                 Debug( LDAP_DEBUG_ANY,
492                                     "idl_fetch_one (%s) returns NULL\n",
493                                     k2.dptr, 0, 0 );
494                                 break;
495                         }
496
497                         switch ( (rc = idl_insert( &tmp2, id,
498                             db->dbc_maxids )) ) {
499                         case 1:         /* id inserted first in block */
500                                 rc = idl_change_first( be, db, key, idl,
501                                     i + 1, k2, tmp2 );
502                                 /* FALL */
503
504                         case 2:         /* id already there - how? */
505                         case 0:         /* id inserted */
506                                 if ( rc == 2 ) {
507                                         Debug( LDAP_DEBUG_ANY,
508                                             "id %ld already in next block\n",
509                                             id, 0, 0 );
510                                 }
511                                 free( kstr );
512                                 idl_free( tmp );
513                                 idl_free( tmp2 );
514                                 idl_free( idl );
515                                 return( 0 );
516
517                         case 3:         /* split the original block */
518                                 break;
519                         }
520                         idl_free( tmp2 );
521                 }
522
523                 /*
524                  * must split the block, write both new blocks + update
525                  * and write the indirect header block.
526                  */
527
528                 /* count how many indirect blocks *//* XXX linear count XXX */
529                 for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ )
530                         ;       /* NULL */
531
532                 /* check it against all-id thresholed */
533                 if ( j + 1 > db->dbc_maxindirect ) {
534                         /*
535                          * we've passed the all-id threshold, meaning
536                          * that this set of blocks should be replaced
537                          * by a single "all-id" block.  our job: delete
538                          * all the indirect blocks, and replace the header
539                          * block by an all-id block.
540                          */
541
542                         /* delete all indirect blocks */
543                         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) {
544                                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
545                                         ID_BLOCK_ID(idl, j), key.dptr );
546                                 k2.dptr = kstr;
547                                 k2.dsize = strlen( kstr ) + 1;
548
549                                 rc = ldbm_cache_delete( db, k2 );
550                         }
551
552                         /* store allid block in place of header block */
553                         idl_free( idl );
554                         idl = idl_allids( be );
555                         rc = idl_store( be, db, key, idl );
556
557                         free( kstr );
558                         idl_free( idl );
559                         idl_free( tmp );
560                         return( rc );
561                 }
562
563                 idl_split_block( tmp, id, &tmp2, &tmp3 );
564                 idl_free( tmp );
565
566                 /* create a new updated indirect header block */
567                 tmp = idl_alloc( ID_BLOCK_NMAX(idl) + 1 );
568                 ID_BLOCK_NIDS(tmp) = ID_BLOCK_INDIRECT_VALUE;
569                 /* everything up to the split block */
570                 SAFEMEMCPY(
571                         (char *) &ID_BLOCK_ID(tmp, 0),
572                         (char *) &ID_BLOCK_ID(idl, 0),
573                     i * sizeof(ID) );
574                 /* the two new blocks */
575                 ID_BLOCK_ID(tmp, i) = ID_BLOCK_ID(tmp2, 0);
576                 ID_BLOCK_ID(tmp, i + 1) = ID_BLOCK_ID(tmp3, 0);
577                 /* everything after the split block */
578                 SAFEMEMCPY(
579                         (char *) &ID_BLOCK_ID(tmp, i + 2),
580                         (char *) &ID_BLOCK_ID(idl, i + 1),
581                         (ID_BLOCK_NMAX(idl) - i - 1) * sizeof(ID) );
582
583                 /* store the header block */
584                 rc = idl_store( be, db, key, tmp );
585
586                 /* store the first id block */
587                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
588                         ID_BLOCK_ID(tmp2, 0), key.dptr );
589                 k2.dptr = kstr;
590                 k2.dsize = strlen( kstr ) + 1;
591                 rc = idl_store( be, db, k2, tmp2 );
592
593                 /* store the second id block */
594                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
595                         ID_BLOCK_ID(tmp3, 0), key.dptr );
596                 k2.dptr = kstr;
597                 k2.dsize = strlen( kstr ) + 1;
598                 rc = idl_store( be, db, k2, tmp3 );
599
600                 idl_free( tmp2 );
601                 idl_free( tmp3 );
602                 break;
603         }
604
605         free( kstr );
606         idl_free( tmp );
607         idl_free( idl );
608         return( rc );
609 }
610
611
612 /*
613  * idl_insert - insert an id into an id list.
614  *
615  *      returns
616  *              0       id inserted
617  *              1       id inserted, first id in block has changed
618  *              2       id not inserted, already there
619  *              3       id not inserted, block must be split
620  */
621 int
622 idl_insert( ID_BLOCK **idl, ID id, unsigned int maxids )
623 {
624         unsigned int    i;
625
626         if ( ID_BLOCK_ALLIDS( *idl ) ) {
627                 return( 2 );    /* already there */
628         }
629
630         /* is it already there? *//* XXX linear search XXX */
631         for ( i = 0; i < ID_BLOCK_NIDS(*idl) && id > ID_BLOCK_ID(*idl, i); i++ ) {
632                 ;       /* NULL */
633         }
634         if ( i < ID_BLOCK_NIDS(*idl) && ID_BLOCK_ID(*idl, i) == id ) {
635                 return( 2 );    /* already there */
636         }
637
638         /* do we need to make room for it? */
639         if ( ID_BLOCK_NIDS(*idl) == ID_BLOCK_NMAX(*idl) ) {
640                 /* make room or indicate block needs splitting */
641                 if ( ID_BLOCK_NMAX(*idl) >= maxids ) {
642                         return( 3 );    /* block needs splitting */
643                 }
644
645                 ID_BLOCK_NMAX(*idl) *= 2;
646                 if ( ID_BLOCK_NMAX(*idl) > maxids ) {
647                         ID_BLOCK_NMAX(*idl) = maxids;
648                 }
649                 *idl = (ID_BLOCK *) ch_realloc( (char *) *idl,
650                     (ID_BLOCK_NMAX(*idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
651         }
652
653         /* make a slot for the new id */
654         SAFEMEMCPY( &ID_BLOCK_ID(*idl, i), &ID_BLOCK_ID(*idl, i+1), 
655                 ID_BLOCK_NIDS(*idl) - i );
656
657         ID_BLOCK_ID(*idl, i) = id;
658         ID_BLOCK_NIDS(*idl)++;
659         (void) memset(
660                 (char *) &ID_BLOCK_ID((*idl), ID_BLOCK_NIDS(*idl)),
661                 '\0',
662             (ID_BLOCK_NMAX(*idl) - ID_BLOCK_NIDS(*idl)) * sizeof(ID) );
663
664         return( i == 0 ? 1 : 0 );       /* inserted - first id changed or not */
665 }
666
667
668 int
669 idl_delete_key (
670         Backend         *be,
671         DBCache  *db,
672         Datum           key,
673         ID              id
674 )
675 {
676         Datum  data;
677         ID_BLOCK *idl, *tmp;
678         unsigned i;
679         int j, nids;
680         char    *kstr;
681
682         if ( (idl = idl_fetch_one( be, db, key ) ) == NULL )
683         {
684                 /* It wasn't found.  Hmm... */
685                 return -1;
686         }
687
688         if ( ID_BLOCK_ALLIDS( idl ) ) {
689                 idl_free( idl );
690                 return 0;
691         }
692
693         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
694                 for ( i=0; i < ID_BLOCK_NIDS(idl); i++ ) {
695                         if ( ID_BLOCK_ID(idl, i) == id ) {
696                                 if( --ID_BLOCK_NIDS(idl) == 0 ) {
697                                         ldbm_cache_delete( db, key );
698
699                                 } else {
700                                         SAFEMEMCPY (
701                                                 &ID_BLOCK_ID(idl, i),
702                                                 &ID_BLOCK_ID(idl, i+1),
703                                                 (ID_BLOCK_NIDS(idl)-i) * sizeof(ID) );
704
705                                         ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)) = NOID;
706
707                                         idl_store( be, db, key, idl );
708                                 }
709
710                                 idl_free( idl );
711                                 return 0;
712                         }
713                         /*  We didn't find the ID.  Hmmm... */
714                 }
715                 idl_free( idl );
716                 return -1;
717         }
718         
719         /* We have to go through an indirect block and find the ID
720            in the list of IDL's
721            */
722         for ( nids = 0; !ID_BLOCK_NOID(idl, nids); nids++ )
723                 ;       /* NULL */
724         kstr = (char *) ch_malloc( key.dsize + CONT_SIZE );
725
726         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) 
727         {
728                 ldbm_datum_init( data );
729                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
730                         ID_BLOCK_ID(idl, j), key.dptr );
731                 data.dptr = kstr;
732                 data.dsize = strlen( kstr ) + 1;
733
734                 if ( (tmp = idl_fetch_one( be, db, data )) == NULL ) {
735                         Debug( LDAP_DEBUG_ANY,
736                             "idl_fetch of (%s) returns NULL\n", data.dptr, 0, 0 );
737                         continue;
738                 }
739                 /*
740                    Now try to find the ID in tmp
741                 */
742                 for ( i=0; i < ID_BLOCK_NIDS(tmp); i++ )
743                 {
744                         if ( ID_BLOCK_ID(tmp, i) == id )
745                         {
746                                 SAFEMEMCPY(
747                                         &ID_BLOCK_ID(tmp, i),
748                                         &ID_BLOCK_ID(tmp, i+1),
749                                         (ID_BLOCK_NIDS(tmp)-(i+1)) * sizeof(ID));
750                                 ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp)-1 ) = NOID;
751                                 ID_BLOCK_NIDS(tmp)--;
752
753                                 if ( ID_BLOCK_NIDS(tmp) ) {
754                                         idl_store ( be, db, data, tmp );
755
756                                 } else {
757                                         ldbm_cache_delete( db, data );
758                                         SAFEMEMCPY(
759                                                 &ID_BLOCK_ID(idl, j),
760                                                 &ID_BLOCK_ID(idl, j+1),
761                                                 (nids-(j+1)) * sizeof(ID));
762                                         ID_BLOCK_ID(idl, nids-1) = NOID;
763                                         nids--;
764                                         if ( ! nids )
765                                                 ldbm_cache_delete( db, key );
766                                         else
767                                                 idl_store( be, db, key, idl );
768                                 }
769                                 idl_free( tmp );
770                                 free( kstr );
771                                 idl_free( idl );
772                                 return 0;
773                         }
774                 }
775                 idl_free( tmp );
776         }
777         free( kstr );
778         idl_free( idl );
779         return -1;
780 }
781
782
783 /* return a duplicate of a single ID_BLOCK */
784 static ID_BLOCK *
785 idl_dup( ID_BLOCK *idl )
786 {
787         ID_BLOCK        *new;
788
789         if ( idl == NULL ) {
790                 return( NULL );
791         }
792
793         new = idl_alloc( ID_BLOCK_NMAX(idl) );
794
795         SAFEMEMCPY(
796                 (char *) new,
797                 (char *) idl,
798                 (ID_BLOCK_NMAX(idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
799
800         return( new );
801 }
802
803
804 /* return the smaller ID_BLOCK */
805 static ID_BLOCK *
806 idl_min( ID_BLOCK *a, ID_BLOCK *b )
807 {
808         return( ID_BLOCK_NIDS(a) > ID_BLOCK_NIDS(b) ? b : a );
809 }
810
811
812 /*
813  * idl_intersection - return a intersection b
814  */
815 ID_BLOCK *
816 idl_intersection(
817     Backend     *be,
818     ID_BLOCK    *a,
819     ID_BLOCK    *b
820 )
821 {
822         unsigned int    ai, bi, ni;
823         ID_BLOCK                *n;
824
825         if ( a == NULL || b == NULL ) {
826                 return( NULL );
827         }
828         if ( ID_BLOCK_ALLIDS( a ) ) {
829                 return( idl_dup( b ) );
830         }
831         if ( ID_BLOCK_ALLIDS( b ) ) {
832                 return( idl_dup( a ) );
833         }
834
835         n = idl_dup( idl_min( a, b ) );
836
837         for ( ni = 0, ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
838                 for ( ;
839                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
840                         bi++ )
841                 {
842                         ;       /* NULL */
843                 }
844
845                 if ( bi == ID_BLOCK_NIDS(b) ) {
846                         break;
847                 }
848
849                 if ( ID_BLOCK_ID(b, bi) == ID_BLOCK_ID(a, ai) ) {
850                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
851                 }
852         }
853
854         if ( ni == 0 ) {
855                 idl_free( n );
856                 return( NULL );
857         }
858         ID_BLOCK_NIDS(n) = ni;
859
860         return( n );
861 }
862
863
864 /*
865  * idl_union - return a union b
866  */
867 ID_BLOCK *
868 idl_union(
869     Backend     *be,
870     ID_BLOCK    *a,
871     ID_BLOCK    *b
872 )
873 {
874         unsigned int    ai, bi, ni;
875         ID_BLOCK                *n;
876
877         if ( a == NULL ) {
878                 return( idl_dup( b ) );
879         }
880         if ( b == NULL ) {
881                 return( idl_dup( a ) );
882         }
883         if ( ID_BLOCK_ALLIDS( a ) || ID_BLOCK_ALLIDS( b ) ) {
884                 return( idl_allids( be ) );
885         }
886
887         if ( ID_BLOCK_NIDS(b) < ID_BLOCK_NIDS(a) ) {
888                 n = a;
889                 a = b;
890                 b = n;
891         }
892
893         n = idl_alloc( ID_BLOCK_NIDS(a) + ID_BLOCK_NIDS(b) );
894
895         for ( ni = 0, ai = 0, bi = 0;
896                 ai < ID_BLOCK_NIDS(a) && bi < ID_BLOCK_NIDS(b);
897                 )
898         {
899                 if ( ID_BLOCK_ID(a, ai) < ID_BLOCK_ID(b, bi) ) {
900                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai++);
901
902                 } else if ( ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai) ) {
903                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi++);
904
905                 } else {
906                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
907                         ai++, bi++;
908                 }
909         }
910
911         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
912                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
913         }
914         for ( ; bi < ID_BLOCK_NIDS(b); bi++ ) {
915                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi);
916         }
917         ID_BLOCK_NIDS(n) = ni;
918
919         return( n );
920 }
921
922
923 /*
924  * idl_notin - return a intersection ~b (or a minus b)
925  */
926 ID_BLOCK *
927 idl_notin(
928     Backend     *be,
929     ID_BLOCK    *a,
930     ID_BLOCK    *b
931 )
932 {
933         unsigned int    ni, ai, bi;
934         ID_BLOCK                *n;
935
936         if ( a == NULL ) {
937                 return( NULL );
938         }
939         if ( b == NULL || ID_BLOCK_ALLIDS( b )) {
940                 return( idl_dup( a ) );
941         }
942
943         if ( ID_BLOCK_ALLIDS( a ) ) {
944                 n = idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
945                 ni = 0;
946
947                 for ( ai = 1, bi = 0;
948                         ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n) && bi < ID_BLOCK_NMAX(b);
949                         ai++ )
950                 {
951                         if ( ID_BLOCK_ID(b, bi) == ai ) {
952                                 bi++;
953                         } else {
954                                 ID_BLOCK_ID(n, ni++) = ai;
955                         }
956                 }
957
958                 for ( ; ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n); ai++ ) {
959                         ID_BLOCK_ID(n, ni++) = ai;
960                 }
961
962                 if ( ni == ID_BLOCK_NMAX(n) ) {
963                         idl_free( n );
964                         return( idl_allids( be ) );
965                 } else {
966                         ID_BLOCK_NIDS(n) = ni;
967                         return( n );
968                 }
969         }
970
971         n = idl_dup( a );
972
973         ni = 0;
974         for ( ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
975                 for ( ;
976                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
977                     bi++ )
978                 {
979                         ;       /* NULL */
980                 }
981
982                 if ( bi == ID_BLOCK_NIDS(b) ) {
983                         break;
984                 }
985
986                 if ( ID_BLOCK_ID(b, bi) != ID_BLOCK_ID(a, ai) ) {
987                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
988                 }
989         }
990
991         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
992                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
993         }
994         ID_BLOCK_NIDS(n) = ni;
995
996         return( n );
997 }
998
999 /*      return the first ID in the block
1000  *      if ALLIDS block
1001  *              NIDS > 1 return 1
1002  *              otherwise return NOID 
1003  *      otherwise return first ID
1004  *
1005  *      cursor is set to 1
1006  */         
1007 ID
1008 idl_firstid( ID_BLOCK *idl, ID *cursor )
1009 {
1010         *cursor = 1;
1011
1012         if ( idl == NULL || ID_BLOCK_NIDS(idl) == 0 ) {
1013                 return( NOID );
1014         }
1015
1016         if ( ID_BLOCK_ALLIDS( idl ) ) {
1017                 return( ID_BLOCK_NIDS(idl) > 1 ? 1 : NOID );
1018         }
1019
1020         return( ID_BLOCK_ID(idl, 0) );
1021 }
1022
1023 /*      return next ID
1024  *      if ALLIDS block, cursor is id.
1025  *              increment id
1026  *              if id < NIDS return id
1027  *              otherwise NOID.
1028  *      otherwise cursor is index into block
1029  *              if index < nids
1030  *                      return id at index then increment
1031  */ 
1032 ID
1033 idl_nextid( ID_BLOCK *idl, ID *cursor )
1034 {
1035         if ( ID_BLOCK_ALLIDS( idl ) ) {
1036                 if( ++(*cursor) < ID_BLOCK_NIDS(idl) ) {
1037                         return *cursor;
1038                 } else {
1039                         return NOID;
1040                 }
1041         }
1042
1043         if ( *cursor < ID_BLOCK_NIDS(idl) ) {
1044                 return( ID_BLOCK_ID(idl, (*cursor)++) );
1045         }
1046
1047         return( NOID );
1048 }