cdesktopenv/cde/lib/DtSearch/raima/keyfcns.c

1281 lines
34 KiB
C

/*
* 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 <stdio.h>
#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 */