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 libraries 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 void checkavailfd(void);
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(Bytearray *isfhandle, struct keydesc *keydesc, struct errcode *errcode)
81 * Check if 1 UNIX file decriptor is available.
86 * Get FCB corresponding to the isfhandle handle.
88 if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
94 * Check that the limit on the number of keys is not exceeded.
96 if (fcb->nkeys >= MAXNKEYS) {
97 _amseterrcode(errcode, ETOOMANY);
102 * Update information in FCB from CNTL page on the disk
104 (void)_isfcb_cntlpg_r(fcb);
107 * Check key descriptor for validity.
109 if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
110 _amseterrcode(errcode, EBADKEY);
115 * Convert key descriptor to internal form.
117 _iskey_xtoi (&keydesc2, keydesc);
119 /* Check if key already exists. */
120 if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
121 _amseterrcode(errcode, EKEXISTS);
126 * Open (or even create) .ind file if that is not open (created) yet.
128 if (_open2_indfile(fcb) != ISOK) {
129 _amseterrcode(errcode, EACCES);
134 * Create index structure.
136 if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
137 _amseterrcode(errcode, err);
142 * Add key descriptor to FCB.
144 if (_isfcb_altkeyadd(fcb, &keydesc2) == ISERROR) {
145 _amseterrcode(errcode, ETOOMANY);
149 _amseterrcode(errcode, ISOK);
157 * Update CNTL Page from the FCB.
159 (void)_isfcb_cntlpg_w(fcb);
171 * Restore FCB from CNTL page.
173 if (fcb) (void)_isfcb_cntlpg_r(fcb);
180 * _amaddprimary(isfhandle, keydesc, errcode)
182 * _amaddprimary() build primary key
185 * isfhandle Handle of ISAM file
186 * keydesc Key descriptor
189 * errcode error status of the operation
195 _amaddprimary(Bytearray *isfhandle, struct keydesc *keydesc, struct errcode *errcode)
204 * Check if 1 UNIX file decriptor is available.
209 * Get FCB corresponding to the isfhandle handle.
211 if ((fcb = _openfcb(isfhandle, errcode)) == NULL) {
217 * Check that the limit on the numbrer of keys is not exceeded.
219 if (fcb->nkeys >= MAXNKEYS) {
220 _amseterrcode(errcode, ETOOMANY);
225 * Update information in FCB from CNTL page on the disk
227 (void)_isfcb_cntlpg_r(fcb);
230 * Check key descriptor for validity.
232 if (_validate_keydesc(keydesc, fcb->minreclen) != ISOK) {
233 _amseterrcode(errcode, EBADKEY);
238 * Convert key descriptor to internal form.
240 _iskey_xtoi (&keydesc2, keydesc);
242 /* Check if key already exists. */
243 if (_isfcb_findkey(fcb ,&keydesc2) != (Keydesc2 *) 0) {
244 _amseterrcode(errcode, EKEXISTS);
249 * Check that primary key does not already exist.
251 if (!FCB_NOPRIMARY_KEY(fcb)) {
252 _amseterrcode(errcode, EKEXISTS);
257 * Open (or even create) .ind file if that is not open (created) yet.
259 if (_open2_indfile(fcb) != ISOK) {
260 _amseterrcode(errcode, EACCES);
265 * Create index structure.
267 if ((err = _create_index(fcb ,&keydesc2)) != ISOK) {
268 _amseterrcode(errcode, err);
273 * Add key descriptor to FCB.
275 if (_isfcb_primkeyadd(fcb, &keydesc2) == ISERROR) {
276 _amseterrcode(errcode, ETOOMANY);
280 _amseterrcode(errcode, ISOK);
288 * Update CNTL Page from the FCB.
290 (void)_isfcb_cntlpg_w(fcb);
301 * Restore FCB from CNTL page.
303 if (fcb) (void)_isfcb_cntlpg_r(fcb);
312 * Read data file, extract key from record, sort keys, create index.
313 * Check unique constraint, create duplicate serial numbers.
316 int _create_index(Fcb *fcb, Keydesc2 *pkeydesc2)
319 int keylength = pkeydesc2->k2_len;
322 * Set comparison function for sorting.
324 * nparts + 1 is used to compare keys that allow duplicates.
325 * The (nparts + 1) comparison will never be reached on ISNODUPS key.
327 _iskeycmp_set(pkeydesc2, pkeydesc2->k2_nparts+1);
330 * Handle empty file as a special case, to avoid nasty behavior
331 * in buildbtree() arithmetics.
333 if (fcb->nrecords == 0L) {
334 pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, (Issort *) NULL);
340 * Create a sorter for this key descriptor.
342 srt = _issort_create(keylength, (int)fcb->nrecords, _iskeycmp);
345 * Read sequentially all records, extract keys, and
346 * insert the keys into the sorter.
348 _readallrecords(fcb, srt, pkeydesc2);
350 _issort_sort(srt); /* Sort the keys */
353 * Check for potential duplicates if the index is ISNODUPS.
355 if (!ALLOWS_DUPS2(pkeydesc2)) {
356 if (_duplicate_exist(srt, keylength)) {
357 _issort_destroy(srt);
363 * Attach duplicate serial numbers to the keys that are ISDUPS.
365 if (ALLOWS_DUPS2(pkeydesc2)) {
366 _attach_dups_serial(srt, pkeydesc2);
370 * Allocate and build the B-tree
372 pkeydesc2->k2_rootnode = _buildbtree(fcb, pkeydesc2, srt);
374 _issort_destroy(srt); /* Destroy sorter */
382 * REad all records, extract keys, and insert them into sorter.
386 _readallrecords(Fcb *fcb, Issort *srt, Keydesc2 *pkeydesc2)
388 char record [ISMAXRECLEN];
389 char keybuf [MAXKEYSIZE];
392 int (*rec_read)() = (fcb->varflag?_vlrec_read:_flrec_read);
394 for (recnum = 1; recnum <= fcb->lastrecno; recnum++) {
396 if (rec_read(fcb, record, recnum, &reclen) != ISOK)
397 continue; /* Skip deleted record */
400 * Zero out the entire key buffer to allow for using
401 * memcmp() as comparison function to compare whole keys.
403 memset(keybuf, 0, pkeydesc2->k2_len);
406 * Extract key parts from record buffer.
408 _iskey_extract(pkeydesc2, record, keybuf);
413 strecno(recnum, keybuf + KEY_RECNO_OFF);
415 _issort_insert(srt, keybuf); /* Insert key into sorter */
422 * _attach_dups_serial()
424 * Attach serial numbers to duplicate keys
428 _attach_dups_serial(Issort *srt, Keydesc2 *pkeydesc2)
430 int netkeylength = pkeydesc2->k2_len - RECNOSIZE - DUPIDSIZE;
432 char *lastkey = NULL;
437 while (curkey = _issort_read(srt)) {
438 if (lastkey && memcmp(lastkey + RECNOSIZE + DUPIDSIZE,
439 curkey + RECNOSIZE + DUPIDSIZE,
446 * Store serial number in the key.
448 stdupser(dup_serial, curkey + KEY_DUPS_OFF);
461 _buildbtree(Fcb *fcb, Keydesc2 *pkeydesc2, Issort *srt)
463 Bufhdr *_allockpage();
465 int nrecords = fcb->nrecords;
466 int keyspernode[ISMAXBTRLEVEL];
467 int one_more[ISMAXBTRLEVEL];
468 char *nodebuf[ISMAXBTRLEVEL];
469 Bufhdr *nodebufhd[ISMAXBTRLEVEL];
470 Blkno pageblkno[ISMAXBTRLEVEL];
471 int curindex[ISMAXBTRLEVEL];
473 int keylength = pkeydesc2->k2_len;
474 int leafcapac = getkeysperleaf(keylength);
475 int nleafcapac = getkeyspernode(keylength);
477 int blocks_in_this_level, blocks_in_prev_level;
478 int slack = ISLEAFSLACK;
482 * Handle an empty B-tree as a special case.
484 if (fcb->nrecords == 0) {
485 (void)_allockpage(fcb, leafcapac, 0, pageblkno + 0);
486 return (pageblkno[0]);
490 * COMPRESS changes the fill factor to 100%
492 if ((pkeydesc2->k2_flags & COMPRESS) == COMPRESS)
496 * Figure out fill factors for each level.
498 perleaf = ((double)leafcapac * (100.0 - (double) slack)) / 100.0;
501 * Make it more robust.
503 if (perleaf >= leafcapac)
506 if (perleaf < leafcapac/2 + 1)
507 perleaf = leafcapac/2 + 1;
510 * Iterativelly determince values in keyspernode[] and one_mode[]
512 blocks_in_this_level = nrecords / perleaf;
513 if (blocks_in_this_level * leafcapac < nrecords)
514 blocks_in_this_level++;
516 keyspernode[0] = nrecords / blocks_in_this_level;
517 one_more[0] = nrecords % blocks_in_this_level;
520 for (depth = 1; blocks_in_this_level > 1; depth++) {
521 blocks_in_prev_level = blocks_in_this_level;
523 blocks_in_this_level = (blocks_in_prev_level-1) / nleafcapac + 1;
524 keyspernode[depth] = blocks_in_prev_level / blocks_in_this_level;
525 one_more[depth] = blocks_in_prev_level % blocks_in_this_level;
528 if (depth >= ISMAXBTRLEVEL)
529 _isfatal_error("Too many levels in B-tree");
532 * Make sure that we start reading keys from the beginning.
537 * Boot the Main loop.
539 for (i = 0; i < depth ; i++) {
540 curindex[i] = ISPAGESIZE; /* Any big number will do */
549 while ((keyp = _issort_read(srt)) != (char *) NULL) {
551 if (curindex[0] >= keyspernode[0] + (one_more[0] > 0)) {
554 * Commit all changed buffers here to avoid buffer pool overflow.
557 _isdisk_commit1(nodebufhd[0]);
561 /* Allocate new leaf. */
562 nodebufhd[0] = _allockpage(fcb, leafcapac, 0, pageblkno+0);
563 nodebuf[0] = nodebufhd[0]->isb_buffer;
568 /* Copy key into the page. */
569 memcpy( nodebuf[0] + BT_KEYS_OFF + keylength * curindex[0],keyp,
571 stshort((short) curindex[0] + 1, nodebuf[0] + BT_NKEYS_OFF);
573 if (curindex[0]++ == 0) { /* Store first key in */
576 for (i = 1;i < depth; i++) {
577 if (curindex[i] >= keyspernode[i] + (one_more[i] > 0)) {
581 _isdisk_commit1(nodebufhd[i]);
583 nodebufhd[i] = _allockpage(fcb, nleafcapac, i, pageblkno+i);
584 nodebuf[i] = nodebufhd[i]->isb_buffer;
589 /* Copy key into page. */
590 memcpy( nodebuf[i] + BT_KEYS_OFF + keylength * curindex[i],keyp,
593 /* Store pointer to level below. */
594 stblkno(pageblkno[i-1], nodebuf[i] + ISPAGESIZE -
595 (curindex[i]+1) * BLKNOSIZE);
597 stshort((short) curindex[i] + 1, nodebuf[i] + BT_NKEYS_OFF);
598 if (curindex[i]++ > 0) {
606 return (pageblkno [depth - 1]); /* Return blkno of B-tree root */
612 * Return 1 if there are duplicates in the sorted key object.
615 Static int _duplicate_exist(Issort *srt, int keylength)
618 char *lastkey = (char *) 0;
619 int netkeylength = keylength - RECNOSIZE;
623 while (curkey = _issort_read(srt)) {
624 if (lastkey && memcmp(lastkey + RECNOSIZE, curkey + RECNOSIZE,
626 return 1; /* Duplicate key found */
630 return 0; /* No duplicates found */
639 Bytearray *isfhandle2;
642 * Check that there are UNIX file descriptors available.
644 while (_watchfd_check() < 1) {
646 * Find victim (LRU FCB) and close it.
648 if((isfhandle2 = _mngfcb_victim()) == NULL)
649 _isfatal_error ("_openfcb() cannot find LRU victim");
651 fcb = _mngfcb_find(isfhandle2);
652 (void) _watchfd_decr(_isfcb_nfds(fcb));
654 _mngfcb_delete(isfhandle2);