]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/idl.c
91499f04fa8bee38be1c760b6c54c44a868da832
[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 "slap.h"
15 #include "back-ldbm.h"
16
17 static ID_BLOCK* idl_dup( ID_BLOCK *idl );
18
19 /* Allocate an ID_BLOCK with room for nids ids */
20 ID_BLOCK *
21 idl_alloc( unsigned int nids )
22 {
23         ID_BLOCK        *new;
24
25         /* nmax + nids + space for the ids */
26         new = (ID_BLOCK *) ch_calloc( (ID_BLOCK_IDS_OFFSET + nids), sizeof(ID) );
27         ID_BLOCK_NMAX(new) = nids;
28         ID_BLOCK_NIDS(new) = 0;
29
30         return( new );
31 }
32
33
34 /* Allocate an empty ALLIDS ID_BLOCK */
35 ID_BLOCK        *
36 idl_allids( Backend *be )
37 {
38         ID_BLOCK        *idl;
39
40         idl = idl_alloc( 0 );
41         ID_BLOCK_NMAX(idl) = ID_BLOCK_ALLIDS_VALUE;
42         ID_BLOCK_NIDS(idl) = next_id_get( be );
43
44         return( idl );
45 }
46
47 /* Free an ID_BLOCK */
48 void
49 idl_free( ID_BLOCK *idl )
50 {
51         if ( idl == NULL ) {
52                 Debug( LDAP_DEBUG_TRACE,
53                         "idl_free: called with NULL pointer\n",
54                         0, 0, 0 );
55                 return;
56         }
57
58         free( (char *) idl );
59 }
60
61
62 /* Fetch an single ID_BLOCK from the cache */
63 static ID_BLOCK *
64 idl_fetch_one(
65     Backend             *be,
66     DBCache     *db,
67     Datum               key
68 )
69 {
70         Datum   data;
71         ID_BLOCK        *idl;
72
73         /* Debug( LDAP_DEBUG_TRACE, "=> idl_fetch_one\n", 0, 0, 0 ); */
74
75         data = ldbm_cache_fetch( db, key );
76
77         if( data.dptr == NULL ) {
78                 return NULL;
79         }
80
81         idl = idl_dup((ID_BLOCK *) data.dptr);
82
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, do nothing */
474                 rc = 0;
475                 break;
476
477         case 3:         /* id not inserted - block is full */
478                 /*
479                  * first, see if it will fit in the next block,
480                  * without splitting, unless we're trying to insert
481                  * into the beginning of the first block.
482                  */
483
484                 /* is there a next block? */
485                 if ( !first && !ID_BLOCK_NOID(idl, i + 1) ) {
486                         /* read it in */
487                         sprintf( kstr, "%c%ld%s", CONT_PREFIX,
488                                 ID_BLOCK_ID(idl, i + 1), key.dptr );
489                         k2.dptr = kstr;
490                         k2.dsize = strlen( kstr ) + 1;
491                         if ( (tmp2 = idl_fetch_one( be, db, k2 )) == NULL ) {
492                                 Debug( LDAP_DEBUG_ANY,
493                                     "idl_fetch_one (%s) returns NULL\n",
494                                     k2.dptr, 0, 0 );
495                                 /* split the original block */
496                                 goto split;
497                         }
498
499                         switch ( (rc = idl_insert( &tmp2, id,
500                             db->dbc_maxids )) ) {
501                         case 1:         /* id inserted first in block */
502                                 rc = idl_change_first( be, db, key, idl,
503                                     i + 1, k2, tmp2 );
504                                 /* FALL */
505
506                         case 2:         /* id already there - how? */
507                         case 0:         /* id inserted */
508                                 if ( rc == 2 ) {
509                                         Debug( LDAP_DEBUG_ANY,
510                                             "id %ld already in next block\n",
511                                             id, 0, 0 );
512                                 }
513                                 free( kstr );
514                                 idl_free( tmp );
515                                 idl_free( tmp2 );
516                                 idl_free( idl );
517                                 return( 0 );
518
519                         case 3:         /* split the original block */
520                                 break;
521                         }
522
523                         idl_free( tmp2 );
524                 }
525
526 split:
527                 /*
528                  * must split the block, write both new blocks + update
529                  * and write the indirect header block.
530                  */
531
532                 rc = 0; /* optimistic */
533
534
535                 /* count how many indirect blocks *//* XXX linear count XXX */
536                 for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ )
537                         ;       /* NULL */
538
539                 /* check it against all-id thresholed */
540                 if ( j + 1 > db->dbc_maxindirect ) {
541                         /*
542                          * we've passed the all-id threshold, meaning
543                          * that this set of blocks should be replaced
544                          * by a single "all-id" block.  our job: delete
545                          * all the indirect blocks, and replace the header
546                          * block by an all-id block.
547                          */
548
549                         /* delete all indirect blocks */
550                         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) {
551                                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
552                                         ID_BLOCK_ID(idl, j), key.dptr );
553                                 k2.dptr = kstr;
554                                 k2.dsize = strlen( kstr ) + 1;
555
556                                 rc = ldbm_cache_delete( db, k2 );
557                         }
558
559                         /* store allid block in place of header block */
560                         idl_free( idl );
561                         idl = idl_allids( be );
562                         rc = idl_store( be, db, key, idl );
563
564                         free( kstr );
565                         idl_free( idl );
566                         idl_free( tmp );
567                         return( rc );
568                 }
569
570                 idl_split_block( tmp, id, &tmp2, &tmp3 );
571                 idl_free( tmp );
572
573                 /* create a new updated indirect header block */
574                 tmp = idl_alloc( ID_BLOCK_NMAX(idl) + 1 );
575                 ID_BLOCK_NIDS(tmp) = ID_BLOCK_INDIRECT_VALUE;
576                 /* everything up to the split block */
577                 SAFEMEMCPY(
578                         (char *) &ID_BLOCK_ID(tmp, 0),
579                         (char *) &ID_BLOCK_ID(idl, 0),
580                     i * sizeof(ID) );
581                 /* the two new blocks */
582                 ID_BLOCK_ID(tmp, i) = ID_BLOCK_ID(tmp2, 0);
583                 ID_BLOCK_ID(tmp, i + 1) = ID_BLOCK_ID(tmp3, 0);
584                 /* everything after the split block */
585                 SAFEMEMCPY(
586                         (char *) &ID_BLOCK_ID(tmp, i + 2),
587                         (char *) &ID_BLOCK_ID(idl, i + 1),
588                         (ID_BLOCK_NMAX(idl) - i - 1) * sizeof(ID) );
589
590                 /* store the header block */
591                 rc = idl_store( be, db, key, tmp );
592
593                 /* store the first id block */
594                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
595                         ID_BLOCK_ID(tmp2, 0), key.dptr );
596                 k2.dptr = kstr;
597                 k2.dsize = strlen( kstr ) + 1;
598                 rc = idl_store( be, db, k2, tmp2 );
599
600                 /* store the second id block */
601                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
602                         ID_BLOCK_ID(tmp3, 0), key.dptr );
603                 k2.dptr = kstr;
604                 k2.dsize = strlen( kstr ) + 1;
605                 rc = idl_store( be, db, k2, tmp3 );
606
607                 idl_free( tmp2 );
608                 idl_free( tmp3 );
609                 break;
610         }
611
612         free( kstr );
613         idl_free( tmp );
614         idl_free( idl );
615         return( rc );
616 }
617
618
619 /*
620  * idl_insert - insert an id into an id list.
621  *
622  *      returns
623  *              0       id inserted
624  *              1       id inserted, first id in block has changed
625  *              2       id not inserted, already there
626  *              3       id not inserted, block must be split
627  */
628 int
629 idl_insert( ID_BLOCK **idl, ID id, unsigned int maxids )
630 {
631         unsigned int    i;
632
633         if ( ID_BLOCK_ALLIDS( *idl ) ) {
634                 return( 2 );    /* already there */
635         }
636
637         /* is it already there? *//* XXX linear search XXX */
638         for ( i = 0; i < ID_BLOCK_NIDS(*idl) && id > ID_BLOCK_ID(*idl, i); i++ ) {
639                 ;       /* NULL */
640         }
641         if ( i < ID_BLOCK_NIDS(*idl) && ID_BLOCK_ID(*idl, i) == id ) {
642                 return( 2 );    /* already there */
643         }
644
645         /* do we need to make room for it? */
646         if ( ID_BLOCK_NIDS(*idl) == ID_BLOCK_NMAX(*idl) ) {
647                 /* make room or indicate block needs splitting */
648                 if ( ID_BLOCK_NMAX(*idl) >= maxids ) {
649                         return( 3 );    /* block needs splitting */
650                 }
651
652                 ID_BLOCK_NMAX(*idl) *= 2;
653                 if ( ID_BLOCK_NMAX(*idl) > maxids ) {
654                         ID_BLOCK_NMAX(*idl) = maxids;
655                 }
656                 *idl = (ID_BLOCK *) ch_realloc( (char *) *idl,
657                     (ID_BLOCK_NMAX(*idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
658         }
659
660         /* make a slot for the new id */
661         SAFEMEMCPY( &ID_BLOCK_ID(*idl, i+1), &ID_BLOCK_ID(*idl, i),
662                     (ID_BLOCK_NIDS(*idl) - i) * sizeof(ID) );
663
664         ID_BLOCK_ID(*idl, i) = id;
665         ID_BLOCK_NIDS(*idl)++;
666         (void) memset(
667                 (char *) &ID_BLOCK_ID((*idl), ID_BLOCK_NIDS(*idl)),
668                 '\0',
669             (ID_BLOCK_NMAX(*idl) - ID_BLOCK_NIDS(*idl)) * sizeof(ID) );
670
671         return( i == 0 ? 1 : 0 );       /* inserted - first id changed or not */
672 }
673
674
675 int
676 idl_delete_key (
677         Backend         *be,
678         DBCache  *db,
679         Datum           key,
680         ID              id
681 )
682 {
683         Datum  data;
684         ID_BLOCK *idl;
685         unsigned i;
686         int j, nids;
687         char    *kstr;
688
689         if ( (idl = idl_fetch_one( be, db, key ) ) == NULL )
690         {
691                 /* It wasn't found.  Hmm... */
692                 return -1;
693         }
694
695         if ( ID_BLOCK_ALLIDS( idl ) ) {
696                 idl_free( idl );
697                 return 0;
698         }
699
700         if ( ! ID_BLOCK_INDIRECT( idl ) ) {
701                 for ( i=0; i < ID_BLOCK_NIDS(idl); i++ ) {
702                         if ( ID_BLOCK_ID(idl, i) == id ) {
703                                 if( --ID_BLOCK_NIDS(idl) == 0 ) {
704                                         ldbm_cache_delete( db, key );
705
706                                 } else {
707                                         SAFEMEMCPY (
708                                                 &ID_BLOCK_ID(idl, i),
709                                                 &ID_BLOCK_ID(idl, i+1),
710                                                 (ID_BLOCK_NIDS(idl)-i) * sizeof(ID) );
711
712                                         ID_BLOCK_ID(idl, ID_BLOCK_NIDS(idl)) = NOID;
713
714                                         idl_store( be, db, key, idl );
715                                 }
716
717                                 idl_free( idl );
718                                 return 0;
719                         }
720                         /*  We didn't find the ID.  Hmmm... */
721                 }
722                 idl_free( idl );
723                 return -1;
724         }
725         
726         /* We have to go through an indirect block and find the ID
727            in the list of IDL's
728            */
729         for ( nids = 0; !ID_BLOCK_NOID(idl, nids); nids++ )
730                 ;       /* NULL */
731         kstr = (char *) ch_malloc( key.dsize + CONT_SIZE );
732
733         for ( j = 0; !ID_BLOCK_NOID(idl, j); j++ ) 
734         {
735                 ID_BLOCK *tmp;
736                 ldbm_datum_init( data );
737                 sprintf( kstr, "%c%ld%s", CONT_PREFIX,
738                         ID_BLOCK_ID(idl, j), key.dptr );
739                 data.dptr = kstr;
740                 data.dsize = strlen( kstr ) + 1;
741
742                 if ( (tmp = idl_fetch_one( be, db, data )) == NULL ) {
743                         Debug( LDAP_DEBUG_ANY,
744                             "idl_fetch of (%s) returns NULL\n", data.dptr, 0, 0 );
745                         continue;
746                 }
747                 /*
748                    Now try to find the ID in tmp
749                 */
750                 for ( i=0; i < ID_BLOCK_NIDS(tmp); i++ )
751                 {
752                         if ( ID_BLOCK_ID(tmp, i) == id )
753                         {
754                                 SAFEMEMCPY(
755                                         &ID_BLOCK_ID(tmp, i),
756                                         &ID_BLOCK_ID(tmp, i+1),
757                                         (ID_BLOCK_NIDS(tmp)-(i+1)) * sizeof(ID));
758                                 ID_BLOCK_ID(tmp, ID_BLOCK_NIDS(tmp)-1 ) = NOID;
759                                 ID_BLOCK_NIDS(tmp)--;
760
761                                 if ( ID_BLOCK_NIDS(tmp) ) {
762                                         idl_store ( be, db, data, tmp );
763
764                                 } else {
765                                         ldbm_cache_delete( db, data );
766                                         SAFEMEMCPY(
767                                                 &ID_BLOCK_ID(idl, j),
768                                                 &ID_BLOCK_ID(idl, j+1),
769                                                 (nids-(j+1)) * sizeof(ID));
770                                         ID_BLOCK_ID(idl, nids-1) = NOID;
771                                         nids--;
772                                         if ( ! nids )
773                                                 ldbm_cache_delete( db, key );
774                                         else
775                                                 idl_store( be, db, key, idl );
776                                 }
777                                 idl_free( tmp );
778                                 free( kstr );
779                                 idl_free( idl );
780                                 return 0;
781                         }
782                 }
783                 idl_free( tmp );
784         }
785         free( kstr );
786         idl_free( idl );
787         return -1;
788 }
789
790
791 /* return a duplicate of a single ID_BLOCK */
792 static ID_BLOCK *
793 idl_dup( ID_BLOCK *idl )
794 {
795         ID_BLOCK        *new;
796
797         if ( idl == NULL ) {
798                 return( NULL );
799         }
800
801         new = idl_alloc( ID_BLOCK_NMAX(idl) );
802
803         SAFEMEMCPY(
804                 (char *) new,
805                 (char *) idl,
806                 (ID_BLOCK_NMAX(idl) + ID_BLOCK_IDS_OFFSET) * sizeof(ID) );
807
808         return( new );
809 }
810
811
812 /* return the smaller ID_BLOCK */
813 static ID_BLOCK *
814 idl_min( ID_BLOCK *a, ID_BLOCK *b )
815 {
816         return( ID_BLOCK_NIDS(a) > ID_BLOCK_NIDS(b) ? b : a );
817 }
818
819
820 /*
821  * idl_intersection - return a intersection b
822  */
823 ID_BLOCK *
824 idl_intersection(
825     Backend     *be,
826     ID_BLOCK    *a,
827     ID_BLOCK    *b
828 )
829 {
830         unsigned int    ai, bi, ni;
831         ID_BLOCK                *n;
832
833         if ( a == NULL || b == NULL ) {
834                 return( NULL );
835         }
836         if ( ID_BLOCK_ALLIDS( a ) ) {
837                 return( idl_dup( b ) );
838         }
839         if ( ID_BLOCK_ALLIDS( b ) ) {
840                 return( idl_dup( a ) );
841         }
842
843         n = idl_dup( idl_min( a, b ) );
844
845         for ( ni = 0, ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
846                 for ( ;
847                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
848                         bi++ )
849                 {
850                         ;       /* NULL */
851                 }
852
853                 if ( bi == ID_BLOCK_NIDS(b) ) {
854                         break;
855                 }
856
857                 if ( ID_BLOCK_ID(b, bi) == ID_BLOCK_ID(a, ai) ) {
858                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
859                 }
860         }
861
862         if ( ni == 0 ) {
863                 idl_free( n );
864                 return( NULL );
865         }
866         ID_BLOCK_NIDS(n) = ni;
867
868         return( n );
869 }
870
871
872 /*
873  * idl_union - return a union b
874  */
875 ID_BLOCK *
876 idl_union(
877     Backend     *be,
878     ID_BLOCK    *a,
879     ID_BLOCK    *b
880 )
881 {
882         unsigned int    ai, bi, ni;
883         ID_BLOCK                *n;
884
885         if ( a == NULL ) {
886                 return( idl_dup( b ) );
887         }
888         if ( b == NULL ) {
889                 return( idl_dup( a ) );
890         }
891         if ( ID_BLOCK_ALLIDS( a ) || ID_BLOCK_ALLIDS( b ) ) {
892                 return( idl_allids( be ) );
893         }
894
895         if ( ID_BLOCK_NIDS(b) < ID_BLOCK_NIDS(a) ) {
896                 n = a;
897                 a = b;
898                 b = n;
899         }
900
901         n = idl_alloc( ID_BLOCK_NIDS(a) + ID_BLOCK_NIDS(b) );
902
903         for ( ni = 0, ai = 0, bi = 0;
904                 ai < ID_BLOCK_NIDS(a) && bi < ID_BLOCK_NIDS(b);
905                 )
906         {
907                 if ( ID_BLOCK_ID(a, ai) < ID_BLOCK_ID(b, bi) ) {
908                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai++);
909
910                 } else if ( ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai) ) {
911                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi++);
912
913                 } else {
914                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
915                         ai++, bi++;
916                 }
917         }
918
919         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
920                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
921         }
922         for ( ; bi < ID_BLOCK_NIDS(b); bi++ ) {
923                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(b, bi);
924         }
925         ID_BLOCK_NIDS(n) = ni;
926
927         return( n );
928 }
929
930
931 /*
932  * idl_notin - return a intersection ~b (or a minus b)
933  */
934 ID_BLOCK *
935 idl_notin(
936     Backend     *be,
937     ID_BLOCK    *a,
938     ID_BLOCK    *b
939 )
940 {
941         unsigned int    ni, ai, bi;
942         ID_BLOCK                *n;
943
944         if ( a == NULL ) {
945                 return( NULL );
946         }
947         if ( b == NULL || ID_BLOCK_ALLIDS( b )) {
948                 return( idl_dup( a ) );
949         }
950
951         if ( ID_BLOCK_ALLIDS( a ) ) {
952                 n = idl_alloc( SLAPD_LDBM_MIN_MAXIDS );
953                 ni = 0;
954
955                 for ( ai = 1, bi = 0;
956                         ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n) && bi < ID_BLOCK_NMAX(b);
957                         ai++ )
958                 {
959                         if ( ID_BLOCK_ID(b, bi) == ai ) {
960                                 bi++;
961                         } else {
962                                 ID_BLOCK_ID(n, ni++) = ai;
963                         }
964                 }
965
966                 for ( ; ai < ID_BLOCK_NIDS(a) && ni < ID_BLOCK_NMAX(n); ai++ ) {
967                         ID_BLOCK_ID(n, ni++) = ai;
968                 }
969
970                 if ( ni == ID_BLOCK_NMAX(n) ) {
971                         idl_free( n );
972                         return( idl_allids( be ) );
973                 } else {
974                         ID_BLOCK_NIDS(n) = ni;
975                         return( n );
976                 }
977         }
978
979         n = idl_dup( a );
980
981         ni = 0;
982         for ( ai = 0, bi = 0; ai < ID_BLOCK_NIDS(a); ai++ ) {
983                 for ( ;
984                         bi < ID_BLOCK_NIDS(b) && ID_BLOCK_ID(b, bi) < ID_BLOCK_ID(a, ai);
985                     bi++ )
986                 {
987                         ;       /* NULL */
988                 }
989
990                 if ( bi == ID_BLOCK_NIDS(b) ) {
991                         break;
992                 }
993
994                 if ( ID_BLOCK_ID(b, bi) != ID_BLOCK_ID(a, ai) ) {
995                         ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
996                 }
997         }
998
999         for ( ; ai < ID_BLOCK_NIDS(a); ai++ ) {
1000                 ID_BLOCK_ID(n, ni++) = ID_BLOCK_ID(a, ai);
1001         }
1002         ID_BLOCK_NIDS(n) = ni;
1003
1004         return( n );
1005 }
1006
1007 /*      return the first ID in the block
1008  *      if ALLIDS block
1009  *              NIDS > 1 return 1
1010  *              otherwise return NOID 
1011  *      otherwise return first ID
1012  *
1013  *      cursor is set to 1
1014  */         
1015 ID
1016 idl_firstid( ID_BLOCK *idl, ID *cursor )
1017 {
1018         *cursor = 1;
1019
1020         if ( idl == NULL || ID_BLOCK_NIDS(idl) == 0 ) {
1021                 return( NOID );
1022         }
1023
1024         if ( ID_BLOCK_ALLIDS( idl ) ) {
1025                 return( ID_BLOCK_NIDS(idl) > 1 ? 1 : NOID );
1026         }
1027
1028         return( ID_BLOCK_ID(idl, 0) );
1029 }
1030
1031 /*      return next ID
1032  *      if ALLIDS block, cursor is id.
1033  *              increment id
1034  *              if id < NIDS return id
1035  *              otherwise NOID.
1036  *      otherwise cursor is index into block
1037  *              if index < nids
1038  *                      return id at index then increment
1039  */ 
1040 ID
1041 idl_nextid( ID_BLOCK *idl, ID *cursor )
1042 {
1043         if ( ID_BLOCK_ALLIDS( idl ) ) {
1044                 if( ++(*cursor) < ID_BLOCK_NIDS(idl) ) {
1045                         return *cursor;
1046                 } else {
1047                         return NOID;
1048                 }
1049         }
1050
1051         if ( *cursor < ID_BLOCK_NIDS(idl) ) {
1052                 return( ID_BLOCK_ID(idl, (*cursor)++) );
1053         }
1054
1055         return( NOID );
1056 }