2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
6 * These libraries and programs are free software; you can
7 * redistribute them and/or modify them under the terms of the GNU
8 * Lesser General Public License as published by the Free Software
9 * Foundation; either version 2 of the License, or (at your option)
12 * These libraries and programs are distributed in the hope that
13 * they will be useful, but WITHOUT ANY WARRANTY; without even the
14 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU Lesser General Public License for more
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with these librararies and programs; if not, write
20 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21 * Floor, Boston, MA 02110-1301 USA
23 /*%% (c) Copyright 1993, 1994 Hewlett-Packard Company */
24 /*%% (c) Copyright 1993, 1994 International Business Machines Corp. */
25 /*%% (c) Copyright 1993, 1994 Sun Microsystems, Inc. */
26 /*%% (c) Copyright 1993, 1994 Novell, Inc. */
27 /*%% $XConsortium: isamaddindex.c /main/3 1995/10/23 11:34:12 rswiston $ */
29 static char sccsid[] = "@(#)isamaddindex.c 1.14 89/09/14 Copyr 1988 Sun Micro";
32 * Copyright (c) 1988 by Sun Microsystems, Inc.
44 #include "isam_impl.h"
45 #include <sys/types.h>
48 extern int _iskeycmp();
50 static void _readallrecords(), _attach_dups_serial();
51 static Blkno _buildbtree();
52 static int _duplicate_exist();
53 static checkavailfd();
56 * _amaddindex(isfhandle, keydesc, errcode)
58 * _amaddindex() build a secondary index
61 * isfhandle Handle of ISAM file
62 * keydesc Key descriptor
65 * errcode error status of the operation
71 _amaddindex(isfhandle, keydesc, errcode)
73 struct keydesc *keydesc;
74 struct errcode *errcode;
84 * Check if 1 UNIX file decriptor is available.
89 * Get FCB corresponding to the isfhandle handle.
91 if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
97 * Check that the limit on the number of keys is not exceeded.
99 if (fcb->nkeys >= MAXNKEYS) {
100 _amseterrcode(errcode, ETOOMANY);
105 * Update information in FCB from CNTL page on the disk
107 (void)_isfcb_cntlpg_r(fcb);
110 * Check key descriptor for validity.
112 if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
113 _amseterrcode(errcode, EBADKEY);
118 * Convert key descriptor to internal form.
120 _iskey_xtoi (&keydesc2, keydesc);
122 /* Check if key already exists. */
123 if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
124 _amseterrcode(errcode, EKEXISTS);
129 * Open (or even create) .ind file if that is not open (created) yet.
131 if (_open2_indfile(fcb) != ISOK) {
132 _amseterrcode(errcode, EACCES);
137 * Create index structure.
139 if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
140 _amseterrcode(errcode, err);
145 * Add key descriptor to FCB.
147 if (_isfcb_altkeyadd(fcb, &keydesc2) == ISERROR) {
148 _amseterrcode(errcode, ETOOMANY);
152 _amseterrcode(errcode, ISOK);
160 * Update CNTL Page from the FCB.
162 (void)_isfcb_cntlpg_w(fcb);
174 * Restore FCB from CNTL page.
176 if (fcb) (void)_isfcb_cntlpg_r(fcb);
183 * _amaddprimary(isfhandle, keydesc, errcode)
185 * _amaddprimary() build primary key
188 * isfhandle Handle of ISAM file
189 * keydesc Key descriptor
192 * errcode error status of the operation
198 _amaddprimary(isfhandle, keydesc, errcode)
199 Bytearray *isfhandle;
200 struct keydesc *keydesc;
201 struct errcode *errcode;
210 * Check if 1 UNIX file decriptor is available.
215 * Get FCB corresponding to the isfhandle handle.
217 if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
223 * Check that the limit on the numbrer of keys is not exceeded.
225 if (fcb->nkeys >= MAXNKEYS) {
226 _amseterrcode(errcode, ETOOMANY);
231 * Update information in FCB from CNTL page on the disk
233 (void)_isfcb_cntlpg_r(fcb);
236 * Check key descriptor for validity.
238 if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
239 _amseterrcode(errcode, EBADKEY);
244 * Convert key descriptor to internal form.
246 _iskey_xtoi (&keydesc2, keydesc);
248 /* Check if key already exists. */
249 if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
250 _amseterrcode(errcode, EKEXISTS);
255 * Check that primary key does not already exist.
257 if (!FCB_NOPRIMARY_KEY(fcb)) {
258 _amseterrcode(errcode, EKEXISTS);
263 * Open (or even create) .ind file if that is not open (created) yet.
265 if (_open2_indfile(fcb) != ISOK) {
266 _amseterrcode(errcode, EACCES);
271 * Create index structure.
273 if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
274 _amseterrcode(errcode, err);
279 * Add key descriptor to FCB.
281 if (_isfcb_primkeyadd(fcb, &keydesc2) == ISERROR) {
282 _amseterrcode(errcode, ETOOMANY);
286 _amseterrcode(errcode, ISOK);
294 * Update CNTL Page from the FCB.
296 (void)_isfcb_cntlpg_w(fcb);
307 * Restore FCB from CNTL page.
309 if (fcb) (void)_isfcb_cntlpg_r(fcb);
318 * Read data file, extract key from record, sort keys, create index.
319 * Check unique constraint, create duplicate serial numbers.
322 int _create_index(fcb, pkeydesc2)
327 int keylength = pkeydesc2->k2_len;
330 * Set comparison function for sorting.
332 * nparts + 1 is used to compare keys that allow duplicates.
333 * The (nparts + 1) comparison will never be reached on ISNODUPS key.
335 _iskeycmp_set(pkeydesc2, pkeydesc2->k2_nparts+1);
338 * Handle empty file as a special case, to avoid nasty behavior
339 * in buildbtree() arithmetics.
341 if (fcb->nrecords == 0L) {
342 pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, (Issort *) NULL);
348 * Create a sorter for this key descriptor.
350 srt = _issort_create(keylength, (int)fcb->nrecords, _iskeycmp);
353 * Read sequentially all records, extract keys, and
354 * insert the keys into the sorter.
356 _readallrecords(fcb, srt, pkeydesc2);
358 _issort_sort(srt); /* Sort the keys */
361 * Check for potential duplicates if the index is ISNODUPS.
363 if (!ALLOWS_DUPS2(pkeydesc2)) {
364 if (_duplicate_exist(srt, keylength)) {
365 _issort_destroy(srt);
371 * Attach duplicate serial numbers to the keys that are ISDUPS.
373 if (ALLOWS_DUPS2(pkeydesc2)) {
374 _attach_dups_serial(srt, pkeydesc2);
378 * Allocate and build the B-tree
380 pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, srt);
382 _issort_destroy(srt); /* Destroy sorter */
390 * REad all records, extract keys, and insert them into sorter.
394 _readallrecords(fcb, srt, pkeydesc2)
399 char record [ISMAXRECLEN];
400 char keybuf [MAXKEYSIZE];
403 int (*rec_read)() = (fcb->varflag?_vlrec_read:_flrec_read);
405 for (recnum = 1; recnum <= fcb->lastrecno; recnum++) {
407 if (rec_read(fcb, record, recnum, &reclen) != ISOK)
408 continue; /* Skip deleted record */
411 * Zero out the entire key buffer to allow for using
412 * memcmp() as comparison function to compare whole keys.
414 memset(keybuf, 0, pkeydesc2->k2_len);
417 * Extract key parts from record buffer.
419 _iskey_extract(pkeydesc2, record, keybuf);
424 strecno(recnum, keybuf + KEY_RECNO_OFF);
426 _issort_insert(srt, keybuf); /* Insert key into sorter */
433 * _attach_dups_serial()
435 * Attach serial numbers to duplicate keys
439 _attach_dups_serial(srt, pkeydesc2)
443 int netkeylength = pkeydesc2->k2_len - RECNOSIZE - DUPIDSIZE;
445 char *lastkey = NULL;
450 while (curkey = _issort_read(srt)) {
451 if (lastkey && memcmp(lastkey + RECNOSIZE + DUPIDSIZE,
452 curkey + RECNOSIZE + DUPIDSIZE,
459 * Store serial number in the key.
461 stdupser(dup_serial, curkey + KEY_DUPS_OFF);
474 _buildbtree(fcb, pkeydesc2, srt)
479 Bufhdr *_allockpage();
481 int nrecords = fcb->nrecords;
482 int keyspernode[ISMAXBTRLEVEL];
483 int one_more[ISMAXBTRLEVEL];
484 char *nodebuf[ISMAXBTRLEVEL];
485 Bufhdr *nodebufhd[ISMAXBTRLEVEL];
486 Blkno pageblkno[ISMAXBTRLEVEL];
487 int curindex[ISMAXBTRLEVEL];
489 int keylength = pkeydesc2->k2_len;
490 int leafcapac = getkeysperleaf(keylength);
491 int nleafcapac = getkeyspernode(keylength);
493 int blocks_in_this_level, blocks_in_prev_level;
494 int slack = ISLEAFSLACK;
498 * Handle an empty B-tree as a special case.
500 if (fcb->nrecords == 0) {
501 (void)_allockpage(fcb, leafcapac, 0, pageblkno + 0);
502 return (pageblkno[0]);
506 * COMPRESS changes the fill factor to 100%
508 if ((pkeydesc2->k2_flags & COMPRESS) == COMPRESS)
512 * Figure out fill factors for each level.
514 perleaf = ((double)leafcapac * (100.0 - (double) slack)) / 100.0;
517 * Make it more robust.
519 if (perleaf >= leafcapac)
522 if (perleaf < leafcapac/2 + 1)
523 perleaf = leafcapac/2 + 1;
526 * Iterativelly determince values in keyspernode[] and one_mode[]
528 blocks_in_this_level = nrecords / perleaf;
529 if (blocks_in_this_level * leafcapac < nrecords)
530 blocks_in_this_level++;
532 keyspernode[0] = nrecords / blocks_in_this_level;
533 one_more[0] = nrecords % blocks_in_this_level;
536 for (depth = 1; blocks_in_this_level > 1; depth++) {
537 blocks_in_prev_level = blocks_in_this_level;
539 blocks_in_this_level = (blocks_in_prev_level-1) / nleafcapac + 1;
540 keyspernode[depth] = blocks_in_prev_level / blocks_in_this_level;
541 one_more[depth] = blocks_in_prev_level % blocks_in_this_level;
544 if (depth >= ISMAXBTRLEVEL)
545 _isfatal_error("Too many levels in B-tree");
548 * Make sure that we start reading keys from the beginning.
553 * Boot the Main loop.
555 for (i = 0; i < depth ; i++) {
556 curindex[i] = ISPAGESIZE; /* Any big number will do */
564 while ((keyp = _issort_read(srt)) != (char *) NULL) {
566 if (curindex[0] >= keyspernode[0] + (one_more[0] > 0)) {
569 * Commit all changed buffers here to avoid buffer pool overflow.
572 _isdisk_commit1(nodebufhd[0]);
576 /* Allocate new leaf. */
577 nodebufhd[0] = _allockpage(fcb, leafcapac, 0, pageblkno+0);
578 nodebuf[0] = nodebufhd[0]->isb_buffer;
583 /* Copy key into the page. */
584 memcpy( nodebuf[0] + BT_KEYS_OFF + keylength * curindex[0],keyp,
586 stshort((short) curindex[0] + 1, nodebuf[0] + BT_NKEYS_OFF);
588 if (curindex[0]++ == 0) { /* Store first key in */
591 for (i = 1;i < depth; i++) {
592 if (curindex[i] >= keyspernode[i] + (one_more[i] > 0)) {
596 _isdisk_commit1(nodebufhd[i]);
598 nodebufhd[i] = _allockpage(fcb, nleafcapac, i, pageblkno+i);
599 nodebuf[i] = nodebufhd[i]->isb_buffer;
604 /* Copy key into page. */
605 memcpy( nodebuf[i] + BT_KEYS_OFF + keylength * curindex[i],keyp,
608 /* Store pointer to level below. */
609 stblkno(pageblkno[i-1], nodebuf[i] + ISPAGESIZE -
610 (curindex[i]+1) * BLKNOSIZE);
612 stshort((short) curindex[i] + 1, nodebuf[i] + BT_NKEYS_OFF);
613 if (curindex[i]++ > 0) {
621 return (pageblkno [depth - 1]); /* Return blkno of B-tree root */
627 * Return 1 if there are duplicates in the sorted key object.
630 Static int _duplicate_exist(srt, keylength)
635 char *lastkey = (char *) 0;
636 int netkeylength = keylength - RECNOSIZE;
640 while (curkey = _issort_read(srt)) {
641 if (lastkey && memcmp(lastkey + RECNOSIZE, curkey + RECNOSIZE,
643 return 1; /* Duplicate key found */
647 return 0; /* No duplicates found */
656 Bytearray *isfhandle2;
659 * Check that there are UNIX file descriptors available.
661 while (_watchfd_check() < 1) {
663 * Find victim (LRU FCB) and close it.
665 if((isfhandle2 = _mngfcb_victim()) == NULL)
666 _isfatal_error ("_openfcb() cannot find LRU victim");
668 fcb = _mngfcb_find(isfhandle2);
669 (void) _watchfd_decr(_isfcb_nfds(fcb));
671 _mngfcb_delete(isfhandle2);