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 /* $XConsortium: keyfcns.c /main/3 1996/08/12 12:34:31 cde-ibm $ */
25 * COMPONENT_NAME: austext
27 * FUNCTIONS: NODE_WIDTH
53 * OBJECT CODE ONLY SOURCE MATERIALS
55 /*---------------------------------------------------------------------------
56 db_VISTA Key File/Field Manipulation Functions
57 ----------------------------------------------
58 An implementation of the B-tree indexing method described in
59 "Sorting and Searching: The Art of Computer Programming, Vol III",
60 Knuth, Donald E., Addison-Wesley, 1975. pp 473-480.
62 A more detailed description of the generic algorithms can be found
63 in "Fundamentals of Data Structures in Pascal", Horowitz & Sahni,
64 Computer Science Press, 1984. pp 491-512.
66 A tutorial survey of B-tree methods can be found in "The Ubiquitous
67 B-Tree", Comer, D., ACM Computing Surveys, Vol 11, No. 2, June 1979.
69 (C) Copyright 1985, 1986, 1987 by Raima Corp.
71 ---------------------------------------------------------------------------*/
73 /* ********************** EDIT HISTORY *******************************
75 SCR DATE INI DESCRIPTION
76 ----- --------- --- -----------------------------------------------------
77 158 15-JUN-88 RSC added compliment flag to key_bldcom
78 42 15-JUN-88 RSC make keynext 4X faster!!
79 04-Aug-88 RTK MULTI_TASK changes
80 310 10-Aug-88 RSC cleanup function prototypes
81 420 16-Sep-88 RTK Several missing FAR pointers
82 420 16-Sep-88 RTK np_ptr not initialized in d_prkeys
83 536 06-Jan-89 RSC pointer can go negative in node_search if 1st slot matches
84 21-Feb-89 RSC add consistency check to key_locpos
85 16-Mar-89 WLW reset table pointers in d_keyread
92 /* Data definitions used for the B-tree algorithms */
94 /* node number of root */
97 /* null node pointer */
100 /* index file node structure */
102 LONG last_chgd; /* date/time of last change of this node */
103 INT used_slots; /* current # of used slots in node */
104 char slots[1]; /* start of slot area */
106 /* Number of used slots plus orphan */
107 #define NODE_WIDTH(node) ((node)->used_slots*slot_len + sizeof(F_ADDR))
109 /* last status value */
113 #define KEYNOTFOUND 2
116 /* Internal function prototypes */
117 static int node_search(P1(CONST char FAR *) Pi(DB_ADDR FAR *)
118 Pi(NODE FAR *) Pi(int *) Pi(int *)
120 static int keycmp(P1(CONST char FAR *) Pi(KEY_SLOT FAR *)
122 static int expand(P1(CONST char FAR *) Pi(DB_ADDR) Pi(F_ADDR));
123 static int split_root(P1(NODE FAR *));
124 static int split_node(P1(F_ADDR) Pi(NODE FAR *));
125 static int delete(P0);
126 static void open_slots(P1(NODE FAR *) Pi(int) Pi(int));
127 static void close_slots(P1(NODE FAR *) Pi(int) Pi(int));
128 static void key_found(P1(DB_ADDR *));
134 static KEY_INFO FAR *curkey;
138 static int max_slots;
142 static FIELD_ENTRY FAR *cfld_ptr;
150 /* Open B-tree key field index processing
154 register int fd_lc; /* loop control */
155 long t; /* total keys thru level l */
156 int l; /* level number */
157 register int i; /* field subscript */
158 register FIELD_ENTRY FAR *fld_ptr;
159 register KEY_INFO FAR *ki_ptr;
160 FILE_ENTRY FAR *file_ptr;
162 /* child ptr key number */
163 key_data = sizeof(F_ADDR) + sizeof(INT);
165 /* count total number of key fields */
167 for (fd_lc = size_fd - old_size_fd, fld_ptr = &field_table[old_size_fd];
168 --fd_lc >= 0; ++fld_ptr) {
169 if (fld_ptr->fd_key != NOKEY )
174 /* Macro references must be on one line for some compilers */
176 ALLOC(&db_global.Key_info, no_of_keys*sizeof(KEY_INFO), "key_info");
178 return( dberr(S_NOMEMORY) );
179 for (i = 0, fld_ptr = &field_table[old_size_fd];
180 i < size_fd; ++i, ++fld_ptr) {
181 if ( fld_ptr->fd_key != NOKEY ) {
182 ki_ptr = &key_info[fld_ptr->fd_keyno];
184 ki_ptr->lstat = KEYINIT;
186 ki_ptr->keyfile = fld_ptr->fd_keyfile;
187 ki_ptr->dba = NULL_DBA;
188 file_ptr = &file_table[fld_ptr->fd_keyfile];
190 /* Macro references must be on one line for some compilers */
191 ALLOC(&ki_ptr->Keyval, file_ptr->ft_slsize, db_avname);
192 if ( ! ki_ptr->keyval )
193 return( dberr(S_NOMEMORY) );
194 MEM_UNLOCK(&ki_ptr->Keyval);
195 /* compute maximum possible levels */
196 for (t = file_ptr->ft_slots, l = 1; t < MAXRECORDS; ++l)
197 t *= file_ptr->ft_slots;
198 ki_ptr->max_lvls = ++l;
201 ALLOC(&ki_ptr->Node_path, l*sizeof(NODE_PATH), db_avname);
202 if ( ! ki_ptr->node_path )
203 return( dberr(S_NOMEMORY) );
204 byteset(ki_ptr->node_path, 0, l*sizeof(NODE_PATH));
205 MEM_UNLOCK(&ki_ptr->Node_path);
209 return( db_status = S_OKAY );
214 /* Close key field processing
219 KEY_INFO FAR *ki_ptr;
222 /* free memory allocated for key functions */
223 for (k = 0, ki_ptr = key_info; k < no_of_keys; ++k, ++ki_ptr) {
224 MEM_UNLOCK(&ki_ptr->Node_path);
225 FREE(&ki_ptr->Node_path);
226 MEM_UNLOCK(&ki_ptr->Keyval);
227 FREE(&ki_ptr->Keyval);
229 MEM_UNLOCK(&db_global.Key_info);
230 FREE(&db_global.Key_info);
235 /* Initialize key function operation
238 int field; /* field number to be processed */
240 FIELD_ENTRY FAR *fld_ptr;
241 FILE_ENTRY FAR *file_ptr;
243 fld_ptr = &field_table[field];
244 if ( fld_ptr->fd_key == NOKEY )
245 return( dberr(S_NOTKEY) );
249 keyno = fld_ptr->fd_keyno;
251 prefix = keyno - curr_db_table->key_offset;
253 key_len = fld_ptr->fd_len;
254 keyfile = fld_ptr->fd_keyfile;
255 file_ptr = &file_table[keyfile];
256 slot_len = file_ptr->ft_slsize;
257 max_slots = file_ptr->ft_slots;
258 mid_slot = max_slots/2;
259 curkey = &key_info[keyno];
260 unique = (fld_ptr->fd_key == UNIQUE);
261 dio_setdef( keyfile );
263 return( db_status = S_OKAY );
268 /* Reset key_info last status to reposition keys on file "fno"
274 register KEY_INFO FAR *ki_ptr;
276 for (i = 0, ki_ptr = key_info; i < no_of_keys; ++i, ++ki_ptr) {
277 if (((fno == size_ft) || (ki_ptr->keyfile == fno)) &&
278 ((ki_ptr->lstat == KEYFOUND) || (ki_ptr->lstat == KEYNOTFOUND)))
279 ki_ptr->lstat = KEYREPOS;
281 return( db_status = S_OKAY );
286 /* Locate proper key position on B-tree
288 key_locpos(key_val, dba)
289 CONST char FAR *key_val; /* key search value */
290 DB_ADDR FAR *dba; /* database address of located key */
292 NODE FAR *node; /* pointer to current node */
293 F_ADDR child; /* page number of child node */
294 F_ADDR pg; /* page number of current node */
295 int stat; /* saves node search status */
297 int match_lvl; /* lowest level with duplicate key */
298 NODE_PATH FAR *np_ptr;
299 char FAR *node_slot_ptr;
302 MEM_LOCK(&curkey->Node_path);
303 for (curkey->level = 0, np_ptr = curkey->node_path, pg = ROOT_ADDR;
305 ++curkey->level, ++np_ptr, pg = child) {
306 /* read in next node */
307 if ( dio_get(pg, (char FAR * FAR *)&node, NOPGHOLD) != S_OKAY ) {
308 MEM_UNLOCK(&curkey->Node_path);
313 if ( curkey->level == 0 && node->used_slots == 0 ) {
315 curkey->lstat = KEYEOF;
316 MEM_UNLOCK(&curkey->Node_path);
317 return( db_status = S_NOTFOUND );
319 else if (node->used_slots == 0)
320 return( dberr( S_SYSERR ) ); /* non-root nodes can't be empty */
322 /* search node for key */
323 stat = node_search(key_val, ((*dba == NULL_DBA) ? NULL : dba), node,
324 &slot, &slot_pos, &child);
327 node_slot_ptr = &node->slots[slot_pos];
328 if ( stat == S_OKAY ) {
329 if ( unique || *dba != NULL_DBA )
332 /* mark level as having matching key */
333 match_lvl = curkey->level;
335 /* save the key value */
336 bytecpy(&key_type, node_slot_ptr, slot_len);
338 /* check for end of search */
339 if ( child == NULL_NODE )
342 if ( match_lvl >= 0 ) {
343 /* set to lowest duplicate key */
344 curkey->level = match_lvl;
346 curkey->lstat = KEYFOUND;
348 else if ( stat == S_OKAY ) {
349 bytecpy(&key_type, node_slot_ptr, slot_len);
351 curkey->lstat = KEYFOUND;
354 /* key not found - save key at positioned slot */
355 if ( np_ptr->slot > 0 )
356 node_slot_ptr -= slot_len;
357 bytecpy(&key_type, node_slot_ptr, slot_len);
358 curkey->lstat = KEYNOTFOUND;
359 db_status = S_NOTFOUND;
361 MEM_UNLOCK(&curkey->Node_path);
362 MEM_LOCK(&curkey->Keyval);
363 /* save key value and database address for possible repositioning */
364 bytecpy(curkey->keyval, key_type.ks.data, key_len);
365 MEM_UNLOCK(&curkey->Keyval);
366 bytecpy(&curkey->dba, &key_type.ks.data[key_len], sizeof(DB_ADDR));
368 /* return database address for d_keyfind */
369 if ( *dba == NULL_DBA )
370 bytecpy(dba, &curkey->dba, sizeof(DB_ADDR));
377 /* Search node for key value
379 static int node_search(key_val, dba, node, slotno, slot_offset,
381 CONST char FAR *key_val; /* key being searched */
382 DB_ADDR FAR *dba; /* database address included in comparison if not null */
383 NODE FAR *node; /* node being searched */
384 int *slotno; /* slot number of key position in node */
385 int *slot_offset; /* slot position offset */
386 F_ADDR *child; /* child ptr of located key */
388 register int cmp, i, l, u, slot_pos;
389 char FAR *node_slot_ptr;
391 /* perform binary search on node */
393 u = node->used_slots - 1;
396 node_slot_ptr = &node->slots[slot_pos = i*slot_len];
397 if ( (cmp = keycmp(key_val, (KEY_SLOT FAR *)node_slot_ptr, dba)) < 0 )
401 else if ( i && !unique && !dba ) {
402 /* backup to lowest duplicate in node */
403 while (keycmp(key_val, (KEY_SLOT FAR *)(node_slot_ptr -= slot_len),
405 slot_pos -= slot_len;
406 if (--i == 0) goto have_slot;
408 node_slot_ptr += slot_len;
415 if ( cmp > 0 ) { /* always return next highest position */
417 node_slot_ptr += slot_len;
418 slot_pos += slot_len;
420 /* return child pointer from located slot */
421 bytecpy(child, node_slot_ptr, sizeof(F_ADDR));
423 /* return slot number */
425 *slot_offset = slot_pos;
427 return( db_status = (cmp == 0 ? S_OKAY : S_NOTFOUND) );
434 static int keycmp(key_val, slot, dba)
435 CONST char FAR *key_val; /* key value */
436 KEY_SLOT FAR *slot; /* pointer to key slot to be compared */
437 DB_ADDR FAR *dba; /* database address included in comparison if not null */
440 returns < 0 if key_val < slot
441 > 0 if key_val > slot
442 = 0 if key_val == slot
446 if (((cmp = INTcmp((char FAR *)&prefix, (char FAR *)&slot->keyno)) == 0) &&
447 ((cmp = fldcmp(cfld_ptr, key_val, slot->data)) == 0) &&
449 cmp = ADDRcmp(dba, (DB_ADDR FAR *)&slot->data[key_len]);
455 /* Scan thru key field
458 int fcn; /* next or prev */
459 DB_ADDR *dba; /* db address of scanned record */
463 NODE_PATH FAR *np_ptr;
464 char FAR *node_slot_ptr;
466 /* locate next key on file */
467 switch ( curkey->lstat ) {
470 return( key_boundary(((fcn == KEYNEXT) ? KEYFRST : KEYLAST), dba) );
472 MEM_LOCK(&curkey->Keyval);
473 key_locpos(curkey->keyval, &curkey->dba);
474 MEM_UNLOCK(&curkey->Keyval);
475 if (db_status != S_OKAY)
479 MEM_LOCK(&curkey->Node_path);
481 ++curkey->node_path[curkey->level].slot;
482 MEM_UNLOCK(&curkey->Node_path);
484 MEM_LOCK(&curkey->Node_path);
485 np_ptr = &curkey->node_path[curkey->level];
486 if (dio_get(np_ptr->node, (char FAR * FAR *)&node, NOPGHOLD) != S_OKAY) {
487 MEM_UNLOCK(&curkey->Node_path);
490 node_slot_ptr = &node->slots[np_ptr->slot*slot_len];
491 bytecpy(&child, node_slot_ptr, sizeof(F_ADDR));
492 if (child == NULL_NODE) {
493 if (fcn == KEYPREV) {
495 node_slot_ptr -= slot_len;
497 while (((fcn == KEYNEXT) && (np_ptr->slot >= node->used_slots)) ||
498 ((fcn == KEYPREV) && (np_ptr->slot < 0))) {
499 if (curkey->level <= 0) {
500 /* return end of file */
501 curkey->lstat = KEYEOF;
502 MEM_UNLOCK(&curkey->Node_path);
503 return( db_status = S_NOTFOUND );
506 node_slot_ptr = NULL;
507 if (dio_get((--np_ptr)->node, (char FAR * FAR *)&node,
508 NOPGHOLD) != S_OKAY) {
509 MEM_UNLOCK(&curkey->Node_path);
515 if (node_slot_ptr == NULL)
516 node_slot_ptr = &node->slots[np_ptr->slot*slot_len];
518 else do { /* move down to leaf node */
519 if ( dio_get(child, (char FAR * FAR *)&node, NOPGHOLD) != S_OKAY ) {
520 MEM_UNLOCK(&curkey->Node_path);
524 (++np_ptr)->node = child;
525 if (fcn == KEYNEXT) {
527 node_slot_ptr = node->slots;
530 np_ptr->slot = node->used_slots;
531 node_slot_ptr = &node->slots[np_ptr->slot*slot_len];
533 bytecpy(&child, node_slot_ptr, sizeof(F_ADDR));
534 } while ( child != NULL_NODE );
536 if (np_ptr->slot == node->used_slots) {
538 node_slot_ptr -= slot_len;
541 bytecpy(&key_type, node_slot_ptr, slot_len);
542 if (key_type.ks.keyno == prefix)
545 curkey->lstat = KEYEOF;
546 db_status = S_NOTFOUND;
548 MEM_UNLOCK(&curkey->Node_path);
553 /* Key has been found. Save appropriate information
555 static void key_found(dba)
558 MEM_LOCK(&curkey->Keyval);
559 /* save key value and database address for possible repositioning */
560 bytecpy(curkey->keyval, key_type.ks.data, key_len);
561 MEM_UNLOCK(&curkey->Keyval);
562 bytecpy(&curkey->dba, &key_type.ks.data[key_len], sizeof(DB_ADDR));
564 /* return found db addr */
567 curkey->lstat = KEYFOUND; /*[42] FOUND a match */
574 key_boundary(fcn, dba )
575 int fcn; /* KEYFRST or KEYLAST */
576 DB_ADDR *dba; /* to get dba of first or last key */
578 F_ADDR pg; /* node number */
579 NODE FAR *node; /* pointer to node contents in cache */
580 int cmp; /* keycmp result */
581 int match_lvl; /* lowest level containing matched key */
582 register int lvl; /* node_path level variable */
583 register int slot; /* slot position in node */
584 register NODE_PATH FAR *np_ptr;
585 register char FAR *node_slot_ptr;
587 if ( fcn == KEYFIND ) {
588 curkey->lstat = KEYINIT;
589 return( db_status = S_OKAY );
591 curkey->lstat = KEYNOTFOUND;
593 /* traverse B-tree for first or last key with specified prefix */
596 MEM_LOCK(&curkey->Node_path);
597 for (lvl = 0; TRUE; ++lvl) {
599 if ( dio_get(pg, (char FAR * FAR *)&node, NOPGHOLD) != S_OKAY ) {
600 MEM_UNLOCK(&curkey->Node_path);
603 if ( node->used_slots == 0 ) {
604 /* must not be anything on file */
605 curkey->lstat = KEYEOF;
606 MEM_UNLOCK(&curkey->Node_path);
607 return( db_status = S_NOTFOUND );
609 if ( fcn == KEYFRST ) {
610 for (slot = 0, node_slot_ptr = node->slots;
611 slot < node->used_slots;
612 ++slot, node_slot_ptr += slot_len) {
613 if ((cmp = INTcmp((char FAR *)&prefix,
614 (char FAR *)(node_slot_ptr + sizeof(F_ADDR)))) <= 0)
619 for (slot = node->used_slots - 1,
620 node_slot_ptr = &node->slots[slot*slot_len];
622 --slot, node_slot_ptr -= slot_len) {
623 if ((cmp = INTcmp((char FAR *)&prefix,
624 (char FAR *)(node_slot_ptr + sizeof(F_ADDR)))) >= 0)
628 /* save node path position */
629 np_ptr = &curkey->node_path[lvl];
634 /* save matched level & key value */
636 bytecpy(&key_type, node_slot_ptr, slot_len);
638 /* fetch appropriate child pointer */
640 node_slot_ptr += slot_len;
641 bytecpy(&pg, node_slot_ptr, sizeof(F_ADDR));
643 if ( pg == NULL_NODE ) break;
645 if ( match_lvl >= 0 ) {
646 curkey->level = match_lvl;
648 curkey->lstat = KEYREPOS; /*[42] Need to reposition */
651 /* no keys on file with requested prefix */
653 curkey->lstat = KEYEOF;
654 db_status = S_NOTFOUND;
656 MEM_UNLOCK(&curkey->Node_path);
661 /* Insert key field into B-tree
663 key_insert(fld, key_val, dba )
664 int fld; /* key field number */
665 CONST char FAR *key_val; /* key value */
666 DB_ADDR dba; /* record's database address */
670 /* initialize key function operation */
671 if ( key_init(fld) != S_OKAY ) return( db_status );
673 /* locate insertion point */
674 if ( key_locpos(key_val, &dba) == S_NOTFOUND ) {
675 /* expand node to include key */
676 if ( (stat = expand(key_val, dba, NULL_NODE)) == S_OKAY ) {
677 /* save key value and database address for possible repositioning */
678 MEM_LOCK(&curkey->Keyval);
679 bytecpy(curkey->keyval, key_val, key_len);
680 MEM_UNLOCK(&curkey->Keyval);
683 /* reset key position */
684 key_reset(curkey->keyfile);
688 else if ( db_status == S_OKAY )
696 /* Expand node for new key
698 static int expand(key_val, dba, brother )
699 CONST char FAR *key_val; /* key value */
700 DB_ADDR dba; /* record's database address */
701 F_ADDR brother; /* page number of brother node */
705 NODE_PATH FAR *np_ptr;
707 register char FAR *node_slot_ptr;
709 MEM_LOCK(&curkey->Node_path);
710 np_ptr = &curkey->node_path[curkey->level];
712 if (dio_get(pg = np_ptr->node, (char FAR * FAR *)&node, PGHOLD) != S_OKAY) {
713 MEM_UNLOCK(&curkey->Node_path);
716 if ( node->used_slots >= max_slots ) {
717 MEM_UNLOCK(&curkey->Node_path);
718 return( dberr(S_KEYERR) );
720 node_slot_ptr = &node->slots[slot_pos = np_ptr->slot*slot_len];
721 open_slots(node, slot_pos, 1);
723 /* copy brother into opened slot's child pointer */
724 bytecpy(node_slot_ptr + slot_len, &brother, sizeof(F_ADDR));
725 /* copy keyno into current slot */
726 bytecpy(node_slot_ptr + sizeof(F_ADDR), &prefix, sizeof(INT));
728 node_slot_ptr += key_data;
729 /* clear slot data area to zeros */
730 byteset(node_slot_ptr, 0, slot_len - key_data);
732 /* copy keyval into current slot */
733 if ( cfld_ptr->fd_type == CHARACTER && cfld_ptr->fd_dim[1] == 0 )
734 strncpy(node_slot_ptr, key_val, key_len);
736 bytecpy(node_slot_ptr, key_val, key_len);
738 /* copy database address into current slot */
739 bytecpy(node_slot_ptr + key_len, &dba, sizeof(DB_ADDR));
741 if ( node->used_slots == max_slots ) {
742 if ( pg == ROOT_ADDR )
745 split_node(pg, node);
750 MEM_UNLOCK(&curkey->Node_path);
756 /* Split node into two nodes
758 static int split_node(l_pg, l_node )
759 F_ADDR l_pg; /* left node's page number */
760 NODE FAR *l_node; /* left node buffer */
766 char FAR *l_node_slot_ptr;
768 /* extract middle key */
769 l_node_slot_ptr = &l_node->slots[mid_slot*slot_len];
770 bytecpy(&prefix, l_node_slot_ptr + sizeof(F_ADDR), sizeof(INT));
772 keyno = prefix + curr_db_table->key_offset;
774 fldno = key_info[keyno].fldno;
775 cfld_ptr = &field_table[fldno];
776 key_len = cfld_ptr->fd_len;
777 bytecpy(key_val, l_node_slot_ptr + key_data, key_len);
778 bytecpy(&dba, l_node_slot_ptr + key_data + key_len, sizeof(DB_ADDR));
780 /* divide left node */
781 l_node->used_slots = mid_slot;
783 /* allocate new right node */
784 if ((dio_pzalloc(keyfile, &r_pg) != S_OKAY) ||
785 (dio_get(r_pg, (char FAR * FAR *)&r_node, PGHOLD) != S_OKAY))
788 /* copy slots from left node at slot mid_slot+1 into right node */
789 r_node->used_slots = max_slots - (mid_slot + 1);
790 l_node_slot_ptr += slot_len;
791 bytecpy(r_node->slots, l_node_slot_ptr, NODE_WIDTH(r_node));
798 /* expand parent slot to include middle key and new right node ptr */
799 return( expand(key_val, dba, r_pg) );
805 static int split_root(node )
809 NODE FAR *l_node, FAR *r_node;
810 register int slot_pos;
811 char FAR *node_slot_ptr;
813 /* allocate two new nodes */
814 if ((dio_pzalloc(keyfile, &l_pg) != S_OKAY) ||
815 (dio_pzalloc(keyfile, &r_pg) != S_OKAY) ||
816 (dio_get(l_pg, (char FAR * FAR *)&l_node, PGHOLD) != S_OKAY) ||
817 (dio_get(r_pg, (char FAR * FAR *)&r_node, PGHOLD) != S_OKAY))
820 /* copy 0..max_slots/2-1 keys from root into left node */
821 l_node->used_slots = mid_slot;
822 slot_pos = mid_slot*slot_len;
823 bytecpy(l_node->slots, node->slots, NODE_WIDTH(l_node));
825 /* copy max_slots/2+1..max_slots from root into right node */
826 r_node->used_slots = max_slots - (mid_slot + 1);
827 node_slot_ptr = &node->slots[slot_pos += slot_len];
828 bytecpy(r_node->slots, node_slot_ptr, NODE_WIDTH(r_node));
830 /* copy mid_slot into slot[0] of root */
831 bytecpy(node->slots, node_slot_ptr - slot_len, slot_len);
833 /* copy left page number into p[0] of root */
834 bytecpy(node->slots, &l_pg, sizeof(F_ADDR));
836 /* copy right page number into p[1] of root */
837 bytecpy(&node->slots[slot_len], &r_pg, sizeof(F_ADDR));
839 /* reset number of used slots in root */
840 node->used_slots = 1;
844 dio_touch(ROOT_ADDR);
851 /* Delete key from B-tree
853 key_delete(fld, key_val, dba )
855 char CONST FAR *key_val;
860 /* initialize key function operation */
861 if ( key_init(fld) != S_OKAY ) return( db_status );
863 /* locate key to be deleted */
864 if ((stat = key_locpos(key_val, &dba)) == S_OKAY) {
865 if ( (stat = delete()) == S_OKAY ) {
866 /* save key value and database address for possible repositioning */
867 MEM_LOCK(&curkey->Keyval);
868 bytecpy(curkey->keyval, key_val, key_len);
869 MEM_UNLOCK(&curkey->Keyval);
872 /* reset key position */
873 key_reset(curkey->keyfile);
876 return( db_status = stat );
881 /* Delete key at current node_path position
885 F_ADDR pg, p_pg, l_pg, r_pg;
891 NODE_PATH FAR *np_ptr;
892 char FAR *node_slot_ptr;
893 char FAR *p_node_slot_ptr;
894 char FAR *l_node_slot_ptr;
895 char FAR *r_node_slot_ptr;
897 MEM_LOCK(&curkey->Node_path);
898 np_ptr = &curkey->node_path[curkey->level];
900 /* read node containing key to be deleted */
901 if (dio_get(pg = np_ptr->node, (char FAR * FAR *)&node, PGHOLD) != S_OKAY) {
902 MEM_UNLOCK(&curkey->Node_path);
905 /* copy pointer to right sub-tree */
906 slot_pos = np_ptr->slot*slot_len;
907 node_slot_ptr = &node->slots[slot_pos];
908 bytecpy(&r_pg, node_slot_ptr + slot_len, sizeof(F_ADDR));
910 if ( r_pg != NULL_NODE ) {
911 /* find leftmost descendent of right sub-tree */
914 if ( dio_get(r_pg, (char FAR * FAR *)&r_node, NOPGHOLD) != S_OKAY ) {
915 MEM_UNLOCK(&curkey->Node_path);
922 bytecpy(&r_pg, r_node->slots, sizeof(F_ADDR));
923 } while ( r_pg != NULL_NODE );
925 /* copy key from leaf into node */
926 node_slot_ptr += sizeof(F_ADDR);
927 r_node_slot_ptr = &r_node->slots[sizeof(F_ADDR)];
928 bytecpy(node_slot_ptr, r_node_slot_ptr, slot_len - sizeof(F_ADDR));
931 /* set up to delete key from leaf */
932 /* (this is more efficient than a recursive call) */
934 node_slot_ptr = node->slots;
935 if (dio_get(pg = np_ptr->node, (char FAR * FAR *)&node, PGHOLD) != S_OKAY) {
936 MEM_UNLOCK(&curkey->Node_path);
940 shrink: /* delete key from leaf (shrink node ) */
941 close_slots(node, slot_pos, 1);
943 /* check if necessary to adjust nodes */
944 if ((curkey->level > 0) && (node->used_slots < mid_slot)) {
945 /* read in parent node */
946 if (dio_get(p_pg = (np_ptr - 1)->node, (char FAR * FAR *)&p_node,
948 MEM_UNLOCK(&curkey->Node_path);
951 slot_pos = (np_ptr - 1)->slot*slot_len;
952 node_slot_ptr = &node->slots[slot_pos];
953 if ((np_ptr - 1)->slot == p_node->used_slots ) {
954 /* pg is right node */
958 /* parent slot position should bisect left & right nodes */
959 --(np_ptr - 1)->slot;
960 slot_pos -= slot_len;
963 p_node_slot_ptr = &p_node->slots[slot_pos];
964 bytecpy(&l_pg, p_node_slot_ptr, sizeof(F_ADDR));
965 if ( dio_get(l_pg, (char FAR * FAR *)&l_node, PGHOLD) != S_OKAY ) {
966 MEM_UNLOCK(&curkey->Node_path);
971 /* pg is left node */
975 /* read right node */
976 p_node_slot_ptr = &p_node->slots[slot_pos + slot_len];
977 bytecpy(&r_pg, p_node_slot_ptr, sizeof(F_ADDR));
978 if (dio_get(r_pg, (char FAR * FAR *)&r_node, PGHOLD) != S_OKAY) {
979 MEM_UNLOCK(&curkey->Node_path);
983 if ((l_node->used_slots + r_node->used_slots + 1) < max_slots) {
984 /* combine left and right nodes */
985 if ((curkey->level == 1) && (p_node->used_slots == 1)) {
986 /* shrink down to root */
987 /* copy right node data into root */
988 p_node_slot_ptr = &p_node->slots[slot_len];
989 bytecpy(p_node_slot_ptr, r_node->slots, NODE_WIDTH(r_node));
990 p_node->used_slots = r_node->used_slots + 1;
991 r_node->used_slots = 0;
994 /* copy left node data into root */
995 open_slots(p_node, 0, l_node->used_slots);
996 bytecpy(p_node->slots, l_node->slots, NODE_WIDTH(l_node));
997 l_node->used_slots = 0;
1002 /* free node pages */
1003 dio_pzdel(keyfile, r_pg);
1004 dio_pzdel(keyfile, l_pg);
1005 MEM_UNLOCK(&curkey->Node_path);
1006 return( db_status );
1008 /* open space for l_node->used_slots+1 slots in right node */
1009 open_slots(r_node, 0, l_node->used_slots + 1);
1011 /* move left node slots into right node */
1012 amt = NODE_WIDTH(l_node);
1013 r_node_slot_ptr = r_node->slots;
1014 bytecpy(r_node_slot_ptr, l_node->slots, amt);
1016 /* move parent slot data into right node */
1017 r_node_slot_ptr += amt;
1018 p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)];
1019 bytecpy(r_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR));
1024 /* delete left node */
1025 l_node->used_slots = 0;
1026 if ( dio_pzdel(keyfile, l_pg) != S_OKAY ) {
1027 MEM_UNLOCK(&curkey->Node_path);
1028 return( db_status );
1030 /* decrement level & make parent node current node */
1035 goto shrink; /* delete slot from parent */
1037 /* acquire needed key from sibling */
1038 if ((l_node->used_slots + 1) < r_node->used_slots) {
1039 /* get key from right node */
1041 /* move parent slot to end of left node */
1042 l_node_slot_ptr = &l_node->slots[NODE_WIDTH(l_node)];
1043 p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)];
1044 bytecpy(l_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR));
1046 ++l_node->used_slots;
1048 /* copy slot 0 child from right node to left node orphan */
1049 l_node_slot_ptr += slot_len - sizeof(F_ADDR);
1050 r_node_slot_ptr = r_node->slots;
1051 bytecpy(l_node_slot_ptr, r_node_slot_ptr, sizeof(F_ADDR));
1053 /* move slot 0 of right node to parent */
1054 r_node_slot_ptr += sizeof(F_ADDR);
1055 bytecpy(p_node_slot_ptr, r_node_slot_ptr, slot_len - sizeof(F_ADDR));
1057 /* delete slot 0 from right node */
1058 close_slots(r_node, 0, 1);
1060 else if ((r_node->used_slots + 1) < l_node->used_slots) {
1061 /* get key from left node */
1063 /* open one slot at front of right node */
1064 open_slots(r_node, 0, 1);
1066 /* move parent slot to slot 0 of right node */
1067 r_node_slot_ptr = &r_node->slots[sizeof(F_ADDR)];
1068 p_node_slot_ptr = &p_node->slots[slot_pos + sizeof(F_ADDR)];
1069 bytecpy(r_node_slot_ptr, p_node_slot_ptr, slot_len - sizeof(F_ADDR));
1071 /* move end slot of left node to parent */
1072 l_node_slot_ptr = &l_node->slots[NODE_WIDTH(l_node) - slot_len];
1073 bytecpy(p_node_slot_ptr, l_node_slot_ptr, slot_len - sizeof(F_ADDR));
1075 /* move left orphan to child of slot 0 in right node */
1076 l_node_slot_ptr += slot_len - sizeof(F_ADDR);
1077 bytecpy(r_node->slots, l_node_slot_ptr, sizeof(F_ADDR));
1079 /* delete end slot from left node */
1080 --l_node->used_slots;
1089 MEM_UNLOCK(&curkey->Node_path);
1090 return( db_status );
1095 /* Open n slots in node
1097 static void open_slots(node, slot_pos, n)
1102 register char FAR *dst, FAR *src;
1103 register int amt, w, nw;
1105 nw = NODE_WIDTH(node);
1107 src = &node->slots[nw];
1109 amt = nw - slot_pos;
1113 node->used_slots += n;
1118 /* Close n slots in node
1120 static void close_slots(node, slot_pos, n)
1125 register char FAR *dst, FAR *src;
1126 register int w, amt;
1128 node->used_slots -= n;
1131 dst = &node->slots[slot_pos];
1133 amt = NODE_WIDTH(node) - slot_pos;
1141 /* Read value of last key scanned
1143 d_keyread(key_val TASK_PARM)
1147 register int kt_lc; /* loop control */
1154 FIELD_ENTRY FAR *fld_ptr;
1155 register KEY_ENTRY FAR *key_ptr;
1157 DB_ENTER(NO_DB_ID TASK_ID LOCK_SET(RECORD_IO));
1159 if ((curkey->lstat != KEYFOUND) &&
1160 (curkey->lstat != KEYNOTFOUND) &&
1161 (curkey->lstat != KEYREPOS)) {
1162 RETURN( dberr(S_KEYSEQ) );
1164 /* clear key area */
1165 byteset(key_val, '\0', cfld_ptr->fd_len);
1167 if ( cfld_ptr->fd_type == COMKEY ) {
1168 /* copy compound key fields */
1169 for (kt_lc = size_kt - cfld_ptr->fd_ptr,
1170 key_ptr = &key_table[cfld_ptr->fd_ptr];
1171 (--kt_lc >= 0) && (key_ptr->kt_key == fldno); ++key_ptr) {
1172 fld_ptr = &field_table[key_ptr->kt_field];
1173 fptr = key_type.ks.data + key_ptr->kt_ptr;
1174 kptr = key_val + key_ptr->kt_ptr;
1175 if ( key_ptr->kt_sort == 'd' ) {
1176 switch ( fld_ptr->fd_type ) {
1179 bytecpy(&fv, fptr, sizeof(float));
1180 fv = (float)0.0 - fv;
1181 bytecpy(kptr, &fv, sizeof(float));
1184 bytecpy(&dv, fptr, sizeof(double));
1186 bytecpy(kptr, &dv, sizeof(double));
1190 key_cmpcpy(kptr, fptr, fld_ptr->fd_len);
1191 if ( fld_ptr->fd_dim[0] > 1 && fld_ptr->fd_dim[1] == 0 ) {
1192 /* make sure a null byte is at the end */
1193 kptr[fld_ptr->fd_len-1] = '\0';
1197 key_cmpcpy(kptr, fptr, fld_ptr->fd_len);
1201 bytecpy(kptr, fptr, fld_ptr->fd_len);
1205 else if ( cfld_ptr->fd_type == CHARACTER && cfld_ptr->fd_dim[1] == 0 )
1206 strncpy(key_val, key_type.ks.data, key_len);
1208 bytecpy(key_val, key_type.ks.data, key_len);
1210 RETURN( db_status = S_OKAY );
1215 /* Build compound key value from record
1217 key_bldcom(fld, rec, key, cflag )
1218 int fld; /* compound key field number */
1219 char FAR *rec; /* ptr to record data */
1220 char FAR *key; /* ptr to array to recv constructed key */
1221 int cflag; /* TRUE to compliment compound descending keys */
1223 register int kt_lc; /* loop control */
1229 FIELD_ENTRY FAR *fld_ptr, FAR *kfld_ptr;
1230 register KEY_ENTRY FAR *key_ptr;
1232 /* clear key area */
1233 fld_ptr = &field_table[fld];
1234 byteset(key, '\0', fld_ptr->fd_len);
1236 /* create compound key value */
1237 rec -= record_table[fld_ptr->fd_rec].rt_data;
1238 for (kt_lc = size_kt - fld_ptr->fd_ptr,
1239 key_ptr = &key_table[fld_ptr->fd_ptr];
1240 (--kt_lc >= 0) && (key_ptr->kt_key == fld); ++key_ptr) {
1241 kfld_ptr = &field_table[key_ptr->kt_field];
1242 fptr = rec + kfld_ptr->fd_ptr;
1244 /* Complement descending keys if permitted (cflag) */
1245 if ( cflag && ( key_ptr->kt_sort == 'd' )) {
1246 switch ( kfld_ptr->fd_type ) {
1249 bytecpy(&fv, fptr, sizeof(float));
1250 fv = (float)0.0 - fv;
1251 bytecpy(&key[key_ptr->kt_ptr], &fv, sizeof(float));
1254 bytecpy(&dv, fptr, sizeof(double));
1256 bytecpy(&key[key_ptr->kt_ptr], &dv, sizeof(double));
1260 key_cmpcpy(key+key_ptr->kt_ptr, fptr, kfld_ptr->fd_len);
1261 if ( kfld_ptr->fd_dim[0] > 1 && kfld_ptr->fd_dim[1] == 0 ) {
1262 /* make sure a null byte is at the end */
1263 *(key + key_ptr->kt_ptr + kfld_ptr->fd_len - 1) = '\0';
1267 key_cmpcpy(key+key_ptr->kt_ptr, fptr, kfld_ptr->fd_len);
1271 bytecpy(&key[key_ptr->kt_ptr], fptr, kfld_ptr->fd_len);
1274 return( db_status = S_OKAY );
1279 /* Complement and copy bytes
1281 void key_cmpcpy(s1, s2, n)
1282 register char FAR *s1;
1283 register char FAR *s2;
1290 /* vpp -nOS2 -dUNIX -nBSD -nVANILLA_BSD -nVMS -nMEMLOCK -nWINDOWS -nFAR_ALLOC -f/usr/users/master/config/nonwin keyfcns.c */