/* * CDE - Common Desktop Environment * * Copyright (c) 1993-2012, The Open Group. All rights reserved. * * These libraries and programs are free software; you can * redistribute them and/or modify them under the terms of the GNU * Lesser General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * These libraries and programs are distributed in the hope that * they will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public * License along with these libraries and programs; if not, write * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth * Floor, Boston, MA 02110-1301 USA */ /* $XConsortium: keyfcns.c /main/3 1996/08/12 12:34:31 cde-ibm $ */ /* * COMPONENT_NAME: austext * * FUNCTIONS: NODE_WIDTH * Pi * close_slots * d_keyread * delete * expand * key_bldcom * key_boundary * key_close * key_cmpcpy * key_delete * key_found * key_init * key_insert * key_locpos * key_open * key_reset * key_scan * keycmp * node_search * open_slots * split_node * split_root * * ORIGINS: 157 * * OBJECT CODE ONLY SOURCE MATERIALS */ /*--------------------------------------------------------------------------- db_VISTA Key File/Field Manipulation Functions ---------------------------------------------- An implementation of the B-tree indexing method described in "Sorting and Searching: The Art of Computer Programming, Vol III", Knuth, Donald E., Addison-Wesley, 1975. pp 473-480. A more detailed description of the generic algorithms can be found in "Fundamentals of Data Structures in Pascal", Horowitz & Sahni, Computer Science Press, 1984. pp 491-512. A tutorial survey of B-tree methods can be found in "The Ubiquitous B-Tree", Comer, D., ACM Computing Surveys, Vol 11, No. 2, June 1979. (C) Copyright 1985, 1986, 1987 by Raima Corp. ---------------------------------------------------------------------------*/ /* ********************** EDIT HISTORY ******************************* SCR DATE INI DESCRIPTION ----- --------- --- ----------------------------------------------------- 158 15-JUN-88 RSC added compliment flag to key_bldcom 42 15-JUN-88 RSC make keynext 4X faster!! 04-Aug-88 RTK MULTI_TASK changes 310 10-Aug-88 RSC cleanup function prototypes 420 16-Sep-88 RTK Several missing FAR pointers 420 16-Sep-88 RTK np_ptr not initialized in d_prkeys 536 06-Jan-89 RSC pointer can go negative in node_search if 1st slot matches 21-Feb-89 RSC add consistency check to key_locpos 16-Mar-89 WLW reset table pointers in d_keyread */ #include #include "vista.h" #include "dbtype.h" /* Data definitions used for the B-tree algorithms */ /* node number of root */ #define ROOT_ADDR 1L /* null node pointer */ #define NULL_NODE -1L /* index file node structure */ typedef struct { LONG last_chgd; /* date/time of last change of this node */ INT used_slots; /* current # of used slots in node */ char slots[1]; /* start of slot area */ } NODE; /* Number of used slots plus orphan */ #define NODE_WIDTH(node) ((node)->used_slots*slot_len + sizeof(F_ADDR)) /* last status value */ #define KEYEOF -1 #define KEYINIT 0 #define KEYFOUND 1 #define KEYNOTFOUND 2 #define KEYREPOS 3 /* Internal function prototypes */ static int node_search(const char *, DB_ADDR *, NODE *, int *, int *, F_ADDR *); static int keycmp(const char *, KEY_SLOT *, DB_ADDR *); static int expand(const char *, DB_ADDR, F_ADDR); static int split_root(NODE *); static int split_node(F_ADDR, NODE *); static int delete(void); static void open_slots(NODE *, int, int); static void close_slots(NODE *, int, int); static void key_found(DB_ADDR *); static KEY_INFO *curkey; static int key_len; static int key_data; static int slot_len; static int max_slots; static int mid_slot; static int keyfile; static INT fldno; static FIELD_ENTRY *cfld_ptr; static INT keyno; static INT prefix; static int unique; /* Open B-tree key field index processing */ int key_open(void) { int fd_lc; /* loop control */ long t; /* total keys thru level l */ int l; /* level number */ int i; /* field subscript */ FIELD_ENTRY *fld_ptr; KEY_INFO *ki_ptr; FILE_ENTRY *file_ptr; /* child ptr key number */ key_data = sizeof(F_ADDR) + sizeof(INT); /* count total number of key fields */ no_of_keys = 0; for (fd_lc = size_fd - old_size_fd, fld_ptr = &field_table[old_size_fd]; --fd_lc >= 0; ++fld_ptr) { if (fld_ptr->fd_key != NOKEY ) ++no_of_keys; } if ( no_of_keys ) { key_info = /* Macro references must be on one line for some compilers */ (KEY_INFO *) ALLOC(&db_global.Key_info, no_of_keys*sizeof(KEY_INFO), "key_info"); if ( ! key_info ) return( dberr(S_NOMEMORY) ); for (i = 0, fld_ptr = &field_table[old_size_fd]; i < size_fd; ++i, ++fld_ptr) { if ( fld_ptr->fd_key != NOKEY ) { ki_ptr = &key_info[fld_ptr->fd_keyno]; ki_ptr->level = 0; ki_ptr->lstat = KEYINIT; ki_ptr->fldno = i; ki_ptr->keyfile = fld_ptr->fd_keyfile; ki_ptr->dba = NULL_DBA; file_ptr = &file_table[fld_ptr->fd_keyfile]; ki_ptr->keyval = /* Macro references must be on one line for some compilers */ ALLOC(&ki_ptr->Keyval, file_ptr->ft_slsize, db_avname); if ( ! ki_ptr->keyval ) return( dberr(S_NOMEMORY) ); MEM_UNLOCK(&ki_ptr->Keyval); /* compute maximum possible levels */ for (t = file_ptr->ft_slots, l = 1; t < MAXRECORDS; ++l) t *= file_ptr->ft_slots; ki_ptr->max_lvls = ++l; ki_ptr->node_path = (NODE_PATH *) ALLOC(&ki_ptr->Node_path, l*sizeof(NODE_PATH), db_avname); if ( ! ki_ptr->node_path ) return( dberr(S_NOMEMORY) ); byteset(ki_ptr->node_path, 0, l*sizeof(NODE_PATH)); MEM_UNLOCK(&ki_ptr->Node_path); } } } return( db_status = S_OKAY ); } /* Close key field processing */ void key_close(void) { int k; KEY_INFO *ki_ptr; if ( key_info ) { /* free memory allocated for key functions */ for (k = 0, ki_ptr = key_info; k < no_of_keys; ++k, ++ki_ptr) { MEM_UNLOCK(&ki_ptr->Node_path); FREE(&ki_ptr->Node_path); MEM_UNLOCK(&ki_ptr->Keyval); FREE(&ki_ptr->Keyval); } MEM_UNLOCK(&db_global.Key_info); FREE(&db_global.Key_info); } } /* Initialize key function operation */ int key_init( int field /* field number to be processed */ ) { FIELD_ENTRY *fld_ptr; FILE_ENTRY *file_ptr; fld_ptr = &field_table[field]; if ( fld_ptr->fd_key == NOKEY ) return( dberr(S_NOTKEY) ); fldno = field; cfld_ptr = fld_ptr; keyno = fld_ptr->fd_keyno; prefix = keyno - curr_db_table->key_offset; key_len = fld_ptr->fd_len; keyfile = fld_ptr->fd_keyfile; file_ptr = &file_table[keyfile]; slot_len = file_ptr->ft_slsize; max_slots = file_ptr->ft_slots; mid_slot = max_slots/2; curkey = &key_info[keyno]; unique = (fld_ptr->fd_key == UNIQUE); dio_setdef( keyfile ); return( db_status = S_OKAY ); } /* Reset key_info last status to reposition keys on file "fno" */ int key_reset(FILE_NO fno) { int i; KEY_INFO *ki_ptr; for (i = 0, ki_ptr = key_info; i < no_of_keys; ++i, ++ki_ptr) { if (((fno == size_ft) || (ki_ptr->keyfile == fno)) && ((ki_ptr->lstat == KEYFOUND) || (ki_ptr->lstat == KEYNOTFOUND))) ki_ptr->lstat = KEYREPOS; } return( db_status = S_OKAY ); } /* Locate proper key position on B-tree */ int key_locpos( const char *key_val, /* key search value */ DB_ADDR *dba /* database address of located key */ ) { NODE *node; /* pointer to current node */ F_ADDR child; /* page number of child node */ F_ADDR pg; /* page number of current node */ int stat; /* saves node search status */ int slot, slot_pos; int match_lvl; /* lowest level with duplicate key */ NODE_PATH *np_ptr; char *node_slot_ptr; match_lvl = -1; MEM_LOCK(&curkey->Node_path); for (curkey->level = 0, np_ptr = curkey->node_path, pg = ROOT_ADDR; TRUE; ++curkey->level, ++np_ptr, pg = child) { /* read in next node */ if ( dio_get(pg, (char * *)&node, NOPGHOLD) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } np_ptr->node = pg; if ( curkey->level == 0 && node->used_slots == 0 ) { np_ptr->slot = 0; curkey->lstat = KEYEOF; MEM_UNLOCK(&curkey->Node_path); return( db_status = S_NOTFOUND ); } else if (node->used_slots == 0) return( dberr( S_SYSERR ) ); /* non-root nodes can't be empty */ /* search node for key */ stat = node_search(key_val, ((*dba == NULL_DBA) ? NULL : dba), node, &slot, &slot_pos, &child); np_ptr->slot = slot; node_slot_ptr = &node->slots[slot_pos]; if ( stat == S_OKAY ) { if ( unique || *dba != NULL_DBA ) break; /* mark level as having matching key */ match_lvl = curkey->level; /* save the key value */ bytecpy(&key_type, node_slot_ptr, slot_len); } /* check for end of search */ if ( child == NULL_NODE ) break; } if ( match_lvl >= 0 ) { /* set to lowest duplicate key */ curkey->level = match_lvl; db_status = S_OKAY; curkey->lstat = KEYFOUND; } else if ( stat == S_OKAY ) { bytecpy(&key_type, node_slot_ptr, slot_len); db_status = S_OKAY; curkey->lstat = KEYFOUND; } else { /* key not found - save key at positioned slot */ if ( np_ptr->slot > 0 ) node_slot_ptr -= slot_len; bytecpy(&key_type, node_slot_ptr, slot_len); curkey->lstat = KEYNOTFOUND; db_status = S_NOTFOUND; } MEM_UNLOCK(&curkey->Node_path); MEM_LOCK(&curkey->Keyval); /* save key value and database address for possible repositioning */ bytecpy(curkey->keyval, key_type.ks.data, key_len); MEM_UNLOCK(&curkey->Keyval); bytecpy(&curkey->dba, &key_type.ks.data[key_len], sizeof(DB_ADDR)); /* return database address for d_keyfind */ if ( *dba == NULL_DBA ) bytecpy(dba, &curkey->dba, sizeof(DB_ADDR)); return( db_status ); } /* Search node for key value */ static int node_search( const char *key_val, /* key being searched */ DB_ADDR *dba, /* database address included in comparison if not null */ NODE *node, /* node being searched */ int *slotno, /* slot number of key position in node */ int *slot_offset, /* slot position offset */ F_ADDR *child /* child ptr of located key */ ) { int cmp, i, l, u, slot_pos; char *node_slot_ptr; /* perform binary search on node */ l = 0; u = node->used_slots - 1; while ( u >= l ) { i = (l + u)/2; node_slot_ptr = &node->slots[slot_pos = i*slot_len]; if ( (cmp = keycmp(key_val, (KEY_SLOT *)node_slot_ptr, dba)) < 0 ) u = i - 1; else if ( cmp > 0 ) l = i + 1; else if ( i && !unique && !dba ) { /* backup to lowest duplicate in node */ while (keycmp(key_val, (KEY_SLOT *)(node_slot_ptr -= slot_len), dba) == 0) { slot_pos -= slot_len; if (--i == 0) goto have_slot; } node_slot_ptr += slot_len; goto have_slot; } else goto have_slot; } have_slot: if ( cmp > 0 ) { /* always return next highest position */ ++i; node_slot_ptr += slot_len; slot_pos += slot_len; } /* return child pointer from located slot */ bytecpy(child, node_slot_ptr, sizeof(F_ADDR)); /* return slot number */ *slotno = i; *slot_offset = slot_pos; return( db_status = (cmp == 0 ? S_OKAY : S_NOTFOUND) ); } /* Compare key value */ static int keycmp( const char *key_val, /* key value */ KEY_SLOT *slot, /* pointer to key slot to be compared */ DB_ADDR *dba /* database address included in comparison if not null */ ) { /* returns < 0 if key_val < slot > 0 if key_val > slot = 0 if key_val == slot */ int cmp; if (((cmp = INTcmp((char *)&prefix, (char *)&slot->keyno)) == 0) && ((cmp = fldcmp(cfld_ptr, key_val, slot->data)) == 0) && dba) cmp = ADDRcmp(dba, (DB_ADDR *)&slot->data[key_len]); return( cmp ); } /* Scan thru key field */ int key_scan( int fcn, /* next or prev */ DB_ADDR *dba /* db address of scanned record */ ) { F_ADDR child; NODE *node; NODE_PATH *np_ptr; char *node_slot_ptr; /* locate next key on file */ switch ( curkey->lstat ) { case KEYINIT: case KEYEOF: return( key_boundary(((fcn == KEYNEXT) ? KEYFRST : KEYLAST), dba) ); case KEYREPOS: MEM_LOCK(&curkey->Keyval); key_locpos(curkey->keyval, &curkey->dba); MEM_UNLOCK(&curkey->Keyval); if (db_status != S_OKAY) break; /* PASS THROUGH */ case KEYFOUND: MEM_LOCK(&curkey->Node_path); if (fcn == KEYNEXT) ++curkey->node_path[curkey->level].slot; MEM_UNLOCK(&curkey->Node_path); } MEM_LOCK(&curkey->Node_path); np_ptr = &curkey->node_path[curkey->level]; if (dio_get(np_ptr->node, (char * *)&node, NOPGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } node_slot_ptr = &node->slots[np_ptr->slot*slot_len]; bytecpy(&child, node_slot_ptr, sizeof(F_ADDR)); if (child == NULL_NODE) { if (fcn == KEYPREV) { --np_ptr->slot; node_slot_ptr -= slot_len; } while (((fcn == KEYNEXT) && (np_ptr->slot >= node->used_slots)) || ((fcn == KEYPREV) && (np_ptr->slot < 0))) { if (curkey->level <= 0) { /* return end of file */ curkey->lstat = KEYEOF; MEM_UNLOCK(&curkey->Node_path); return( db_status = S_NOTFOUND ); } --curkey->level; node_slot_ptr = NULL; if (dio_get((--np_ptr)->node, (char * *)&node, NOPGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } if (fcn == KEYPREV) --np_ptr->slot; } if (node_slot_ptr == NULL) node_slot_ptr = &node->slots[np_ptr->slot*slot_len]; } else do { /* move down to leaf node */ if ( dio_get(child, (char * *)&node, NOPGHOLD) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } ++curkey->level; (++np_ptr)->node = child; if (fcn == KEYNEXT) { np_ptr->slot = 0; node_slot_ptr = node->slots; } else { np_ptr->slot = node->used_slots; node_slot_ptr = &node->slots[np_ptr->slot*slot_len]; } bytecpy(&child, node_slot_ptr, sizeof(F_ADDR)); } while ( child != NULL_NODE ); if (np_ptr->slot == node->used_slots) { --np_ptr->slot; node_slot_ptr -= slot_len; } bytecpy(&key_type, node_slot_ptr, slot_len); if (key_type.ks.keyno == prefix) key_found(dba); else { curkey->lstat = KEYEOF; db_status = S_NOTFOUND; } MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* Key has been found. Save appropriate information */ static void key_found(DB_ADDR *dba) { MEM_LOCK(&curkey->Keyval); /* save key value and database address for possible repositioning */ bytecpy(curkey->keyval, key_type.ks.data, key_len); MEM_UNLOCK(&curkey->Keyval); bytecpy(&curkey->dba, &key_type.ks.data[key_len], sizeof(DB_ADDR)); /* return found db addr */ *dba = curkey->dba; curkey->lstat = KEYFOUND; /*[42] FOUND a match */ db_status = S_OKAY; } /* Find key boundary */ int key_boundary( int fcn, /* KEYFRST or KEYLAST */ DB_ADDR *dba /* to get dba of first or last key */ ) { F_ADDR pg; /* node number */ NODE *node; /* pointer to node contents in cache */ int cmp = 0; /* keycmp result */ int match_lvl; /* lowest level containing matched key */ int lvl; /* node_path level variable */ int slot; /* slot position in node */ NODE_PATH *np_ptr; char *node_slot_ptr; if ( fcn == KEYFIND ) { curkey->lstat = KEYINIT; return( db_status = S_OKAY ); } curkey->lstat = KEYNOTFOUND; /* traverse B-tree for first or last key with specified prefix */ match_lvl = -1; pg = ROOT_ADDR; MEM_LOCK(&curkey->Node_path); for (lvl = 0; TRUE; ++lvl) { /* read next node */ if ( dio_get(pg, (char * *)&node, NOPGHOLD) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } if ( node->used_slots == 0 ) { /* must not be anything on file */ curkey->lstat = KEYEOF; MEM_UNLOCK(&curkey->Node_path); return( db_status = S_NOTFOUND ); } if ( fcn == KEYFRST ) { for (slot = 0, node_slot_ptr = node->slots; slot < node->used_slots; ++slot, node_slot_ptr += slot_len) { if ((cmp = INTcmp((char *)&prefix, (char *)(node_slot_ptr + sizeof(F_ADDR)))) <= 0) break; } } else { /* KEYLAST */ for (slot = node->used_slots - 1, node_slot_ptr = &node->slots[slot*slot_len]; slot >= 0; --slot, node_slot_ptr -= slot_len) { if ((cmp = INTcmp((char *)&prefix, (char *)(node_slot_ptr + sizeof(F_ADDR)))) >= 0) break; } } /* save node path position */ np_ptr = &curkey->node_path[lvl]; np_ptr->node = pg; np_ptr->slot = slot; if ( cmp == 0 ) { /* save matched level & key value */ match_lvl = lvl; bytecpy(&key_type, node_slot_ptr, slot_len); } /* fetch appropriate child pointer */ if (fcn == KEYLAST) node_slot_ptr += slot_len; bytecpy(&pg, node_slot_ptr, sizeof(F_ADDR)); if ( pg == NULL_NODE ) break; } if ( match_lvl >= 0 ) { curkey->level = match_lvl; key_found(dba); curkey->lstat = KEYREPOS; /*[42] Need to reposition */ } else { /* no keys on file with requested prefix */ curkey->level = 0; curkey->lstat = KEYEOF; db_status = S_NOTFOUND; } MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* Insert key field into B-tree */ int key_insert( int fld, /* key field number */ const char *key_val, /* key value */ DB_ADDR dba /* record's database address */ ) { int stat; /* initialize key function operation */ if ( key_init(fld) != S_OKAY ) return( db_status ); /* locate insertion point */ if ( key_locpos(key_val, &dba) == S_NOTFOUND ) { /* expand node to include key */ if ( (stat = expand(key_val, dba, NULL_NODE)) == S_OKAY ) { /* save key value and database address for possible repositioning */ MEM_LOCK(&curkey->Keyval); bytecpy(curkey->keyval, key_val, key_len); MEM_UNLOCK(&curkey->Keyval); curkey->dba = dba; /* reset key position */ key_reset(curkey->keyfile); } db_status = stat; } else if ( db_status == S_OKAY ) dberr(S_SYSERR); return( db_status ); } /* Expand node for new key */ static int expand( const char *key_val, /* key value */ DB_ADDR dba, /* record's database address */ F_ADDR brother /* page number of brother node */ ) { F_ADDR pg; NODE *node; NODE_PATH *np_ptr; int slot_pos; char *node_slot_ptr; MEM_LOCK(&curkey->Node_path); np_ptr = &curkey->node_path[curkey->level]; if (dio_get(pg = np_ptr->node, (char * *)&node, PGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } if ( node->used_slots >= max_slots ) { MEM_UNLOCK(&curkey->Node_path); return( dberr(S_KEYERR) ); } node_slot_ptr = &node->slots[slot_pos = np_ptr->slot*slot_len]; open_slots(node, slot_pos, 1); /* copy brother into opened slot's child pointer */ bytecpy(node_slot_ptr + slot_len, &brother, sizeof(F_ADDR)); /* copy keyno into current slot */ bytecpy(node_slot_ptr + sizeof(F_ADDR), &prefix, sizeof(INT)); node_slot_ptr += key_data; /* clear slot data area to zeros */ byteset(node_slot_ptr, 0, slot_len - key_data); /* copy keyval into current slot */ if ( cfld_ptr->fd_type == CHARACTER && cfld_ptr->fd_dim[1] == 0 ) strncpy(node_slot_ptr, key_val, key_len); else bytecpy(node_slot_ptr, key_val, key_len); /* copy database address into current slot */ bytecpy(node_slot_ptr + key_len, &dba, sizeof(DB_ADDR)); if ( node->used_slots == max_slots ) { if ( pg == ROOT_ADDR ) split_root(node); else split_node(pg, node); } else dio_touch(pg); MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* Split node into two nodes */ static int split_node( F_ADDR l_pg, /* left node's page number */ NODE *l_node /* left node buffer */ ) { F_ADDR r_pg; NODE *r_node; char key_val[256]; DB_ADDR dba; char *l_node_slot_ptr; /* extract middle key */ l_node_slot_ptr = &l_node->slots[mid_slot*slot_len]; bytecpy(&prefix, l_node_slot_ptr + sizeof(F_ADDR), sizeof(INT)); keyno = prefix + curr_db_table->key_offset; fldno = key_info[keyno].fldno; cfld_ptr = &field_table[fldno]; key_len = cfld_ptr->fd_len; bytecpy(key_val, l_node_slot_ptr + key_data, key_len); bytecpy(&dba, l_node_slot_ptr + key_data + key_len, sizeof(DB_ADDR)); /* divide left node */ l_node->used_slots = mid_slot; /* allocate new right node */ if ((dio_pzalloc(keyfile, &r_pg) != S_OKAY) || (dio_get(r_pg, (char * *)&r_node, PGHOLD) != S_OKAY)) return( db_status ); /* copy slots from left node at slot mid_slot+1 into right node */ r_node->used_slots = max_slots - (mid_slot + 1); l_node_slot_ptr += slot_len; bytecpy(r_node->slots, l_node_slot_ptr, NODE_WIDTH(r_node)); dio_touch(l_pg); dio_touch(r_pg); --curkey->level; /* expand parent slot to include middle key and new right node ptr */ return( expand(key_val, dba, r_pg) ); } /* Split root node */ static int split_root(NODE *node) { F_ADDR l_pg, r_pg; NODE *l_node, *r_node; int slot_pos; char *node_slot_ptr; /* allocate two new nodes */ if ((dio_pzalloc(keyfile, &l_pg) != S_OKAY) || (dio_pzalloc(keyfile, &r_pg) != S_OKAY) || (dio_get(l_pg, (char * *)&l_node, PGHOLD) != S_OKAY) || (dio_get(r_pg, (char * *)&r_node, PGHOLD) != S_OKAY)) return( db_status ); /* copy 0..max_slots/2-1 keys from root into left node */ l_node->used_slots = mid_slot; slot_pos = mid_slot*slot_len; bytecpy(l_node->slots, node->slots, NODE_WIDTH(l_node)); /* copy max_slots/2+1..max_slots from root into right node */ r_node->used_slots = max_slots - (mid_slot + 1); node_slot_ptr = &node->slots[slot_pos += slot_len]; bytecpy(r_node->slots, node_slot_ptr, NODE_WIDTH(r_node)); /* copy mid_slot into slot[0] of root */ bytecpy(node->slots, node_slot_ptr - slot_len, slot_len); /* copy left page number into p[0] of root */ bytecpy(node->slots, &l_pg, sizeof(F_ADDR)); /* copy right page number into p[1] of root */ bytecpy(&node->slots[slot_len], &r_pg, sizeof(F_ADDR)); /* reset number of used slots in root */ node->used_slots = 1; dio_touch(l_pg); dio_touch(r_pg); dio_touch(ROOT_ADDR); return( db_status ); } /* Delete key from B-tree */ int key_delete(int fld, char const *key_val, DB_ADDR dba) { int stat; /* initialize key function operation */ if ( key_init(fld) != S_OKAY ) return( db_status ); /* locate key to be deleted */ if ((stat = key_locpos(key_val, &dba)) == S_OKAY) { if ( (stat = delete()) == S_OKAY ) { /* save key value and database address for possible repositioning */ MEM_LOCK(&curkey->Keyval); bytecpy(curkey->keyval, key_val, key_len); MEM_UNLOCK(&curkey->Keyval); curkey->dba = dba; /* reset key position */ key_reset(curkey->keyfile); } } return( db_status = stat ); } /* Delete key at current node_path position */ static int delete(void) { F_ADDR pg, p_pg, l_pg, r_pg; NODE *node; NODE *p_node; NODE *l_node; NODE *r_node; int amt, slot_pos; NODE_PATH *np_ptr; char *node_slot_ptr; char *p_node_slot_ptr; char *l_node_slot_ptr; char *r_node_slot_ptr; MEM_LOCK(&curkey->Node_path); np_ptr = &curkey->node_path[curkey->level]; /* read node containing key to be deleted */ if (dio_get(pg = np_ptr->node, (char * *)&node, PGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* copy pointer to right sub-tree */ slot_pos = np_ptr->slot*slot_len; node_slot_ptr = &node->slots[slot_pos]; bytecpy(&r_pg, node_slot_ptr + slot_len, sizeof(F_ADDR)); if ( r_pg != NULL_NODE ) { /* find leftmost descendent of right sub-tree */ ++np_ptr->slot; do { if ( dio_get(r_pg, (char * *)&r_node, NOPGHOLD) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } ++curkey->level; ++np_ptr; np_ptr->node = r_pg; np_ptr->slot = 0; bytecpy(&r_pg, r_node->slots, sizeof(F_ADDR)); } while ( r_pg != NULL_NODE ); /* copy key from leaf into node */ node_slot_ptr += sizeof(F_ADDR); r_node_slot_ptr = &r_node->slots[sizeof(F_ADDR)]; bytecpy(node_slot_ptr, r_node_slot_ptr, slot_len - sizeof(F_ADDR)); dio_touch(pg); /* set up to delete key from leaf */ /* (this is more efficient than a recursive call) */ slot_pos = 0; node_slot_ptr = node->slots; if (dio_get(pg = np_ptr->node, (char * *)&node, PGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } } shrink: /* delete key from leaf (shrink node ) */ close_slots(node, slot_pos, 1); /* check if necessary to adjust nodes */ if ((curkey->level > 0) && (node->used_slots < mid_slot)) { /* read in parent node */ if (dio_get(p_pg = (np_ptr - 1)->node, (char * *)&p_node, PGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } slot_pos = (np_ptr - 1)->slot*slot_len; node_slot_ptr = &node->slots[slot_pos]; if ((np_ptr - 1)->slot == p_node->used_slots ) { /* pg is right node */ r_pg = pg; r_node = node; /* parent slot position should bisect left & right nodes */ --(np_ptr - 1)->slot; slot_pos -= slot_len; /* read left node */ p_node_slot_ptr = &p_node->slots[slot_pos]; bytecpy(&l_pg, p_node_slot_ptr, sizeof(F_ADDR)); if ( dio_get(l_pg, (char * *)&l_node, PGHOLD) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } } else { /* pg is left node */ l_pg = pg; l_node = node; /* read right node */ p_node_slot_ptr = &p_node->slots[slot_pos + slot_len]; bytecpy(&r_pg, p_node_slot_ptr, sizeof(F_ADDR)); if (dio_get(r_pg, (char * *)&r_node, PGHOLD) != S_OKAY) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } } if ((l_node->used_slots + r_node->used_slots + 1) < max_slots) { /* combine left and right nodes */ if ((curkey->level == 1) && (p_node->used_slots == 1)) { /* shrink down to root */ /* copy right node data into root */ p_node_slot_ptr = &p_node->slots[slot_len]; bytecpy(p_node_slot_ptr, r_node->slots, NODE_WIDTH(r_node)); p_node->used_slots = r_node->used_slots + 1; r_node->used_slots = 0; dio_touch(r_pg); /* copy left node data into root */ open_slots(p_node, 0, l_node->used_slots); bytecpy(p_node->slots, l_node->slots, NODE_WIDTH(l_node)); l_node->used_slots = 0; dio_touch(l_pg); dio_touch(p_pg); /* free node pages */ dio_pzdel(keyfile, r_pg); dio_pzdel(keyfile, l_pg); MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* open space for l_node->used_slots+1 slots in right node */ open_slots(r_node, 0, l_node->used_slots + 1); /* move left node slots into right node */ amt = NODE_WIDTH(l_node); r_node_slot_ptr = r_node->slots; bytecpy(r_node_slot_ptr, l_node->slots, amt); /* move parent slot data into right node */ r_node_slot_ptr += amt; p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)]; bytecpy(r_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR)); dio_touch(r_pg); dio_touch(l_pg); /* delete left node */ l_node->used_slots = 0; if ( dio_pzdel(keyfile, l_pg) != S_OKAY ) { MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* decrement level & make parent node current node */ --curkey->level; --np_ptr; pg = p_pg; node = p_node; goto shrink; /* delete slot from parent */ } /* acquire needed key from sibling */ if ((l_node->used_slots + 1) < r_node->used_slots) { /* get key from right node */ /* move parent slot to end of left node */ l_node_slot_ptr = &l_node->slots[NODE_WIDTH(l_node)]; p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)]; bytecpy(l_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR)); ++l_node->used_slots; /* copy slot 0 child from right node to left node orphan */ l_node_slot_ptr += slot_len - sizeof(F_ADDR); r_node_slot_ptr = r_node->slots; bytecpy(l_node_slot_ptr, r_node_slot_ptr, sizeof(F_ADDR)); /* move slot 0 of right node to parent */ r_node_slot_ptr += sizeof(F_ADDR); bytecpy(p_node_slot_ptr, r_node_slot_ptr, slot_len - sizeof(F_ADDR)); /* delete slot 0 from right node */ close_slots(r_node, 0, 1); } else if ((r_node->used_slots + 1) < l_node->used_slots) { /* get key from left node */ /* open one slot at front of right node */ open_slots(r_node, 0, 1); /* move parent slot to slot 0 of right node */ r_node_slot_ptr = &r_node->slots[sizeof(F_ADDR)]; p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)]; bytecpy(r_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR)); /* move end slot of left node to parent */ l_node_slot_ptr = &l_node->slots[NODE_WIDTH(l_node) - slot_len]; bytecpy(p_node_slot_ptr, l_node_slot_ptr, slot_len - sizeof(F_ADDR)); /* move left orphan to child of slot 0 in right node */ l_node_slot_ptr += slot_len - sizeof(F_ADDR); bytecpy(r_node->slots, l_node_slot_ptr, sizeof(F_ADDR)); /* delete end slot from left node */ --l_node->used_slots; } dio_touch(l_pg); dio_touch(r_pg); dio_touch(p_pg); } else { dio_touch(pg); } MEM_UNLOCK(&curkey->Node_path); return( db_status ); } /* Open n slots in node */ static void open_slots(NODE *node, int slot_pos, int n) { char *dst, *src; int amt, w, nw; nw = NODE_WIDTH(node); w = n*slot_len; src = &node->slots[nw]; dst = src + w; amt = nw - slot_pos; while (amt--) *--dst = *--src; node->used_slots += n; } /* Close n slots in node */ static void close_slots(NODE *node, int slot_pos, int n) { char *dst, *src; int w, amt; node->used_slots -= n; w = n*slot_len; dst = &node->slots[slot_pos]; src = dst + w; amt = NODE_WIDTH(node) - slot_pos; while (amt--) *dst++ = *src++; } /* Read value of last key scanned */ int d_keyread(char *key_val) { int kt_lc; /* loop control */ #ifndef NO_FLOAT float fv; double dv; #endif char *fptr; char *kptr; FIELD_ENTRY *fld_ptr; KEY_ENTRY *key_ptr; DB_ENTER(NO_DB_ID TASK_ID LOCK_SET(RECORD_IO)); if ((curkey->lstat != KEYFOUND) && (curkey->lstat != KEYNOTFOUND) && (curkey->lstat != KEYREPOS)) { RETURN( dberr(S_KEYSEQ) ); } /* clear key area */ byteset(key_val, '\0', cfld_ptr->fd_len); if ( cfld_ptr->fd_type == COMKEY ) { /* copy compound key fields */ for (kt_lc = size_kt - cfld_ptr->fd_ptr, key_ptr = &key_table[cfld_ptr->fd_ptr]; (--kt_lc >= 0) && (key_ptr->kt_key == fldno); ++key_ptr) { fld_ptr = &field_table[key_ptr->kt_field]; fptr = key_type.ks.data + key_ptr->kt_ptr; kptr = key_val + key_ptr->kt_ptr; if ( key_ptr->kt_sort == 'd' ) { switch ( fld_ptr->fd_type ) { #ifndef NO_FLOAT case FLOAT: bytecpy(&fv, fptr, sizeof(float)); fv = (float)0.0 - fv; bytecpy(kptr, &fv, sizeof(float)); break; case DOUBLE: bytecpy(&dv, fptr, sizeof(double)); dv = 0.0 - dv; bytecpy(kptr, &dv, sizeof(double)); break; #endif case CHARACTER: key_cmpcpy(kptr, fptr, fld_ptr->fd_len); if ( fld_ptr->fd_dim[0] > 1 && fld_ptr->fd_dim[1] == 0 ) { /* make sure a null byte is at the end */ kptr[fld_ptr->fd_len-1] = '\0'; } break; default: key_cmpcpy(kptr, fptr, fld_ptr->fd_len); } } else { bytecpy(kptr, fptr, fld_ptr->fd_len); } } } else if ( cfld_ptr->fd_type == CHARACTER && cfld_ptr->fd_dim[1] == 0 ) strncpy(key_val, key_type.ks.data, key_len); else bytecpy(key_val, key_type.ks.data, key_len); RETURN( db_status = S_OKAY ); } /* Build compound key value from record */ int key_bldcom( int fld, /* compound key field number */ char *rec, /* ptr to record data */ char *key, /* ptr to array to recv constructed key */ int cflag /* TRUE to compliment compound descending keys */ ) { int kt_lc; /* loop control */ #ifndef NO_FLOAT float fv; double dv; #endif char *fptr; FIELD_ENTRY *fld_ptr, *kfld_ptr; KEY_ENTRY *key_ptr; /* clear key area */ fld_ptr = &field_table[fld]; byteset(key, '\0', fld_ptr->fd_len); /* create compound key value */ rec -= record_table[fld_ptr->fd_rec].rt_data; for (kt_lc = size_kt - fld_ptr->fd_ptr, key_ptr = &key_table[fld_ptr->fd_ptr]; (--kt_lc >= 0) && (key_ptr->kt_key == fld); ++key_ptr) { kfld_ptr = &field_table[key_ptr->kt_field]; fptr = rec + kfld_ptr->fd_ptr; /* Complement descending keys if permitted (cflag) */ if ( cflag && ( key_ptr->kt_sort == 'd' )) { switch ( kfld_ptr->fd_type ) { #ifndef NO_FLOAT case FLOAT: bytecpy(&fv, fptr, sizeof(float)); fv = (float)0.0 - fv; bytecpy(&key[key_ptr->kt_ptr], &fv, sizeof(float)); break; case DOUBLE: bytecpy(&dv, fptr, sizeof(double)); dv = 0.0 - dv; bytecpy(&key[key_ptr->kt_ptr], &dv, sizeof(double)); break; #endif case CHARACTER: key_cmpcpy(key+key_ptr->kt_ptr, fptr, kfld_ptr->fd_len); if ( kfld_ptr->fd_dim[0] > 1 && kfld_ptr->fd_dim[1] == 0 ) { /* make sure a null byte is at the end */ *(key + key_ptr->kt_ptr + kfld_ptr->fd_len - 1) = '\0'; } break; default: key_cmpcpy(key+key_ptr->kt_ptr, fptr, kfld_ptr->fd_len); } } else { bytecpy(&key[key_ptr->kt_ptr], fptr, kfld_ptr->fd_len); } } return( db_status = S_OKAY ); } /* Complement and copy bytes */ void key_cmpcpy(char *s1, char *s2, INT n) { while ( n-- ) { *s1++ = ~(*s2++); } } /* vpp -nOS2 -dUNIX -nBSD -nVANILLA_BSD -nVMS -nMEMLOCK -nWINDOWS -nFAR_ALLOC -f/usr/users/master/config/nonwin keyfcns.c */