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