diff options
author | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-08-02 08:43:31 +0200 |
---|---|---|
committer | Jörg Frings-Fürst <debian@jff-webhosting.net> | 2014-08-02 08:43:31 +0200 |
commit | daf17154bf13139d9375f48525d19d6aaba08155 (patch) | |
tree | e3c08b6c49dc8a8e83f03327591310546675b43d /xbase64/xbndx.cpp |
Imported Upstream version 3.1.2upstream/3.1.2
Diffstat (limited to 'xbase64/xbndx.cpp')
-rwxr-xr-x | xbase64/xbndx.cpp | 2402 |
1 files changed, 2402 insertions, 0 deletions
diff --git a/xbase64/xbndx.cpp b/xbase64/xbndx.cpp new file mode 100755 index 0000000..e89bc7a --- /dev/null +++ b/xbase64/xbndx.cpp @@ -0,0 +1,2402 @@ +/* xbndx.cpp + + Xbase64 project source code + + NDX indexing routines for X-Base + + Copyright (C) 1997,2003,2004 Gary A Kunkel + + This program is free software; you can redistribute it and/or modify + it 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. + + This program is distributed in the hope that it 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Contact: + + Email: + + xdb-devel@lists.sourceforge.net + xdb-users@lists.sourceforge.net + + Regular Mail: + + XBase Support + 149C South Main St + Keller Texas, 76248 + USA + +*/ + +#ifdef __GNU LesserG__ + #pragma implementation "xbndx.h" +#endif + +#ifdef __WIN32__ +#include <xbase64/xbwincfg.h> +#else +#include <xbase64/xbconfig.h> +#endif + +#include <xbase64/xbase64.h> +#include <iostream> + +#ifdef XB_INDEX_NDX + +#ifdef HAVE_IO_H +#include <io.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +//#include <xbase64/xbexcept.h> + +/*! \file xbndx.cpp +*/ + +/***********************************************************************/ +//! Short description +/*! +*/ +/* This routine dumps the node chain to stdout */ +#ifdef XBASE_DEBUG +void xbNdx::DumpNodeChain() +{ + xbNdxNodeLink *n; + std::cout << std::endl << "*************************" << std::endl; + std::cout << "xbNodeLinkCtr = " << xbNodeLinkCtr << std::endl; + std::cout << "Reused = " << ReusedxbNodeLinks << std::endl; + + n = NodeChain; + while(n){ + std::cout << "xbNodeLink Chain ->" << n->NodeNo << std::endl; + std::cout << " CurKeyNo ->" << n->CurKeyNo << std::endl; + n = n->NextNode; + } + n = FreeNodeChain; + while(n){ + std::cout << "FreexbNodeLink Chain " << n->NodeNo << std::endl; + n = n->NextNode; + } + n = DeleteChain; + while(n){ + std::cout << "DeleteLink Chain " << n->NodeNo << std::endl; + n = n->NextNode; + } +} +#endif +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +/* This routine returns a chain of one or more index nodes back to the */ +/* free node chain */ + +void xbNdx::ReleaseNodeMemory(xbNdxNodeLink *n, xbBool doFree) +{ + xbNdxNodeLink *temp; + + if(doFree){ + while(n){ + temp = n->NextNode; + free(n); + n = temp; + } + } else { + if( !FreeNodeChain ) + FreeNodeChain = n; + else { /* put this list at the end */ + temp = FreeNodeChain; + while( temp->NextNode ) + temp = temp->NextNode; + temp->NextNode = n; + } + } +} +/***********************************************************************/ +//! Short description +/*! +*/ +/* This routine returns a node from the free chain if available, */ +/* otherwise it allocates new memory for the requested node */ + +xbNdxNodeLink * xbNdx::GetNodeMemory() +{ + xbNdxNodeLink * temp; + if( FreeNodeChain ){ + temp = FreeNodeChain; + FreeNodeChain = temp->NextNode; + ReusedxbNodeLinks++; + } else { + temp = (xbNdxNodeLink *) malloc( sizeof( xbNdxNodeLink )); + xbNodeLinkCtr++; + } + memset( temp, 0x00, sizeof( xbNdxNodeLink )); + return temp; +} +/***********************************************************************/ +//! Short description +/*! +*/ +#ifdef XBASE_DEBUG +void xbNdx::DumpHdrNode( xbShort opt ) +{ + if( opt ){ + FILE * log; + if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return; + fprintf( log, "Index Header Node for %s\n", GetFileName().getData()); + fprintf( log, "--------------------------------\n" ); + fprintf( log, "Start node = %ld\n", HeadNode.StartNode ); + fprintf( log, "Total nodes = %ld\n", HeadNode.TotalNodes ); + fprintf( log, "No of keys = %ld\n", HeadNode.NoOfKeys ); + fprintf( log, "Key Length = %d\n", HeadNode.KeyLen ); + fprintf( log, "Keys Per Node = %d\n", HeadNode.KeysPerNode ); + fprintf( log, "Key type = %d\n", HeadNode.KeyType ); + fprintf( log, "Key size = %ld\n", HeadNode.KeySize ); + fprintf( log, "Unknown 2 = %d\n", HeadNode.Unknown2 ); + fprintf( log, "Unique = %d\n", HeadNode.Unique ); + fprintf( log, "KeyExpression = %s\n", HeadNode.KeyExpression ); + fclose( log ); + } + else + { + std::cout << "Start node = " << HeadNode.StartNode << std::endl; + std::cout << "Total nodes = " << HeadNode.TotalNodes << std::endl; + std::cout << "No of keys = " << HeadNode.NoOfKeys << std::endl; + std::cout << "Key Length = " << HeadNode.KeyLen << std::endl; + std::cout << "Keys Per Node = " << HeadNode.KeysPerNode << std::endl; + std::cout << "Key type = " << HeadNode.KeyType << std::endl; + std::cout << "Key size = " << HeadNode.KeySize << std::endl; + std::cout << "Unknown 2 = " << HeadNode.Unknown2 << std::endl; + std::cout << "Unique = " << HeadNode.Unique << std::endl; + std::cout << "KeyExpression = " << HeadNode.KeyExpression << std::endl; +#ifdef XB_VAR_NODESIZE + std::cout << "NodeSize = " << NodeSize << std::endl; +#endif // XB_VAR_NODESIZE + std::cout << std::endl; + } +} +#endif + +/***********************************************************************/ +//! Constructor +/*! + \param pdbf +*/ +xbNdx::xbNdx() : xbIndex() +{ +} + +/***********************************************************************/ +//! Constructor +/*! + \param pdbf +*/ +xbNdx::xbNdx(xbDbf *pdbf) : xbIndex(pdbf) { +#ifndef XB_VAR_NODESIZE + memset( Node, 0x00, XB_NDX_NODE_SIZE ); +#else + memset( Node, 0x00, XB_MAX_NDX_NODE_SIZE ); +#endif + memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode )); + NodeChain = NULL; + FreeNodeChain = NULL; + DeleteChain = NULL; + CurNode = NULL; + xbNodeLinkCtr = 0L; + ReusedxbNodeLinks = 0L; +#ifndef XB_VAR_NODESIZE + NodeSize = XB_NDX_NODE_SIZE; +#else + NodeSize = XB_DEFAULT_NDX_NODE_SIZE; +#endif // XB_VAR_NODESIZE +} + +/***********************************************************************/ +//! Destructor +/*! +*/ +xbNdx::~xbNdx() +{ + CloseIndex(); +} + +/***********************************************************************/ +//! Short description +/*! +*/ +xbShort xbNdx::GetHeadNode( void ) +{ + char *p, *q; + xbShort i; + + if( !IsOpen() ) + return XB_NOT_OPEN; + + if( _fseek( indexfp, 0, SEEK_SET )) + return XB_SEEK_ERROR; + + if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 ) + return XB_READ_ERROR; + + /* load the head node structure */ + p = Node; + HeadNode.StartNode = dbf->xbase->GetLong ( p ); p+=4; + HeadNode.TotalNodes = dbf->xbase->GetLong ( p ); p+=4; + HeadNode.NoOfKeys = dbf->xbase->GetLong ( p ); p+=4; + HeadNode.KeyLen = dbf->xbase->GetShort( p ); p+=2; + HeadNode.KeysPerNode = dbf->xbase->GetShort( p ); p+=2; + HeadNode.KeyType = dbf->xbase->GetShort( p ); p+=2; + HeadNode.KeySize = dbf->xbase->GetLong ( p ); p+=4; + HeadNode.Unknown2 = *p++; + HeadNode.Unique = *p++; + +#ifdef XB_VAR_NODESIZE + // + // Automagically determine the node size. Note the (2 * sizeof(xbLong)) + // is taken directly from CreateIndex(). I don't understand it exactly, + // but this is the value used to calculate the number of keys per node. + // DTB. + // + NodeSize = (2 * sizeof(xbLong)) + HeadNode.KeySize * HeadNode.KeysPerNode; + if(NodeSize % XB_NDX_NODE_MULTIPLE) + NodeSize = ((NodeSize + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) * + XB_NDX_NODE_MULTIPLE; +#endif + + q = HeadNode.KeyExpression; + for( i = XB_NDX_NODE_BASESIZE; i < XB_NDX_NODE_SIZE && *p; i++ ) + *q++ = *p++; + + return 0; +} +/***********************************************************************/ +//! Short description +/*! + \param NodeNo + \param SetNodeChain +*/ +/* This routine reads a leaf node from disk */ +/* */ +/* If SetNodeChain 2, then the node is not appended to the node chain */ +/* but the CurNode pointer points to the node read */ +/* If SetNodeChain 1, then the node is appended to the node chain */ +/* If SetNodeChain 0, then record is only read to Node memory */ + +xbShort xbNdx::GetLeafNode( xbLong NodeNo, xbShort SetNodeChain ) +{ + xbNdxNodeLink *n; + + if( !IsOpen() ) + return XB_NOT_OPEN; + + if( _fseek( indexfp, (xbOffT)NodeNo * XB_NDX_NODE_SIZE, SEEK_SET )) + return XB_SEEK_ERROR; + + if(( fread( Node, XB_NDX_NODE_SIZE, 1, indexfp )) != 1 ) + return XB_READ_ERROR; + + if( !SetNodeChain ) return 0; + + if(( n = GetNodeMemory()) == NULL ) + return XB_NO_MEMORY; + + n->NodeNo = NodeNo; + n->CurKeyNo = 0L; + n->NextNode = NULL; + n->Leaf.NoOfKeysThisNode = dbf->xbase->GetLong( Node ); + memcpy( n->Leaf.KeyRecs, Node+4, XB_NDX_NODE_SIZE - 4 ); + + /* put the node in the chain */ + if( SetNodeChain == 1 ){ + if( NodeChain == NULL ){ /* first one ? */ + NodeChain = n; + CurNode = n; + CurNode->PrevNode = NULL; + } else { + n->PrevNode = CurNode; + CurNode->NextNode = n; + CurNode = n; + } + } + else + CurNode = n; + return 0; +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +#ifdef XBASE_DEBUG +void xbNdx::DumpNodeRec( xbLong n ) +{ + char *p; + xbLong NoOfKeys, LeftBranch, RecNo, NodeType; + xbShort i,j; + FILE * log; + + if(( log = fopen( "xbase64.log", "a+t" )) == NULL ) return; + GetLeafNode( n, 0 ); + NoOfKeys = dbf->xbase->GetLong( Node ); + p = Node + 4; /* go past no of keys */ + + fprintf( log, "----------------------------------------------------\n" ); + fprintf( log, "Node # %ld\n", n ); + fprintf( log, "Number of keys = %ld\n", NoOfKeys ); + fprintf( log, " Key Left Dbf Rec Key\n" ); + fprintf( log, "Number Branch Number Data\n" ); + + NodeType = 0; + for( i = 0; i < (NoOfKeys+NodeType); i++ ){ + LeftBranch = dbf->xbase->GetLong( p ); + if( i == 0 && LeftBranch ){ + NodeType = 1; /* print one extra entry for interior nodes */ + fprintf( log, "Interior node\n" ); + } + + p+=4; + RecNo = dbf->xbase->GetLong( p ); + p+=4; + + fprintf( log, " %3d %9ld %9ld ", i, LeftBranch, RecNo ); + + if( NodeType == 1 && i == NoOfKeys ) + fprintf( log, "...\n" ); + + else if( !HeadNode.KeyType ){ + for( j = 0; j < HeadNode.KeyLen; j++ ) + fputc( *p++, log ); + fputc( '\n', log ); + } + + else { + fprintf( log, "??????\n" /*, dbf->xbase->GetDouble( p )*/ ); + p += 8; + } + } + fclose( log ); +} +#endif +/***********************************************************************/ +#ifndef XB_INLINE_GETDBFNO +xbLong xbNdx::GetDbfNo( xbShort RecNo, xbNdxNodeLink * n ) +{ + xbNdxLeafNode *temp; + char *p; + if( !n ) return 0L; + temp = &n->Leaf; + if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L; + p = temp->KeyRecs + 4; + p += RecNo * ( 8 + HeadNode.KeyLen ); + return( dbf->xbase->GetLong( p )); +} +#endif +/***********************************************************************/ +//! Short description +/*! + \param RecNo + \param n +*/ +xbLong xbNdx::GetLeftNodeNo( xbShort RecNo, xbNdxNodeLink * n ) +{ + xbNdxLeafNode *temp; + char *p; + if( !n ) return 0L; + temp = &n->Leaf; + if( RecNo < 0 || RecNo > temp->NoOfKeysThisNode ) return 0L; + p = temp->KeyRecs; + p += RecNo * ( 8 + HeadNode.KeyLen ); + return( dbf->xbase->GetLong( p )); +} +/***********************************************************************/ +//! Short description +/*! + \param RecNo + \param n +*/ +char * xbNdx::GetKeyData( xbShort RecNo, xbNdxNodeLink * n ) +{ + xbNdxLeafNode *temp; + char *p; + if( !n ) return 0L; + temp = &n->Leaf; + if( RecNo < 0 || RecNo > ( temp->NoOfKeysThisNode - 1 )) return 0L; + p = temp->KeyRecs + 8; + p += RecNo * ( 8 + HeadNode.KeyLen ); + return( p ); +} +/***********************************************************************/ +//! Short description +/*! +*/ +xbLong xbNdx::GetTotalNodes( void ) +{ + if( &HeadNode ) + return HeadNode.TotalNodes; + else + return 0L; +} +/***********************************************************************/ +//! Short description +/*! +*/ +xbUShort xbNdx::GetKeysPerNode( void ) +{ + if( &HeadNode ) + return HeadNode.KeysPerNode; + else + return 0L; +} +/***********************************************************************/ +//! Short description +/*! + \param RetrieveSw +*/ +xbShort xbNdx::GetFirstKey( xbShort RetrieveSw ) +{ +/* This routine returns 0 on success and sets CurDbfRec to the record */ +/* corresponding to the first index pointer */ + + xbLong TempNodeNo; + xbShort rc; + + /* initialize the node chain */ + if( NodeChain ){ + ReleaseNodeMemory( NodeChain ); + NodeChain = NULL; + } + + if(( rc = GetHeadNode()) != 0 ){ + CurDbfRec = 0L; + return rc; + } + + /* get a node and add it to the link */ + if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ + return rc; + } + +/* traverse down the left side of the tree */ + while( GetLeftNodeNo( 0, CurNode )){ + TempNodeNo = GetLeftNodeNo( 0, CurNode ); + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + CurNode->CurKeyNo = 0; + } + CurDbfRec = GetDbfNo( 0, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param RetrieveSw +*/ +xbShort xbNdx::GetNextKey( xbShort RetrieveSw ) +{ +/* This routine returns 0 on success and sets CurDbfRec to the record */ +/* corresponding to the next index pointer */ + + xbNdxNodeLink * TempxbNodeLink; + xbLong TempNodeNo; + xbShort rc; + + if( !IsOpen() ){ + CurDbfRec = 0L; + return XB_NOT_OPEN; + } + + if( !CurNode ){ + rc = GetFirstKey( RetrieveSw ); + return rc; + } + + /* more keys on this node ? */ + if(( CurNode->Leaf.NoOfKeysThisNode-1) > CurNode->CurKeyNo ){ + CurNode->CurKeyNo++; + CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; + } + + /* if head node we are at eof */ + if( CurNode->NodeNo == HeadNode.StartNode ) { + return XB_EOF; + } + + /* this logic assumes that interior nodes have n+1 left node no's where */ + /* n is the number of keys in the node */ + + /* pop up one node to the interior node level & free the leaf node */ + + TempxbNodeLink = CurNode; + CurNode = CurNode->PrevNode; + CurNode->NextNode = NULL; + ReleaseNodeMemory( TempxbNodeLink ); + + /* while no more right keys && not head node, pop up one node */ + while(( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode ) && + ( CurNode->NodeNo != HeadNode.StartNode )){ + TempxbNodeLink = CurNode; + CurNode = CurNode->PrevNode; + CurNode->NextNode = NULL; + ReleaseNodeMemory( TempxbNodeLink ); + } + + /* if head node && right most key, return end-of-file */ + if(( HeadNode.StartNode == CurNode->NodeNo ) && + ( CurNode->CurKeyNo >= CurNode->Leaf.NoOfKeysThisNode )) { + return XB_EOF; + } + + /* move one to the right */ + CurNode->CurKeyNo++; + TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode ); + + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + return rc; + } + +/* traverse down the left side of the tree */ + while( GetLeftNodeNo( 0, CurNode )){ + TempNodeNo = GetLeftNodeNo( 0, CurNode ); + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + CurNode->CurKeyNo = 0; + } + CurDbfRec = GetDbfNo( 0, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param NodeNo + \param RetrieveSw +*/ +xbShort xbNdx::GetLastKey( xbLong NodeNo, xbShort RetrieveSw ) +{ +/* This routine returns 0 on success and sets CurDbfRec to the record */ +/* corresponding to the last index pointer */ + +/* If NodeNo = 0, start at head node, otherwise start at NodeNo */ + + xbLong TempNodeNo; + xbShort rc; + + if( NodeNo < 0 || NodeNo > HeadNode.TotalNodes ) + return XB_INVALID_NODE_NO; + + /* initialize the node chain */ + if( NodeChain ){ + ReleaseNodeMemory( NodeChain ); + NodeChain = NULL; + } + if( NodeNo == 0L ) + if(( rc = GetHeadNode()) != 0 ){ + CurDbfRec = 0L; + return rc; + } + + /* get a node and add it to the link */ + if( NodeNo == 0L ){ + if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + } else { + if(( rc = GetLeafNode( NodeNo, 1 )) != 0 ) { + CurDbfRec = 0L; + return rc; + } + } + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; + +/* traverse down the right side of the tree */ + while( GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode )){ + TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; + } + CurNode->CurKeyNo--; /* leaf node has one fewer ix recs */ + CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode-1, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param RetrieveSw +*/ +xbShort xbNdx::GetPrevKey( xbShort RetrieveSw ) +{ +/* This routine returns 0 on success and sets CurDbfRec to the record */ +/* corresponding to the previous index pointer */ + + xbNdxNodeLink * TempxbNodeLink; + xbLong TempNodeNo; + xbShort rc; + + if( !IsOpen() ){ + CurDbfRec = 0L; + return XB_NOT_OPEN; + } + + if( !CurNode ){ + CurDbfRec = 0L; + return GetFirstKey( RetrieveSw ); + } + + /* more keys on this node ? */ + if( CurNode->CurKeyNo > 0 ){ + CurNode->CurKeyNo--; + CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; + } + + /* this logic assumes that interior nodes have n+1 left node no's where */ + /* n is the number of keys in the node */ + /* pop up one node to the interior node level & free the leaf node */ + + if( !CurNode->PrevNode ) { /* michael - make sure prev node exists */ + return XB_EOF; + } + + TempxbNodeLink = CurNode; + CurNode = CurNode->PrevNode; + CurNode->NextNode = NULL; + ReleaseNodeMemory( TempxbNodeLink ); + + /* while no more left keys && not head node, pop up one node */ + while(( CurNode->CurKeyNo == 0 ) && + ( CurNode->NodeNo != HeadNode.StartNode )) { + TempxbNodeLink = CurNode; + CurNode = CurNode->PrevNode; + CurNode->NextNode = NULL; + ReleaseNodeMemory( TempxbNodeLink ); + } + + /* if head node && left most key, return beginning-of-file */ + if(( HeadNode.StartNode == CurNode->NodeNo ) && + ( CurNode->CurKeyNo == 0 )) { + return XB_BOF; + } + + /* move one to the left */ + CurNode->CurKeyNo--; + TempNodeNo = GetLeftNodeNo( CurNode->CurKeyNo, CurNode ); + + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ) { + return rc; + } + + if( GetLeftNodeNo( 0, CurNode )) /* if interior node */ + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; + else /* leaf node */ + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1; + +/* traverse down the right side of the tree */ + while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */ + TempNodeNo = GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + if( GetLeftNodeNo( 0, CurNode )) /* if interior node */ + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; + else /* leaf node */ + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode - 1; + } + CurDbfRec = GetDbfNo( CurNode->Leaf.NoOfKeysThisNode - 1, CurNode ); + if( RetrieveSw ) + return dbf->GetRecord( CurDbfRec ); + else + return XB_NO_ERROR; +} +/**************************************************************************/ +//! Short description +/*! + \param key + \param klen + \param node + \param comp +*/ +/* +** This is a pretty basic binary search with two exceptions: 1) it will +** find the first of duplicate key values and 2) will return the index +** and the value of the last comparision even if it doesn't find a +** match. +*/ +xbShort +xbNdx::BSearchNode(const char *key, xbShort klen, const xbNdxNodeLink *node, + xbShort *comp) +{ + xbShort c, p, start = 0, end = node->Leaf.NoOfKeysThisNode - 1; + + if(start > end){ + *comp = 2; + return 0; + } + + do { + p = (start + end) / 2; + c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen); + switch(c){ + + case 1 : /* greater than */ + start = p + 1; + break; + + case 2 : /* less than */ + end = p - 1; + break; + } + } while(start <= end && c); + + + if(c == 1) + while(p < node->Leaf.NoOfKeysThisNode && + (c = CompareKey(key, GetKeyData(p, (xbNdxNodeLink *)node), klen)) == 1) + p++; + + *comp = c; + + if(!c) + while(p > 0 && !CompareKey(key, GetKeyData(p - 1, (xbNdxNodeLink *)node), klen)) + p--; + + return p; +} +/***********************************************************************/ +//! Short description +/*! + \param Tkey + \param Klen +*/ +xbLong xbNdx::GetLeafFromInteriorNode( const char * Tkey, xbShort Klen ) +{ + /* This function scans an interior node for a key and returns the */ + /* correct interior leaf node no */ + + xbShort p, c; + + /* if Tkey > any keys in node, return right most key */ + p = CurNode->Leaf.NoOfKeysThisNode - 1; + if( CompareKey( Tkey, GetKeyData( p, CurNode ), Klen ) == 1 ) { + CurNode->CurKeyNo = CurNode->Leaf.NoOfKeysThisNode; + return GetLeftNodeNo( CurNode->Leaf.NoOfKeysThisNode, CurNode ); + } + + p = BSearchNode(Tkey, Klen, CurNode, &c); + CurNode->CurKeyNo = p; + return GetLeftNodeNo( p, CurNode ); +} +/***********************************************************************/ +//! Short description +/*! + \param d +*/ +xbShort xbNdx::KeyExists( xbDouble d ) +{ + char buf[9]; + memset( buf, 0x00, 9 ); + dbf->xbase->PutDouble( buf, d ); + return FindKey( buf, 8, 0 ); +} +/***********************************************************************/ +//! Short description +/*! + \param d +*/ +xbShort xbNdx::FindKey( xbDouble d ) +{ + char buf[9]; + memset( buf, 0x00, 9 ); + dbf->xbase->PutDouble( buf, d ); + return FindKey( buf, 8, 1 ); +} +/***********************************************************************/ +//! Short description +/*! + \param Key +*/ +xbShort xbNdx::FindKey( const char * Key ) +{ + return FindKey( Key, strlen( Key ), 1 ); +} +/***********************************************************************/ +//! Short description +/*! + \param Tkey + \param DbfRec +*/ +xbShort xbNdx::FindKey( const char * Tkey, xbLong DbfRec ) +{ + /* find a key with a specifc DBF record number */ + xbShort rc; + + xbLong CurDbfRecNo; + xbLong CurNdxDbfNo; + + /* if we are already on the correct key, return XB_FOUND */ + if( CurNode ) { + CurDbfRecNo = dbf->GetCurRecNo(); + CurNdxDbfNo = GetDbfNo( CurNode->CurKeyNo, CurNode ); + if( CurDbfRecNo == CurNdxDbfNo && + (strncmp(Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ), + HeadNode.KeyLen ) == 0 )) { + return XB_FOUND; + } + } + rc = FindKey( Tkey, HeadNode.KeyLen, 0 ); + + while( rc == 0 || rc == XB_FOUND ) { + if( strncmp( Tkey, GetKeyData( CurNode->CurKeyNo, CurNode ), + HeadNode.KeyLen ) == 0 ){ + if( DbfRec == GetDbfNo( CurNode->CurKeyNo, CurNode )) { + return XB_FOUND; + } + else + rc = GetNextKey( 0 ); + } else { + return XB_NOT_FOUND; + } + } + return XB_NOT_FOUND; +} +/***********************************************************************/ +//! Short description +/*! +*/ +xbShort xbNdx::FindKey( void ) +{ + /* if no paramaters given, use KeyBuf */ + return( FindKey( KeyBuf, HeadNode.KeyLen, 0 )); +} +/***********************************************************************/ +//! Short description +/*! + \param Tkey + \param Klen + \param RetrieveSw +*/ +xbShort xbNdx::FindKey( const char * Tkey, xbShort Klen, xbShort RetrieveSw ) +{ + /* This routine sets the current key to the found key */ + /* if RetrieveSw is true, the method positions the dbf record */ + xbShort rc,i; + xbLong TempNodeNo; + + if( NodeChain ) { + ReleaseNodeMemory( NodeChain ); + NodeChain = NULL; + } + + if(( rc = GetHeadNode()) != 0 ){ + CurDbfRec = 0L; + return rc; + } + + /* load first node */ + if(( rc = GetLeafNode( HeadNode.StartNode, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + + /* traverse down the tree until it hits a leaf */ + while( GetLeftNodeNo( 0, CurNode )){ /* while interior node */ + TempNodeNo = GetLeafFromInteriorNode( Tkey, Klen ); + if(( rc = GetLeafNode( TempNodeNo, 1 )) != 0 ){ + CurDbfRec = 0L; + return rc; + } + } + + i = BSearchNode(Tkey, Klen, CurNode, &rc); + switch(rc) { + case 0 : /* found! */ + CurNode->CurKeyNo = i; + CurDbfRec = GetDbfNo( i, CurNode ); + if( RetrieveSw ) + dbf->GetRecord(CurDbfRec); + return XB_FOUND; + + case 1 : /* less than */ +// if(i < CurNode->Leaf.NoOfKeysThisNode) + break; +// i++; + + case 2 : /* greater than */ + CurNode->CurKeyNo = i; + CurDbfRec = GetDbfNo( i, CurNode ); + if( RetrieveSw ) + dbf->GetRecord(CurDbfRec); + return XB_NOT_FOUND; + } + + CurNode->CurKeyNo = i; + if(i >= CurNode->Leaf.NoOfKeysThisNode){ + CurDbfRec = 0; + return XB_EOF; + } + + CurDbfRec = GetDbfNo( i, CurNode ); + if((RetrieveSw) && (CurDbfRec > 0)) + dbf->GetRecord( CurDbfRec ); + + return XB_NOT_FOUND; +} +/***********************************************************************/ +//! Short description +/*! +*/ +xbShort xbNdx::CalcKeyLen() +{ + xbShort rc; + xbExpNode * TempNode; + char FieldName[11]; + char Type; + + TempNode = IxExp->GetFirstTreeNode(); + + if( !TempNode ) + return 0; + + if( TempNode->Type == 'd' ) return -8; + if( TempNode->Type == 'D' ){ + memset( FieldName, 0x00, 11 ); + memcpy( FieldName, TempNode->NodeText, TempNode->Len ); + Type = dbf->GetFieldType( dbf->GetFieldNo( FieldName )); + if( Type == 'N' || Type == 'F' ) + return -8; + } + + if(( rc = IxExp->ProcessExpression()) != XB_NO_ERROR ) + return 0; + + TempNode = (xbExpNode *) IxExp->Pop(); + if( !TempNode ) + return 0; + rc = TempNode->DataLen; + + if( !TempNode->InTree ) + delete TempNode; + + return rc; +} +/***********************************************************************/ +//! Short description +/*! + \param IxName + \param Exp + \param Unique + \param Overlay +*/ +xbShort xbNdx::CreateIndex(const char * IxName, const char * Exp, + xbShort Unique, xbShort Overlay ) +{ + xbShort i, KeyLen, rc; + + if( IsOpen()) CloseIndex(); + if( strlen( Exp ) > 488 ) + return XB_INVALID_KEY_EXPRESSION; + + if( dbf->GetDbfStatus() == 0 ) + return XB_NOT_OPEN; + + /* Get the index file name and store it in the class */ + SetFileName(IxName); + + /* check if the file already exists */ + if (((indexfp = fopen( GetFileName(), "r" )) != NULL ) && !Overlay ) { + fclose( indexfp ); + return XB_FILE_EXISTS; + } + + if (indexfp) + fclose(indexfp); + + if(( indexfp = fopen( GetFileName(), "w+b" )) == NULL ) + return XB_OPEN_ERROR; + +#ifdef XB_LOCKING_ON + /* + ** Must turn off buffering when multiple programs may be accessing + ** index files. + */ + setbuf( indexfp, NULL ); +#endif + + /* parse the expression */ + IxExp = new xbExpn( dbf->xbase ); + if(( rc = IxExp->BuildExpressionTree( Exp, strlen( Exp ), dbf )) != XB_NO_ERROR ) + return rc; + + /* build the header record */ + memset( &HeadNode, 0x00, sizeof( xbNdxHeadNode )); + HeadNode.StartNode = 1L; + HeadNode.TotalNodes = 2L; + HeadNode.NoOfKeys = 1L; + KeyLen = CalcKeyLen(); + + if( KeyLen == 0 || KeyLen > 100 ) /* 100 byte key length limit */ + return XB_INVALID_KEY; + else if( KeyLen == -8 ){ + HeadNode.KeyType = 1; /* numeric key */ + HeadNode.KeyLen = 8; + } else { + HeadNode.KeyType = 0; /* character key */ + HeadNode.KeyLen = KeyLen; + } + +// HeadNode.KeysPerNode = (xbUShort) ( XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) / +// (HeadNode.KeyLen + 8 ); +// HeadNode.KeySize = HeadNode.KeyLen + 8; +// while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/ + +/* above code replaced with following by Paul Koufalis pkoufalis@cogicom.com */ +// while(( HeadNode.KeyLen % 4 ) != 0 ) HeadNode.KeyLen++; /* multiple of 4*/ +// HeadNode.KeySize = HeadNode.KeyLen + 8; + + +/* above two lines commented out by gary 4/14/99 and replaced w/ following + For compatibility with other Xbase tools + KeyLen is the length of the key data + KeySize = KeyLen+8, rounded up until divisible by 4 +*/ + + HeadNode.KeySize = HeadNode.KeyLen + 8; + while(( HeadNode.KeySize % 4 ) != 0 ) HeadNode.KeySize++; /* multiple of 4*/ + + HeadNode.KeysPerNode = (xbUShort) + (XB_NDX_NODE_SIZE - (2*sizeof( xbLong ))) / HeadNode.KeySize; + + HeadNode.Unique = Unique; + strncpy( HeadNode.KeyExpression, Exp, 488 ); + KeyBuf = (char *) malloc( HeadNode.KeyLen + 1 ); + KeyBuf2 = (char *) malloc( HeadNode.KeyLen + 1 ); + memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 ); + memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 ); + + if(( rc = PutHeadNode( &HeadNode, indexfp, 0 )) != 0 ){ + return rc; + } + /* write node #1 all 0x00 */ + for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){ + if ((fwrite("\x00", 1, 1, indexfp)) != 1){ + fclose( indexfp ); + return XB_WRITE_ERROR; + } + } +// IndexStatus = XB_OPEN; + return dbf->AddIndexToIxList( index, GetFileName() ); +} +/***********************************************************************/ +//! Short description +/*! + \param RecNo + \param n + \param NodeNo +*/ +xbShort xbNdx::PutLeftNodeNo( xbShort RecNo, xbNdxNodeLink *n, xbLong NodeNo ) +{ + /* This routine sets n node's leftnode number */ + xbNdxLeafNode *temp; + char *p; + if( !n ) + return XB_INVALID_NODELINK; + + temp = &n->Leaf; + if( RecNo < 0 || RecNo > HeadNode.KeysPerNode) + return XB_INVALID_KEY; + + p = temp->KeyRecs; + p+= RecNo * ( 8 + HeadNode.KeyLen ); + dbf->xbase->PutLong( p, NodeNo ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param RecNo + \param n + \param DbfNo +*/ +xbShort xbNdx::PutDbfNo( xbShort RecNo, xbNdxNodeLink *n, xbLong DbfNo ) +{ + /* This routine sets n node's dbf number */ + xbNdxLeafNode *temp; + char *p; + if( !n ) + return XB_INVALID_NODELINK; + + temp = &n->Leaf; + if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1)) + return XB_INVALID_KEY; + + p = temp->KeyRecs + 4; + p+= RecNo * ( 8 + HeadNode.KeyLen ); + dbf->xbase->PutLong( p, DbfNo ); + return XB_NO_ERROR; +} +/************************************************************************/ +//! Short description +/*! + \param l + \param n +*/ +xbShort xbNdx::PutLeafNode( xbLong l, xbNdxNodeLink *n ) +{ + if ((_fseek(indexfp, (xbOffT)l * XB_NDX_NODE_SIZE , SEEK_SET)) != 0) { + fclose( indexfp ); + return XB_SEEK_ERROR; + } + dbf->xbase->PutLong( Node, n->Leaf.NoOfKeysThisNode ); + + if(( fwrite( Node, 4, 1, indexfp )) != 1 ){ + fclose( indexfp ); + return XB_WRITE_ERROR; + } + if(( fwrite( &n->Leaf.KeyRecs, XB_NDX_NODE_SIZE-4, 1, indexfp )) != 1 ){ + fclose( indexfp ); + return XB_WRITE_ERROR; + } + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param Head + \param f + \param UpdateOnly +*/ +xbShort xbNdx::PutHeadNode( xbNdxHeadNode * Head, FILE * f, xbShort UpdateOnly ) +{ + char buf[4]; + + if(( _fseek( f, 0L, SEEK_SET )) != 0 ){ + fclose( f ); + return XB_SEEK_ERROR; + } + memset( buf, 0x00, 4 ); + dbf->xbase->PutLong( buf, Head->StartNode ); + if(( fwrite( &buf, 4, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + memset( buf, 0x00, 4 ); + dbf->xbase->PutLong( buf, Head->TotalNodes ); + if(( fwrite( &buf, 4, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + memset( buf, 0x00, 4 ); + dbf->xbase->PutLong( buf, Head->NoOfKeys ); + if(( fwrite( &buf, 4, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + if( UpdateOnly ) + return XB_NO_ERROR; + memset( buf, 0x00, 2 ); + dbf->xbase->PutLong( buf, Head->KeyLen ); + if(( fwrite( &buf, 2, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + memset( buf, 0x00, 2 ); + dbf->xbase->PutLong( buf, Head->KeysPerNode ); + if(( fwrite( &buf, 2, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + memset( buf, 0x00, 2 ); + dbf->xbase->PutLong( buf, Head->KeyType ); + if(( fwrite( &buf, 2, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + memset( buf, 0x00, 4 ); + dbf->xbase->PutLong( buf, Head->KeySize ); + if(( fwrite( &buf, 4, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + if(( fwrite( &Head->Unknown2, XB_NDX_NODE_SIZE - 22, 1, f )) != 1 ){ + fclose( f ); + return XB_WRITE_ERROR; + } + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param RecNo + \param n +*/ +xbShort xbNdx::PutKeyData( xbShort RecNo, xbNdxNodeLink *n ) +{ + /* This routine copies the KeyBuf data into xbNdxNodeLink n */ + xbNdxLeafNode *temp; + char *p; + xbShort i; + if( !n ) + return XB_INVALID_NODELINK; + + temp = &n->Leaf; + if( RecNo < 0 || RecNo > (HeadNode.KeysPerNode-1)) + return XB_INVALID_KEY; + + p = temp->KeyRecs + 8; + p+= RecNo * ( 8 + HeadNode.KeyLen ); + for( i = 0; i < HeadNode.KeyLen; i++ ) { + *p = KeyBuf[i]; + p++; + } + return XB_NO_ERROR; +} +/************************************************************************/ +//! Short description +/*! + \param n + \param pos + \param d + \param l + \param w +*/ +xbShort xbNdx::PutKeyInNode( xbNdxNodeLink * n, xbShort pos, xbLong d, + xbLong l, xbShort w ) +{ + xbShort i; + + /* check the node */ + if (!n) + return XB_INVALID_NODELINK; + + if(pos < 0 || pos > HeadNode.KeysPerNode) + return XB_INVALID_RECORD; + + if(n->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode) + return XB_NODE_FULL; + + /* if key movement, save the original key */ + if( pos < n->Leaf.NoOfKeysThisNode ) + memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1); + + /* if interior node, handle the right most left node no */ + if( GetLeftNodeNo( 0, n )) + PutLeftNodeNo( n->Leaf.NoOfKeysThisNode+1, n, + GetLeftNodeNo( n->Leaf.NoOfKeysThisNode, n )); + + for( i = n->Leaf.NoOfKeysThisNode; i > pos; i-- ){ + memcpy( KeyBuf, GetKeyData(i-1,n), HeadNode.KeyLen ); + PutKeyData( i, n ); + PutDbfNo( i, n, GetDbfNo(i-1,n)); + PutLeftNodeNo(i, n, GetLeftNodeNo(i-1,n)); + } + + /* put new key in node */ + if( pos < n->Leaf.NoOfKeysThisNode ) + memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1); + + PutKeyData( pos, n ); + PutDbfNo( pos, n, d ); + PutLeftNodeNo( pos, n, l ); + n->Leaf.NoOfKeysThisNode++; + if( w ) + return PutLeafNode( n->NodeNo, n ); + else + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param curNode Current Node + \param newNode New Empty Node + \param pos Position of new key in current node + \param d dbf record number +*/ +/* This function splits a full index leaf node into two parts + as of 2/13/04, this split logic was modified to cause an + even split in order to keep the index tree balanced + +*/ + +xbShort xbNdx::SplitLeafNode( xbNdxNodeLink *curNode, + xbNdxNodeLink *newNode, xbShort pos, xbLong d ) +{ + xbShort curNodeNewSize; + xbShort newNodeSize; + xbShort i,j,rc,startPos; + + curNodeNewSize = (curNode->Leaf.NoOfKeysThisNode + 1) / 2; + newNodeSize = curNode->Leaf.NoOfKeysThisNode + 1 - curNodeNewSize; + + /* save off the current key buffer */ + memcpy( KeyBuf2, KeyBuf, HeadNode.KeyLen + 1 ); + if( pos < curNodeNewSize ){ /* new key goes in current node */ + + /* copy second half of current node to beginning of new node */ + /* memcpy( dst, src, len ); */ + startPos = curNode->Leaf.NoOfKeysThisNode - newNodeSize; + + for( i = startPos, j = 0; i < CurNode->Leaf.NoOfKeysThisNode; i++, j++){ + memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + PutDbfNo( j, newNode, GetDbfNo( i, curNode )); + } + + /* make a hole for the new key */ + for( i = curNodeNewSize - 1; i > pos; i-- ){ + memcpy( KeyBuf, GetKeyData( i-1, curNode ), HeadNode.KeyLen ); + PutKeyData( i, curNode ); + PutDbfNo( i, curNode, GetDbfNo( i-1, curNode )); + } + + /* insert key appropriately */ + memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 ); + PutKeyData( pos, curNode ); + PutDbfNo( pos, curNode, d ); + } + else + { + pos -= curNodeNewSize; + + /* do part one of the key migration */ + if( pos ){ + +/* was originally + + startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1; + + + then was changed to +*/ + +/* + if( ((pos + curNodeNewSize) == HeadNode.KeysPerNode) && + (pos == newNodeSize) ){ // off the right end + startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize; + } + else + { + startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + 1; + } + +*/ + +/* + and this didn't work + + + startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize; + +*/ + + startPos = curNodeNewSize; + for( i = startPos, j = 0; + j < pos && i < curNode->Leaf.NoOfKeysThisNode; i++, j++){ + memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + PutDbfNo( j, newNode, GetDbfNo( i, curNode )); + } + } + + /* insert new key appropriately */ + memcpy( KeyBuf, KeyBuf2, HeadNode.KeyLen + 1 ); + PutKeyData( pos, newNode ); + PutDbfNo( pos, newNode, d ); + + /* Load the remainder of the keys on the new node past the new key */ + if( pos < (newNodeSize-1) ){ + +// startPos = curNode->Leaf.NoOfKeysThisNode - curNodeNewSize + pos + 1; + + startPos = curNodeNewSize + pos; + for( i = startPos, j = pos+1; j < newNodeSize; i++, j++){ + memcpy( KeyBuf, GetKeyData( i, curNode ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + PutDbfNo( j, newNode, GetDbfNo( i, curNode )); + } + } + } + + curNode->Leaf.NoOfKeysThisNode = curNodeNewSize; + newNode->Leaf.NoOfKeysThisNode = newNodeSize; + + /* write the new nodes to disk */ + if(( rc = PutLeafNode( curNode->NodeNo, curNode )) != 0 ) + return rc; + if(( rc = PutLeafNode( newNode->NodeNo, newNode )) != 0 ) + return rc; + + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param nodeToSplit Interior node to split + \param newNode New empty node to use + \param dscNodeNo Descendant node number +*/ +/* This routine splits an interior node */ + +xbShort xbNdx::SplitINode( xbNdxNodeLink *nodeToSplit, + xbNdxNodeLink *newNode, xbLong dscNodeNo ) +{ + xbShort i,j,rc; + xbNdxNodeLink * SaveNodeChain; + xbNdxNodeLink * SaveCurNode; + xbLong newNodeToSplitSize; + xbLong newNodeSize; + xbShort pos, startPos, offset; + + newNodeToSplitSize = (nodeToSplit->Leaf.NoOfKeysThisNode + 2 ) / 2; + newNodeSize = nodeToSplit->Leaf.NoOfKeysThisNode + 2 - newNodeToSplitSize; + pos = nodeToSplit->CurKeyNo; + + if( pos < (newNodeToSplitSize-1) ){ + /* copy second half of nodeToSplit to newNode */ + startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeSize +1; + for(i=startPos, j=0; i <= nodeToSplit->Leaf.NoOfKeysThisNode; i++, j++ ){ + if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){ + memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + } + PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); + } + + /* make a hole for the new key */ + for( i = newNodeToSplitSize; i > pos; i-- ){ + memcpy( KeyBuf, GetKeyData( i-1, nodeToSplit ), HeadNode.KeyLen ); + PutKeyData( i, nodeToSplit ); + PutLeftNodeNo( i, nodeToSplit, GetLeftNodeNo( i-1, nodeToSplit )); + } + + /* load new high key value into current position on nodeToSplit */ + if( pos < (newNodeToSplitSize - 1 )){ + SaveNodeChain = NodeChain; + NodeChain = NULL; + SaveCurNode = CurNode; + GetLastKey( GetLeftNodeNo( pos, nodeToSplit ), 0 ); + memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ); + PutKeyData( pos, nodeToSplit ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } + PutLeftNodeNo( pos+1, nodeToSplit, dscNodeNo ); + } + +/*************/ +/* part b */ +/*************/ + + else + { + pos -= newNodeToSplitSize-1; + /* do part one of the key migration */ + if( pos ){ +// startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 2; + +// 5/29/04 gak changed the following line for index probs +// startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + 1; + + if( HeadNode.KeysPerNode % 2 ) + offset = 2; + else + offset = 1; + + startPos = nodeToSplit->Leaf.NoOfKeysThisNode - newNodeToSplitSize + offset; + + for( i = startPos, j = 0; j < pos; i++, j++ ){ + if( i < nodeToSplit->Leaf.NoOfKeysThisNode && j < (pos-1)){ + memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + } + else + { + SaveNodeChain = NodeChain; + NodeChain = NULL; + SaveCurNode = CurNode; + GetLastKey( GetLeftNodeNo( i, nodeToSplit ), 0 ); + memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); + PutKeyData( j, newNode ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } + PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); + } + } + + /* insert new key appropriately */ + if( pos < (newNodeSize - 1)){ + SaveNodeChain = NodeChain; + NodeChain = NULL; + SaveCurNode = CurNode; + GetLastKey( dscNodeNo, 0 ); + memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); + PutKeyData( pos, newNode ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } + PutLeftNodeNo( pos, newNode, dscNodeNo ); + + /* load remainder of the keys */ + if( pos < (newNodeSize - 1)){ + + +// startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+2; +// 5/29/04 gak changed the following line for index probs + + startPos=nodeToSplit->Leaf.NoOfKeysThisNode-newNodeToSplitSize+pos+offset; + + for( i = startPos, j = pos+1; j < newNodeSize; i++, j++ ){ + if( i < nodeToSplit->Leaf.NoOfKeysThisNode ){ + memcpy( KeyBuf, GetKeyData( i, nodeToSplit ), HeadNode.KeyLen ); + PutKeyData( j, newNode ); + } + PutLeftNodeNo( j, newNode, GetLeftNodeNo( i, nodeToSplit )); + } + } + } + + nodeToSplit->Leaf.NoOfKeysThisNode = newNodeToSplitSize - 1; + newNode->Leaf.NoOfKeysThisNode = newNodeSize - 1; + + if((rc = PutLeafNode( nodeToSplit->NodeNo, nodeToSplit )) != 0) return rc; + if((rc = PutLeafNode( newNode->NodeNo, newNode )) != 0) return rc; + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param RecBufSw + \param KeyBufSw +*/ +xbShort xbNdx::CreateKey( xbShort RecBufSw, xbShort KeyBufSw ) +{ + /* RecBufSw 0 Use RecBuf */ + /* 1 Use RecBuf2 */ + /* KeyBufSw 0 Use KeyBuf */ + /* 1 Use KeyBuf2 */ + + xbShort rc; + xbExpNode * TempNode; + + if(( rc = IxExp->ProcessExpression( RecBufSw )) != XB_NO_ERROR ) + return rc; + TempNode = (xbExpNode *) IxExp->Pop(); + if( !TempNode ) + return XB_INVALID_KEY; + + if( KeyBufSw ){ + if( HeadNode.KeyType == 1 ) /* numeric key */ + dbf->xbase->PutDouble( KeyBuf2, TempNode->DoubResult ); + else{ /* character key */ + memset( KeyBuf2, 0x00, HeadNode.KeyLen + 1 ); + memcpy( KeyBuf2, TempNode->StringResult, XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) ); + } + } else { + if( HeadNode.KeyType == 1 ) /* numeric key */ + dbf->xbase->PutDouble( KeyBuf, TempNode->DoubResult ); + else { /* character key */ + memset( KeyBuf, 0x00, HeadNode.KeyLen + 1 ); + memcpy( KeyBuf, TempNode->StringResult.c_str(), XB_MIN(HeadNode.KeyLen + 1, TempNode->DataLen) ); + } + } +// if( !TempNode->InTree ) dbf->xbase->FreeExpNode( TempNode ); + if( !TempNode->InTree ) delete TempNode; + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param key +*/ +xbShort +xbNdx::GetCurrentKey(char *key) +{ + CreateKey(0, 0); + if(HeadNode.KeyType == 1) + memcpy(key, KeyBuf, 8); + else + memcpy(key, KeyBuf, HeadNode.KeyLen + 1); + return 0; +} +/************************************************************************/ +//! Short description +/*! + \param DbfRec +*/ +xbShort xbNdx::AddKey( xbLong DbfRec ) +{ + /* This routine assumes KeyBuf contains the contents of the index to key */ + + char *p; + xbShort i,rc; + xbNdxNodeLink * TempNode; + xbNdxNodeLink * Tparent; + xbLong TempNodeNo; /* new, unattached leaf node no */ + xbNdxNodeLink * SaveNodeChain; + xbNdxNodeLink * SaveCurNode; + + /* find node key belongs in */ + rc = FindKey( KeyBuf, HeadNode.KeyLen, 0 ); + if( rc == XB_FOUND && HeadNode.Unique ) + return XB_KEY_NOT_UNIQUE; + + if( CurNode->Leaf.NoOfKeysThisNode > 0 && rc == XB_FOUND ){ + rc = 0; + while( rc == 0 ){ + if(( p = GetKeyData( CurNode->CurKeyNo, CurNode )) == NULL ) + rc = -1; + else { + rc = CompareKey( KeyBuf, p, HeadNode.KeyLen ); + if( rc == 0 && DbfRec >= GetDbfNo( CurNode->CurKeyNo, CurNode )){ + if((rc = GetNextKey(0)) == XB_EOF) { + if((rc = GetLastKey(0, 0)) != XB_NO_ERROR) + return rc; + CurNode->CurKeyNo++; + } + } + else + rc = -1; + } + } + } + + /* update header node */ + HeadNode.NoOfKeys++; + /************************************************/ + /* section A - if room in node, add key to node */ + /************************************************/ + + if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ + if(( rc = PutKeyInNode( CurNode,CurNode->CurKeyNo,DbfRec,0L,1)) != 0) + return rc; + if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0) + return rc; + return XB_NO_ERROR; + } + + /***********************************************************************/ + /* section B - split leaf node if full and put key in correct position */ + /***********************************************************************/ + + TempNode = GetNodeMemory(); + TempNode->NodeNo = HeadNode.TotalNodes++; + rc = SplitLeafNode( CurNode, TempNode, CurNode->CurKeyNo, DbfRec ); + if( rc ) + return rc; + + TempNodeNo = TempNode->NodeNo; + ReleaseNodeMemory( TempNode ); + + /*****************************************************/ + /* section C go up tree splitting nodes as necessary */ + /*****************************************************/ + Tparent = CurNode->PrevNode; + + while( Tparent && + Tparent->Leaf.NoOfKeysThisNode >= HeadNode.KeysPerNode-1) { + + TempNode = GetNodeMemory(); + + if( !TempNode ) + return XB_NO_MEMORY; + TempNode->NodeNo = HeadNode.TotalNodes++; + + rc = SplitINode( Tparent, TempNode, TempNodeNo ); + if( rc ) return rc; + + TempNodeNo = TempNode->NodeNo; + ReleaseNodeMemory( TempNode ); + ReleaseNodeMemory( CurNode ); + CurNode = Tparent; + CurNode->NextNode = NULL; + Tparent = CurNode->PrevNode; + } + + /************************************************************/ + /* Section D if CurNode is split root, create new root */ + /************************************************************/ + + /* at this point + CurNode = The node that was just split + TempNodeNo = The new node split off from CurNode */ + + if(CurNode->NodeNo == HeadNode.StartNode ){ + TempNode = GetNodeMemory(); + if( !TempNode ) + return XB_NO_MEMORY; + + SaveNodeChain = NodeChain; + NodeChain = NULL; + SaveCurNode = CurNode; + GetLastKey( CurNode->NodeNo, 0 ); + memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode ),HeadNode.KeyLen ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + PutKeyData( 0, TempNode ); + PutLeftNodeNo( 0, TempNode, CurNode->NodeNo ); + PutLeftNodeNo( 1, TempNode, TempNodeNo ); + TempNode->NodeNo = HeadNode.TotalNodes++; + TempNode->Leaf.NoOfKeysThisNode++; + HeadNode.StartNode = TempNode->NodeNo; + rc = PutLeafNode( TempNode->NodeNo, TempNode ); + if( rc ) return rc; + rc = PutHeadNode( &HeadNode, indexfp, 1 ); + if( rc ) return rc; + ReleaseNodeMemory( TempNode ); + return XB_NO_ERROR; + } + /**********************************/ + /* Section E make room in parent */ + /**********************************/ + for( i = Tparent->Leaf.NoOfKeysThisNode; i > Tparent->CurKeyNo; i-- ){ + memcpy( KeyBuf, GetKeyData( i-1, Tparent ), HeadNode.KeyLen ); + PutKeyData( i, Tparent ); + PutLeftNodeNo( i+1, Tparent, GetLeftNodeNo( i, Tparent )); + } + + /* put key in parent */ + SaveNodeChain = NodeChain; + NodeChain = NULL; + SaveCurNode = CurNode; + GetLastKey( CurNode->NodeNo, 0 ); + memcpy( KeyBuf,GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + PutKeyData( i, Tparent ); + PutLeftNodeNo( i+1, Tparent, TempNodeNo ); + Tparent->Leaf.NoOfKeysThisNode++; + rc = PutLeafNode( Tparent->NodeNo, Tparent ); + if( rc ) return rc; + rc = PutHeadNode( &HeadNode, indexfp, 1 ); + if( rc ) return rc; + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param pos + \param n +*/ +xbShort xbNdx::RemoveKeyFromNode( xbShort pos, xbNdxNodeLink *n ) +{ + xbShort i; + /* check the node */ + if( !n ) + return XB_INVALID_NODELINK; + + if( pos < 0 || pos > HeadNode.KeysPerNode ) + return XB_INVALID_KEY; + + for( i = pos; i < n->Leaf.NoOfKeysThisNode-1; i++ ){ + memcpy( KeyBuf, GetKeyData( i+1, n), HeadNode.KeyLen ); + PutKeyData( i, n ); + PutDbfNo( i, n, GetDbfNo( i+1, n )); + PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n )); + } + PutLeftNodeNo( i, n, GetLeftNodeNo( i+1, n )); + n->Leaf.NoOfKeysThisNode--; + /* if last key was deleted, decrement CurKeyNo */ + if( n->CurKeyNo > n->Leaf.NoOfKeysThisNode ) + n->CurKeyNo--; + return PutLeafNode( n->NodeNo, n ); +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +xbShort xbNdx::UpdateParentKey( xbNdxNodeLink * n ) +{ +/* this routine goes backwards thru the node chain looking for a parent + node to update */ + + xbNdxNodeLink * TempNode; + if( !n ) + return XB_INVALID_NODELINK; + + if( !GetDbfNo( 0, n )) + return XB_NOT_LEAFNODE; + + TempNode = n->PrevNode; + while( TempNode ){ + if( TempNode->CurKeyNo < TempNode->Leaf.NoOfKeysThisNode ){ + memcpy(KeyBuf,GetKeyData(n->Leaf.NoOfKeysThisNode-1,n),HeadNode.KeyLen); + PutKeyData( TempNode->CurKeyNo, TempNode ); + return PutLeafNode( TempNode->NodeNo, TempNode ); + } + TempNode = TempNode->PrevNode; + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +/* This routine queues up a list of nodes which have been emptied */ +void xbNdx::UpdateDeleteList( xbNdxNodeLink *n ) +{ + n->NextNode = DeleteChain; + DeleteChain = n; +} +/***********************************************************************/ +//! Short description +/*! +*/ +/* Delete nodes from the node list - for now we leave the empty nodes */ +/* dangling in the file. Eventually we will remove nodes from the file */ + +void xbNdx::ProcessDeleteList( void ) +{ + if( DeleteChain ){ + ReleaseNodeMemory( DeleteChain ); + DeleteChain = NULL; + } +} +/***********************************************************************/ +//! Short description +/*! +*/ +xbShort xbNdx::KeyWasChanged( void ) +{ + CreateKey( 0, 0 ); /* use KeyBuf, RecBuf */ + CreateKey( 1, 1 ); /* use KeyBuf2, RecBuf2 */ + if( CompareKey( KeyBuf, KeyBuf2, HeadNode.KeyLen ) != 0 ) + return 1; + else + return 0; +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +xbNdxNodeLink * xbNdx::LeftSiblingHasSpace( xbNdxNodeLink * n ) +{ + xbNdxNodeLink * TempNode; + xbNdxNodeLink * SaveCurNode; + + /* returns a Nodelink to xbNdxNodeLink n's left sibling if it has space */ + /* if left most node in parent return NULL */ + if( n->PrevNode->CurKeyNo == 0 ) + return NULL; + + SaveCurNode = CurNode; + GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 ); + if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ + TempNode = CurNode; + CurNode = SaveCurNode; + TempNode->PrevNode = n->PrevNode; + return TempNode; + } else { /* node is already full */ + ReleaseNodeMemory( CurNode ); + CurNode = SaveCurNode; + return NULL; + } +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +xbNdxNodeLink * xbNdx::RightSiblingHasSpace( xbNdxNodeLink * n ) +{ + /* returns a Nodelink to xbNdxNodeLink n's right sibling if it has space */ + + xbNdxNodeLink * TempNode; + xbNdxNodeLink * SaveCurNode; + + /* if left most node in parent return NULL */ + if( n->PrevNode->CurKeyNo >= n->PrevNode->Leaf.NoOfKeysThisNode ) + return NULL; + SaveCurNode = CurNode; + /* point curnode to right sib*/ + GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 ); + if( CurNode->Leaf.NoOfKeysThisNode < HeadNode.KeysPerNode ){ + TempNode = CurNode; + CurNode = SaveCurNode; + TempNode->PrevNode = n->PrevNode; + return TempNode; + } else { /* node is already full */ + ReleaseNodeMemory( CurNode ); + CurNode = SaveCurNode; + return NULL; + } +} +/*************************************************************************/ +//! Short description +/*! + \param n + \param Right +*/ +xbShort xbNdx::MoveToRightNode( xbNdxNodeLink * n, xbNdxNodeLink * Right ) +{ + xbShort j; + xbNdxNodeLink * TempNode; + xbNdxNodeLink * SaveCurNode; + xbNdxNodeLink * SaveNodeChain; + + if( n->CurKeyNo == 0 ){ + j = 1; + SaveNodeChain = NodeChain; + SaveCurNode = CurNode; + NodeChain = NULL; + GetLastKey( n->NodeNo, 0 ); + memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode),HeadNode.KeyLen); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } else { + j = 0; + memcpy( KeyBuf, GetKeyData( j, n ), HeadNode.KeyLen); + } + PutKeyInNode( Right, 0, 0L, GetLeftNodeNo( j, n ), 1 ); + ReleaseNodeMemory( Right ); + TempNode = n; + CurNode = n->PrevNode; + n = n->PrevNode; + n->NextNode = NULL; + UpdateDeleteList( TempNode ); + DeleteSibling( n ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param n + \param Left +*/ +xbShort xbNdx::MoveToLeftNode( xbNdxNodeLink * n, xbNdxNodeLink * Left ) +{ + xbShort j, rc; + xbNdxNodeLink * SaveNodeChain; + xbNdxNodeLink * TempNode; + + if( n->CurKeyNo == 0 ) + j = 1; + else + j = 0; + + /* save the original node chain */ + SaveNodeChain = NodeChain; + NodeChain = NULL; + + /* determine new right most key for left node */ + GetLastKey( Left->NodeNo, 0 ); + memcpy( KeyBuf, GetKeyData( CurNode->CurKeyNo, CurNode ), HeadNode.KeyLen); + ReleaseNodeMemory( NodeChain ); + NodeChain = NULL; /* for next GetLastKey */ + PutKeyData( Left->Leaf.NoOfKeysThisNode, Left); + PutLeftNodeNo( Left->Leaf.NoOfKeysThisNode+1, Left, GetLeftNodeNo( j,n )); + Left->Leaf.NoOfKeysThisNode++; + Left->CurKeyNo = Left->Leaf.NoOfKeysThisNode; + if(( rc = PutLeafNode( Left->NodeNo, Left )) != 0 ) + return rc; + n->PrevNode->NextNode = NULL; + UpdateDeleteList( n ); + + /* get the new right most key for left to update parents */ + GetLastKey( Left->NodeNo, 0 ); + + /* assemble the chain */ + TempNode = Left->PrevNode; + TempNode->CurKeyNo--; + NodeChain->PrevNode = Left->PrevNode; + UpdateParentKey( CurNode ); + ReleaseNodeMemory( NodeChain ); + ReleaseNodeMemory( Left ); + CurNode = TempNode; + NodeChain = SaveNodeChain; + TempNode->CurKeyNo++; + DeleteSibling( TempNode ); + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param n +*/ +xbShort xbNdx::DeleteSibling( xbNdxNodeLink * n ) +{ + xbNdxNodeLink * Left; + xbNdxNodeLink * Right; + xbNdxNodeLink * SaveCurNode; + xbNdxNodeLink * SaveNodeChain; + xbNdxNodeLink * TempNode; + xbShort rc; + + /* this routine deletes sibling CurRecNo out of xbNodeLink n */ + if( n->Leaf.NoOfKeysThisNode > 1 ){ + RemoveKeyFromNode( n->CurKeyNo, n ); + if( n->CurKeyNo == n->Leaf.NoOfKeysThisNode ){ + SaveNodeChain = NodeChain; + SaveCurNode = CurNode; + NodeChain = NULL; + GetLastKey( n->NodeNo, 0 ); + /* assemble the node chain */ + TempNode = NodeChain->NextNode; + NodeChain->NextNode = NULL; + ReleaseNodeMemory( NodeChain ); + TempNode->PrevNode = n; + UpdateParentKey( CurNode ); + /* take it back apart */ + ReleaseNodeMemory( TempNode ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } + } else if( n->NodeNo == HeadNode.StartNode ) { + /* get here if root node and only one child remains */ + /* make remaining node the new root */ + if( n->CurKeyNo == 0 ) + HeadNode.StartNode = GetLeftNodeNo( 1, n ); + else + HeadNode.StartNode = GetLeftNodeNo( 0, n ); + UpdateDeleteList( n ); + NodeChain = NULL; + CurNode = NULL; + } + else if (( Left = LeftSiblingHasSpace( n )) != NULL ) + return MoveToLeftNode( n, Left ); + else if (( Right = RightSiblingHasSpace( n )) != NULL ) + return MoveToRightNode( n, Right ); + /* else if left sibling exists */ + else if( n->PrevNode->CurKeyNo > 0 ) { + /* move right branch from left sibling to this node */ + SaveCurNode = CurNode; + SaveNodeChain = NodeChain; + NodeChain = NULL; + GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo-1, n->PrevNode ), 2 ); + Left = CurNode; + Left->PrevNode = SaveCurNode->PrevNode; + GetLastKey( Left->NodeNo, 0 ); + strncpy( KeyBuf, GetKeyData( CurNode->CurKeyNo,CurNode),HeadNode.KeyLen ); + if( n->CurKeyNo == 1 ) + PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, n )); + PutKeyData( 0, n ); + PutLeftNodeNo( 0, n, GetLeftNodeNo( Left->Leaf.NoOfKeysThisNode, Left )); + if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc; + SaveCurNode = n->PrevNode; + SaveCurNode->NextNode = NULL; + ReleaseNodeMemory( n ); + Left->Leaf.NoOfKeysThisNode--; + if(( rc = PutLeafNode( Left->NodeNo, Left )) != XB_NO_ERROR ) return rc; + /* rebuild left side of tree */ + GetLastKey( Left->NodeNo, 0 ); + NodeChain->PrevNode = SaveCurNode; + SaveCurNode->CurKeyNo--; + UpdateParentKey( CurNode ); + ReleaseNodeMemory( NodeChain ); + ReleaseNodeMemory( Left ); + CurNode = SaveCurNode; + NodeChain = SaveNodeChain; + } + /* right sibling must exist */ + else if( n->PrevNode->CurKeyNo <= n->PrevNode->Leaf.NoOfKeysThisNode ){ + /* move left branch from left sibling to this node */ + SaveCurNode = CurNode; + SaveNodeChain = NodeChain; + NodeChain = NULL; + + /* move the left node number one to the left if necessary */ + if( n->CurKeyNo == 0 ){ + PutLeftNodeNo( 0, n, GetLeftNodeNo( 1, n )); + GetLastKey( GetLeftNodeNo( 0, n ), 0 ); + memcpy(KeyBuf,GetKeyData(CurNode->CurKeyNo,CurNode),HeadNode.KeyLen); + PutKeyData( 0, n ); + ReleaseNodeMemory( NodeChain ); + NodeChain = NULL; + } + GetLeafNode( GetLeftNodeNo( n->PrevNode->CurKeyNo+1, n->PrevNode ), 2 ); + /* put leftmost node number from right node in this node */ + PutLeftNodeNo( 1, n, GetLeftNodeNo( 0, CurNode )); + if(( rc = PutLeafNode( n->NodeNo, n )) != XB_NO_ERROR ) return rc; + + /* remove the key from the right node */ + RemoveKeyFromNode( 0, CurNode ); + if(( rc = PutLeafNode( CurNode->NodeNo, CurNode )) != XB_NO_ERROR ) + return rc; + ReleaseNodeMemory( CurNode ); + + /* update new parent key value */ + GetLastKey( n->NodeNo, 0 ); + NodeChain->PrevNode = n->PrevNode; + UpdateParentKey( CurNode ); + ReleaseNodeMemory( NodeChain ); + NodeChain = SaveNodeChain; + CurNode = SaveCurNode; + } else { + /* this should never be true-but could be if 100 byte limit is ignored*/ + std::cout << "Fatal index error" << std::endl; + exit(0); + } + return XB_NO_ERROR; +} +/***********************************************************************/ +//! Short description +/*! + \param DbfRec +*/ +xbShort xbNdx::DeleteKey( xbLong DbfRec ) +{ +/* this routine assumes the key to be deleted is in KeyBuf */ + + xbNdxNodeLink * TempNode; + xbShort rc; + +#if 0 + // Not sure why this check is here, but it prevents numeric keys + // from being deleted (and thus index updates will also fail). + // I have removed it for now. Derry Bryson + if( HeadNode.KeyType != 0x00 ) + xb_error(XB_INVALID_KEY_TYPE); +#endif + + if(( rc = FindKey( KeyBuf, DbfRec )) != XB_FOUND ) + return rc; + + /* found the record to delete at this point */ + HeadNode.NoOfKeys--; + + /* delete the key from the node */ + if(( rc = RemoveKeyFromNode( CurNode->CurKeyNo, CurNode )) != 0 ) + return rc; + + /* if root node, we are done */ + if( !( CurNode->NodeNo == HeadNode.StartNode )){ + /* if leaf node now empty */ + if( CurNode->Leaf.NoOfKeysThisNode == 0 ){ + TempNode = CurNode->PrevNode; + TempNode->NextNode = NULL; + UpdateDeleteList( CurNode ); + CurNode = TempNode; + DeleteSibling( CurNode ); + ProcessDeleteList(); + } + + /* if last key of leaf updated, update key in parent node */ + /* this logic updates the correct parent key */ + + else if( CurNode->CurKeyNo == CurNode->Leaf.NoOfKeysThisNode ) + UpdateParentKey( CurNode ); + } + + if(CurNode) + CurDbfRec = GetDbfNo( CurNode->CurKeyNo, CurNode ); + else + CurDbfRec = 0; + + if(( rc = PutHeadNode( &HeadNode, indexfp, 1 )) != 0 ) + return rc; + return XB_NO_ERROR; +} +/************************************************************************/ +//! Short description +/*! + \param option +*/ +#ifdef XBASE_DEBUG +xbShort xbNdx::CheckIndexIntegrity( const xbShort option ) +{ + /* if option = 1, print out some stats */ + xbShort rc; + xbLong ctr = 1L; + while( ctr <= dbf->NoOfRecords()){ + if( option ) std::cout << "Checking Record " << ctr << std::endl; + if(( rc = dbf->GetRecord(ctr++)) != XB_NO_ERROR ) + return rc; + if(!dbf->RecordDeleted()){ + CreateKey( 0, 0 ); + rc = FindKey( KeyBuf, dbf->GetCurRecNo()); + if( rc != XB_FOUND ){ + if( option ){ + std::cout << "Record number " << dbf->GetCurRecNo() + << " Not Found" << std::endl; + std::cout << "Key = " << KeyBuf << std::endl; + } + return rc; + } + } + } + if( option ) + std::cout << std::endl << "Total records checked = " + << ctr - 1 << std::endl; + return XB_NO_ERROR; +} +#endif +/***********************************************************************/ +//! Short description +/*! + \param statusFunc +*/ +xbShort xbNdx::ReIndex(void (*statusFunc)(xbLong itemNum, xbLong numItems)) +{ + /* this method assumes the index has been locked in exclusive mode */ + xbLong l; + xbShort rc, i, saveAutoLock; + xbNdxHeadNode TempHead; + FILE *t; + xbString TempName; + memcpy( &TempHead, &HeadNode, sizeof( struct xbNdxHeadNode )); + TempHead.NoOfKeys = 1L; + TempHead.TotalNodes = 2L; + TempHead.StartNode = 1L; + rc = dbf->xbase->DirectoryExistsInName( GetFileName() ); + if( rc ){ + TempName.assign(GetFileName(), 0, rc); + TempName += "TEMPFILE.NDX"; + } else + TempName = "TEMPFILE.NDX"; + + if(( t = fopen( TempName, "w+b" )) == NULL ) + return XB_OPEN_ERROR; + + if(( rc = PutHeadNode( &TempHead, t, 0 )) != 0 ){ + fclose( t ); + remove(TempName); + return rc; + } + + for( i = 0; i < XB_NDX_NODE_SIZE; i++ ){ + if(( fwrite( "\x00", 1, 1, t )) != 1 ){ + fclose( t ); + remove(TempName); + return XB_WRITE_ERROR; + } + } + if( fclose( indexfp ) != 0 ) + return XB_CLOSE_ERROR; + if( fclose( t ) != 0 ) + return XB_CLOSE_ERROR; + if( remove( GetFileName() ) != 0 ) + return XB_CLOSE_ERROR; + if( rename(TempName, GetFileName() ) != 0 ) + return XB_WRITE_ERROR; + if(( indexfp = fopen( GetFileName(), "r+b" )) == NULL ) + return XB_OPEN_ERROR; + + saveAutoLock = dbf->GetAutoLock(); + dbf->AutoLockOff(); + for( l = 1; l <= dbf->PhysicalNoOfRecords(); l++ ){ + if(statusFunc && (l == 1 || !(l % 100) || l == dbf->PhysicalNoOfRecords())) + statusFunc(l, dbf->PhysicalNoOfRecords()); + if(( rc = dbf->GetRecord(l)) != XB_NO_ERROR ){ + if(saveAutoLock) + dbf->AutoLockOn(); + return rc; + } + + if(!dbf->GetRealDelete() || !dbf->RecordDeleted()){ + /* Create the key */ + CreateKey( 0, 0 ); + /* add key to index */ + if(( rc = AddKey( l )) != XB_NO_ERROR ){ + if(saveAutoLock) + dbf->AutoLockOn(); + return rc; + } + } + } + return rc; +} + +/***********************************************************************/ +//! Short description +/*! + \param size +*/ +void xbNdx::SetNodeSize(xbShort size) +{ +#ifdef XB_VAR_NODESIZE + if(size >= XB_DEFAULT_NDX_NODE_SIZE) + { + if(size % XB_NDX_NODE_MULTIPLE) + NodeSize = ((size + XB_NDX_NODE_MULTIPLE) / XB_NDX_NODE_MULTIPLE) * + XB_NDX_NODE_MULTIPLE; + else + NodeSize = size; + } + else + NodeSize = XB_DEFAULT_NDX_NODE_SIZE; +#endif +} + +/***********************************************************************/ +//! Short description +/*! + \param buf + \param len +*/ +void xbNdx::GetExpression(char *buf, int len) +{ + memcpy(buf, HeadNode.KeyExpression, + len < XB_NDX_NODE_SIZE ? len : XB_NDX_NODE_SIZE - XB_NDX_NODE_BASESIZE); +} + +const char* xbNdx::GetExtWithDot(bool lower) +{ + return lower? ".ndx": ".NDX"; +} + +xbUShort xbNdx::GetKeyLen() +{ + return HeadNode.KeyLen; +} + +const char* xbNdx::GetKeyExpression() +{ + return HeadNode.KeyExpression; +} + +void xbNdx::FreeNodesMemory() +{ + ReleaseNodeMemory(NodeChain, true); + NodeChain = 0; +// ReleaseNodeMemory(CloneChain, true); +// CloneChain = 0; + ReleaseNodeMemory(FreeNodeChain, true); + FreeNodeChain = 0; + ReleaseNodeMemory(DeleteChain, true); + DeleteChain = 0; +} + +#endif /* XB_INDEX_NDX */ |