From 7cf42f9fd557c044a9bc0fa49be16a352e9183ee Mon Sep 17 00:00:00 2001 From: Miguel Angel Marchuet Frutos Date: Fri, 4 May 2007 08:18:46 +0000 Subject: [PATCH] 2007-05-04 10:15 UTC+0100 Miguel Angel Marchuet (miguelangel/at/marchuet.net) + contrib/bmdbfcdx/Makefile + contrib/bmdbfcdx/bmdbfcdx1.c + contrib/bmdbfcdx/bmsixcdx1.c + contrib/bmdbfcdx/hbrddbmcdx.h + contrib/bmdbfcdx/make_b32.bat + contrib/bmdbfcdx/makefile.bc + contrib/bmdbfcdx/readme.txt + BMDBFCDX RDD: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Is a DBFCDX RDD compatible with clipper 5.3, use SET OPTIMIZE ON to make a static bitmap filters, with SET OPTIMIZE OFF works as harbour DBFCDX. Addons: BM_DbSeekWild( uKey, [lSoftSeek], [lFindLast], [lNext], [lAll] ) => .T./.F. or aSeekRec when lAll clause BM_Turbo( lOnOff ) // Is only recomendable to use it on creating FILTERS BM_DbGetFilterArray() => aFilterRec BM_DbSetFilterArray( aFilterRec ) BM_DbSetFilterArrayAdd( aFilterRec ) BM_DbSetFilterArrayDel( aFilterRec ) Respecting command: SET OPTIMIZE Change the setting that determines whether to optimize using the open orders when processing a filtered database file ------------------------------------------------------------------------------ Syntax SET OPTIMIZE ON | OFF | () Arguments ON enables optimization. OFF disables optimization. is a logical expression that must be enclosed in parentheses. A value of true (.T.) is the same as ON, and a value of false (.F.) is the same as OFF. Note: The initial default of this setting depends on the RDD. Description For RDDs that support optimization, such as BMDBFCDX, SET OPTIMIZE determines whether to optimize filters based on the orders open in the current work area. If this flag is ON, the RDD will optimize the search for records that meet the filter condition to the fullest extent possible, minimizing the need to read the actual data from the database file. If this flag is OFF, the RDD will not optimize. Examples ¦ The following example enables optimization for the Inventor database file using the SET OPTIMIZE command: USE Inventor NEW VIA "BMDBFCDX" SET OPTIMIZE ON --- harbour/ChangeLog | 64 + harbour/contrib/Makefile | 1 + harbour/contrib/bmdbfcdx/Makefile | 13 + harbour/contrib/bmdbfcdx/bmdbfcdx1.c | 10229 ++++++++++++++++++++++++ harbour/contrib/bmdbfcdx/bmsixcdx1.c | 55 + harbour/contrib/bmdbfcdx/hbrddbmcdx.h | 669 ++ harbour/contrib/bmdbfcdx/make_b32.bat | 35 + harbour/contrib/bmdbfcdx/makefile.bc | 26 + harbour/contrib/bmdbfcdx/readme.txt | 56 + 9 files changed, 11148 insertions(+) create mode 100644 harbour/contrib/bmdbfcdx/Makefile create mode 100644 harbour/contrib/bmdbfcdx/bmdbfcdx1.c create mode 100644 harbour/contrib/bmdbfcdx/bmsixcdx1.c create mode 100644 harbour/contrib/bmdbfcdx/hbrddbmcdx.h create mode 100644 harbour/contrib/bmdbfcdx/make_b32.bat create mode 100644 harbour/contrib/bmdbfcdx/makefile.bc create mode 100644 harbour/contrib/bmdbfcdx/readme.txt diff --git a/harbour/ChangeLog b/harbour/ChangeLog index a56d68707b..9680be962e 100644 --- a/harbour/ChangeLog +++ b/harbour/ChangeLog @@ -8,6 +8,70 @@ 2002-12-01 13:30 UTC+0100 Foo Bar */ +2007-05-04 10:15 UTC+0100 Miguel Angel Marchuet (miguelangel/at/marchuet.net) + + contrib/bmdbfcdx/Makefile + + contrib/bmdbfcdx/bmdbfcdx1.c + + contrib/bmdbfcdx/bmsixcdx1.c + + contrib/bmdbfcdx/hbrddbmcdx.h + + contrib/bmdbfcdx/make_b32.bat + + contrib/bmdbfcdx/makefile.bc + + contrib/bmdbfcdx/readme.txt + + BMDBFCDX RDD: + + Is a DBFCDX RDD compatible with clipper 5.3, use SET OPTIMIZE ON to make a + static bitmap filters, with SET OPTIMIZE OFF works as harbour DBFCDX. + + Addons: + + BM_DbSeekWild( uKey, [lSoftSeek], [lFindLast], [lNext], [lAll] ) => .T./.F. or aSeekRec when lAll clause + BM_Turbo( lOnOff ) // Is only recomendable to use it on creating FILTERS + BM_DbGetFilterArray() => aFilterRec + BM_DbSetFilterArray( aFilterRec ) + BM_DbSetFilterArrayAdd( aFilterRec ) + BM_DbSetFilterArrayDel( aFilterRec ) + + Respecting command: + + SET OPTIMIZE + Change the setting that determines whether to optimize using the open orders + when processing a filtered database file + ------------------------------------------------------------------------------ + Syntax + + SET OPTIMIZE ON | OFF | () + + Arguments + + ON enables optimization. + + OFF disables optimization. + + is a logical expression that must be enclosed in + parentheses. A value of true (.T.) is the same as ON, and a value of + false (.F.) is the same as OFF. + + Note: The initial default of this setting depends on the RDD. + + Description + + For RDDs that support optimization, such as BMDBFCDX, SET OPTIMIZE + determines whether to optimize filters based on the orders open in the + current work area. If this flag is ON, the RDD will optimize the search + for records that meet the filter condition to the fullest extent + possible, minimizing the need to read the actual data from the database + file. + + If this flag is OFF, the RDD will not optimize. + + Examples + + ¦ The following example enables optimization for the Inventor + database file using the SET OPTIMIZE command: + + USE Inventor NEW VIA "BMDBFCDX" + SET OPTIMIZE ON + + 2007-05-03 16:22 UTC+0200 Przemyslaw Czerpak (druzus/at/priv.onet.pl) * harbour/source/rdd/usrrdd/usrrdd.c * updated casting for recent modifications in RDD API diff --git a/harbour/contrib/Makefile b/harbour/contrib/Makefile index 71f5bdf6c6..ccf9e03751 100644 --- a/harbour/contrib/Makefile +++ b/harbour/contrib/Makefile @@ -12,6 +12,7 @@ DIRS=\ libmisc \ libnf \ samples \ + bmdbfcdx \ ifeq ($(HB_ARCHITECTURE),w32) diff --git a/harbour/contrib/bmdbfcdx/Makefile b/harbour/contrib/bmdbfcdx/Makefile new file mode 100644 index 0000000000..e4a6aefb15 --- /dev/null +++ b/harbour/contrib/bmdbfcdx/Makefile @@ -0,0 +1,13 @@ +# +# $Id$ +# + +ROOT = ../../../ + +C_SOURCES=\ + bmdbfcdx1.c \ + bmsixcdx1.c + +LIBNAME=bmdbfcdx + +include $(TOP)$(ROOT)config/lib.cf diff --git a/harbour/contrib/bmdbfcdx/bmdbfcdx1.c b/harbour/contrib/bmdbfcdx/bmdbfcdx1.c new file mode 100644 index 0000000000..eb4a15546f --- /dev/null +++ b/harbour/contrib/bmdbfcdx/bmdbfcdx1.c @@ -0,0 +1,10229 @@ +/* + * $Id$ + */ + +/* + * xHarbour Project source code: + * BMDBFCDX RDD (ver.2) + * + * Copyright 1999-2002 Bruno Cantero + * Copyright 2000-2003 Horacio Roldan (portions) + * Copyright 2003 Przemyslaw Czerpak - all code except + * hb_cdxTagDoIndex and related hb_cdxSort* rewritten. + * Copyright 2004 Przemyslaw Czerpak - rest of code rewritten + * Copyright 2006 Miguel Angel Marchuet + * BM_DbSeekWild( uKey, [lSoftSeek], [lFindLast], [lNext], [lAll] ) => .T./.F. or aSeekRec when lAll clause + * BM_Turbo( lOnOff ) + * BM_DbGetFilterArray() => aFilterRec + * BM_DbSetFilterArray( aFilterRec ) + * BM_DbSetFilterArrayAdd( aFilterRec ) + * BM_DbSetFilterArrayDel( aFilterRec ) + * hb_cdxAppend + * hb_cdxCheckRecordFilter + * hb_cdxClearFilter + * hb_cdxDeleteRec + * hb_cdxPutRec + * hb_cdxRecall + * hb_cdxSetFilter + * hb_cdxSkipFilter + * IMPLEMENTATION of Bitmap filters + * www - http://www.xharbour.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#define HB_CDX_CLIP_AUTOPEN +#define HB_CDX_NEW_SORT + +#if !defined( HB_SIXCDX ) +# define HB_CDX_PACKTRAIL +#endif + +#define HB_CDX_DBGCODE +/* +#define HB_CDX_DBGCODE_EXT +#define HB_CDX_DSPDBG_INFO +#define HB_CDP_SUPPORT_OFF +#define HB_CDX_DBGTIME +#define HB_CDX_DBGUPDT +*/ + +#include "hbapi.h" +#include "hbapiitm.h" +#include "hbinit.h" +#include "hbapierr.h" +#include "hbapilng.h" +#include "hbvm.h" +#include "hbset.h" +#include "hbrddbmcdx.h" +#include "hbmath.h" +#include "rddsys.ch" +#include "hbregex.h" + +#ifndef HB_CDP_SUPPORT_OFF + /* for nation sorting support */ + #include "hbapicdp.h" + #define hb_cdpcharcmp( c1, c2, cdpage ) \ + ( ( cdpage && cdpage->lSort ) ? \ + hb_cdpchrcmp( c1, c2, cdpage ) : \ + ( (BYTE)(c1) - (BYTE)(c2) ) ) +/* + #define hb_cdpcharcmp( c1, c2, cdpage ) ( (BYTE)(c1) - (BYTE)(c2) ) + */ +#endif + +/* + * Tag->fRePos = TURE means that rootPage->...->childLeafPage path is + * bad and has to be reloaded + * CurKey->rec == 0 means that there is no correct CurKey + */ + +/* create a new Tag (make index) */ +static void hb_cdxTagDoIndex( LPCDXTAG pTag, BOOL fReindex ); + +/* Close Tag */ +static void hb_cdxTagClose( LPCDXTAG pTag ); + +/* free Tag pages from cache */ +static void hb_cdxTagPoolFree( LPCDXTAG pTag, int nPagesLeft ); + +/* Store tag header to index files */ +static void hb_cdxTagHeaderStore( LPCDXTAG pTag ); + +/* write all changed pages in tag cache */ +static void hb_cdxTagPoolFlush( LPCDXTAG pTag ); + +/* Discard all pages in cache (TagClose and TagPoolFree for all Tags) */ +static void hb_cdxIndexDiscardBuffers( LPCDXINDEX pIndex ); + +/* write all changed pages in cache (pagePool and Tag Header) */ +static void hb_cdxIndexFlushBuffers( LPCDXINDEX pIndex ); + +/* free cached pages of index file */ +static void hb_cdxIndexPoolFree( LPCDXINDEX pIndex, int nPagesLeft ); + +/* split Root Page */ +static int hb_cdxPageRootSplit( LPCDXPAGE pPage ); + +/* free create index structur */ +static void hb_cdxSortFree( LPCDXSORTINFO pSort ); + +static BOOL hb_cdxPageReadTopKey( LPCDXPAGE pPage ); + +static BOOL hb_cdxPageReadNextKey( LPCDXPAGE pPage ); + +static LPCDXTAG hb_cdxGetActiveTag( CDXAREAP pArea ); + +static void hb_cdxClearLogPosInfo( CDXAREAP pArea ); + +static USHORT s_uiRddId = ( USHORT ) -1; + +static BOOL bTurbo = FALSE; +static RDDFUNCS cdxSuper; +static const RDDFUNCS cdxTable = +{ + + /* Movement and positioning methods */ + + ( DBENTRYP_BP ) hb_cdxBof, + ( DBENTRYP_BP ) hb_cdxEof, + ( DBENTRYP_BP ) hb_cdxFound, + ( DBENTRYP_V ) hb_cdxGoBottom, + ( DBENTRYP_UL ) hb_cdxGoTo, + ( DBENTRYP_I ) hb_cdxGoToId, + ( DBENTRYP_V ) hb_cdxGoTop, + ( DBENTRYP_BIB ) hb_cdxSeek, + ( DBENTRYP_L ) hb_cdxSkip, + ( DBENTRYP_L ) hb_cdxSkipFilter, + ( DBENTRYP_L ) hb_cdxSkipRaw, + + + /* Data management */ + + ( DBENTRYP_VF ) hb_cdxAddField, + ( DBENTRYP_B ) hb_cdxAppend, + ( DBENTRYP_I ) hb_cdxCreateFields, + ( DBENTRYP_V ) hb_cdxDeleteRec, + ( DBENTRYP_BP ) hb_cdxDeleted, + ( DBENTRYP_SP ) hb_cdxFieldCount, + ( DBENTRYP_VF ) hb_cdxFieldDisplay, + ( DBENTRYP_SSI ) hb_cdxFieldInfo, + ( DBENTRYP_SVP ) hb_cdxFieldName, + ( DBENTRYP_V ) hb_cdxFlush, + ( DBENTRYP_PP ) hb_cdxGetRec, + ( DBENTRYP_SI ) hb_cdxGetValue, + ( DBENTRYP_SVL ) hb_cdxGetVarLen, + ( DBENTRYP_V ) hb_cdxGoCold, + ( DBENTRYP_V ) hb_cdxGoHot, + ( DBENTRYP_P ) hb_cdxPutRec, + ( DBENTRYP_SI ) hb_cdxPutValue, + ( DBENTRYP_V ) hb_cdxRecall, + ( DBENTRYP_ULP ) hb_cdxRecCount, + ( DBENTRYP_ISI ) hb_cdxRecInfo, + ( DBENTRYP_ULP ) hb_cdxRecNo, + ( DBENTRYP_I ) hb_cdxRecId, + ( DBENTRYP_S ) hb_cdxSetFieldExtent, + + + /* WorkArea/Database management */ + + ( DBENTRYP_P ) hb_cdxAlias, + ( DBENTRYP_V ) hb_cdxClose, + ( DBENTRYP_VP ) hb_cdxCreate, + ( DBENTRYP_SI ) hb_cdxInfo, + ( DBENTRYP_V ) hb_cdxNewArea, + ( DBENTRYP_VP ) hb_cdxOpen, + ( DBENTRYP_V ) hb_cdxRelease, + ( DBENTRYP_SP ) hb_cdxStructSize, + ( DBENTRYP_P ) hb_cdxSysName, + ( DBENTRYP_VEI ) hb_cdxEval, + ( DBENTRYP_V ) hb_cdxPack, + ( DBENTRYP_LSP ) hb_cdxPackRec, + ( DBENTRYP_VS ) hb_cdxSort, + ( DBENTRYP_VT ) hb_cdxTrans, + ( DBENTRYP_VT ) hb_cdxTransRec, + ( DBENTRYP_V ) hb_cdxZap, + + + /* Relational Methods */ + + ( DBENTRYP_VR ) hb_cdxChildEnd, + ( DBENTRYP_VR ) hb_cdxChildStart, + ( DBENTRYP_VR ) hb_cdxChildSync, + ( DBENTRYP_V ) hb_cdxSyncChildren, + ( DBENTRYP_V ) hb_cdxClearRel, + ( DBENTRYP_V ) hb_cdxForceRel, + ( DBENTRYP_SVP ) hb_cdxRelArea, + ( DBENTRYP_VR ) hb_cdxRelEval, + ( DBENTRYP_SVP ) hb_cdxRelText, + ( DBENTRYP_VR ) hb_cdxSetRel, + + + /* Order Management */ + + ( DBENTRYP_OI ) hb_cdxOrderListAdd, + ( DBENTRYP_V ) hb_cdxOrderListClear, + ( DBENTRYP_OI ) hb_cdxOrderListDelete, + ( DBENTRYP_OI ) hb_cdxOrderListFocus, + ( DBENTRYP_V ) hb_cdxOrderListRebuild, + ( DBENTRYP_VOI ) hb_cdxOrderCondition, + ( DBENTRYP_VOC ) hb_cdxOrderCreate, + ( DBENTRYP_OI ) hb_cdxOrderDestroy, + ( DBENTRYP_OII ) hb_cdxOrderInfo, + + + /* Filters and Scope Settings */ + + ( DBENTRYP_V ) hb_cdxClearFilter, + ( DBENTRYP_V ) hb_cdxClearLocate, + ( DBENTRYP_V ) hb_cdxClearScope, + ( DBENTRYP_VPLP ) hb_cdxCountScope, + ( DBENTRYP_I ) hb_cdxFilterText, + ( DBENTRYP_SI ) hb_cdxScopeInfo, + ( DBENTRYP_VFI ) hb_cdxSetFilter, + ( DBENTRYP_VLO ) hb_cdxSetLocate, + ( DBENTRYP_VOS ) hb_cdxSetScope, + ( DBENTRYP_VPL ) hb_cdxSkipScope, + ( DBENTRYP_B ) hb_cdxLocate, + + + /* Miscellaneous */ + + ( DBENTRYP_P ) hb_cdxCompile, + ( DBENTRYP_I ) hb_cdxError, + ( DBENTRYP_I ) hb_cdxEvalBlock, + + + /* Network operations */ + + ( DBENTRYP_VSP ) hb_cdxRawLock, + ( DBENTRYP_VL ) hb_cdxLock, + ( DBENTRYP_I ) hb_cdxUnLock, + + + /* Memofile functions */ + + ( DBENTRYP_V ) hb_cdxCloseMemFile, + ( DBENTRYP_VP ) hb_cdxCreateMemFile, + ( DBENTRYP_SVPB ) hb_cdxGetValueFile, + ( DBENTRYP_VP ) hb_cdxOpenMemFile, + ( DBENTRYP_SVPB ) hb_cdxPutValueFile, + + + /* Database file header handling */ + + ( DBENTRYP_V ) hb_cdxReadDBHeader, + ( DBENTRYP_V ) hb_cdxWriteDBHeader, + + + /* non WorkArea functions */ + ( DBENTRYP_R ) hb_cdxInit, + ( DBENTRYP_R ) hb_cdxExit, + ( DBENTRYP_RVV ) hb_cdxDrop, + ( DBENTRYP_RVV ) hb_cdxExists, + ( DBENTRYP_RSLV ) hb_cdxRddInfo, + + + /* Special and reserved methods */ + + ( DBENTRYP_SVP ) hb_cdxWhoCares +}; + + +#ifdef HB_CDX_DSPDBG_INFO +static void hb_cdxDspTags( LPCDXINDEX pIndex ) +{ + LPCDXTAG pTag = NULL; + + printf( "\r\n*TAGS*" ); + while ( pIndex ) + { + printf( "\r\nBAG: [%s] ->", pIndex->szFileName ); + pTag = pIndex->TagList; + while ( pTag ) + { + printf( " {%s}", pTag->szName ); + pTag = pTag->pNext; + } + pIndex = pIndex->pNext; + } + printf( "\r\n*END*\r\n" ); fflush( stdout ); +} +#endif + +#ifdef HB_CDX_DBGTIME +#include +typedef LONGLONG CDXDBGTIME; + +static CDXDBGTIME cdxTimeIntBld = 0; +static CDXDBGTIME cdxTimeExtBld = 0; +static CDXDBGTIME cdxTimeIntBlc = 0; +static CDXDBGTIME cdxTimeExtBlc = 0; +static CDXDBGTIME cdxTimeGetKey = 0; +static CDXDBGTIME cdxTimeFreeKey = 0; +static CDXDBGTIME cdxTimeIdxBld = 0; + +static CDXDBGTIME hb_cdxGetTime() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + return ( (CDXDBGTIME) tv.tv_sec * 1000000 + (CDXDBGTIME) tv.tv_usec ); +} +#endif +#ifdef HB_CDX_DBGUPDT +static ULONG cdxWriteNO = 0; +static ULONG cdxReadNO = 0; +static SHORT cdxStackSize = 0; +static SHORT cdxTmpStackSize = 0; +#endif + +/* + * internal BMDBFCDX function + */ + + +/* + * generate internal error + */ +static void hb_cdxErrInternal( char * szMsg ) +{ + hb_errInternal( 9201, szMsg ? szMsg : "hb_cdxErrInternal: data integrity error.", "", "" ); +} + +/* + * generate Run-Time error + */ +static ERRCODE hb_cdxErrorRT( CDXAREAP pArea, USHORT uiGenCode, USHORT uiSubCode, char * filename, USHORT uiOsCode, USHORT uiFlags ) +{ + PHB_ITEM pError; + ERRCODE iRet = FAILURE; + + if ( hb_vmRequestQuery() == 0 ) + { + pError = hb_errNew(); + hb_errPutGenCode( pError, uiGenCode ); + hb_errPutSubCode( pError, uiSubCode ); + hb_errPutOsCode( pError, uiOsCode ); + hb_errPutDescription( pError, hb_langDGetErrorDesc( uiGenCode ) ); + if ( filename ) + hb_errPutFileName( pError, filename ); + if ( uiFlags ) + hb_errPutFlags( pError, uiFlags ); + iRet = SELF_ERROR( ( AREAP ) pArea, pError ); + hb_errRelease( pError ); + } + return iRet; +} + +/* + * create index sort table + */ +static void hb_cdxMakeSortTab( CDXAREAP pArea ) +{ +#ifndef HB_CDP_SUPPORT_OFF + if ( pArea->cdPage && pArea->cdPage->lSort && !pArea->bCdxSortTab ) + { + int i, j, l; + BYTE * pbSort; + BYTE b; + + pArea->bCdxSortTab = ( BYTE * ) hb_xgrab( 256 ); + pbSort = ( BYTE * ) hb_xgrab( 256 ); + /* this table should be allready quite good sorted so this simple + algorithms will be one of the most efficient one. */ + for ( i = 0; i <= 255; i++ ) + pbSort[i] = ( BYTE ) i; + l = 255; + do + { + j = l; + for( i = 0; i < j; i++ ) + { + if ( hb_cdpchrcmp( pbSort[i], pbSort[i+1], pArea->cdPage ) > 0 ) + { + b = pbSort[i+1]; + pbSort[i+1] = pbSort[i]; + pbSort[i] = b; + l = i; + } + } + } while ( j != l ); + for ( i = 0; i <= 255; i++ ) + pArea->bCdxSortTab[pbSort[i]] = i; + hb_xfree( pbSort ); + } +#endif +} + +/* + * create new index key + */ +static LPCDXKEY hb_cdxKeyNew( void ) +{ + LPCDXKEY pKey; + + pKey = ( LPCDXKEY ) hb_xgrab( sizeof( CDXKEY ) ); + memset( pKey, 0, sizeof( CDXKEY ) ); + return pKey; +} + +/* + * Free index key + */ +static void hb_cdxKeyFree( LPCDXKEY pKey ) +{ + if ( pKey->val ) + hb_xfree( pKey->val ); + hb_xfree( pKey ); +} + +/* + * copy index key, if dst is null create new dst key else destroy dst + */ +static LPCDXKEY hb_cdxKeyCopy( LPCDXKEY pKeyDest, LPCDXKEY pKey ) +{ + if ( !pKeyDest ) + pKeyDest = hb_cdxKeyNew(); + else + { + pKeyDest->rec = 0; + if ( pKeyDest->val && pKeyDest->len != pKey->len ) + { + hb_xfree( pKeyDest->val ); + pKeyDest->val = NULL; + pKeyDest->len = 0; + } + } + if ( pKey ) + { + if ( pKey->len ) + { + if ( !pKeyDest->val ) + pKeyDest->val = (BYTE *) hb_xgrab( pKey->len + 1 ); + memcpy( pKeyDest->val, pKey->val, pKey->len ); + pKeyDest->len = pKey->len; + pKeyDest->val[ pKeyDest->len ] = '\0'; + } + pKeyDest->rec = pKey->rec; + } + return pKeyDest; +} + +/* + * store bytes value in inkdex key + */ +static LPCDXKEY hb_cdxKeyPut( LPCDXKEY pKey, BYTE * pbVal, USHORT uiLen, ULONG ulRec ) +{ + if ( !pKey ) + pKey = hb_cdxKeyNew(); + else + { + if ( pKey->val && pKey->len != uiLen ) + { + hb_xfree( pKey->val ); + pKey->val = NULL; + pKey->len = 0; + } + } + if ( pbVal && uiLen ) + { + pKey->len = (BYTE) uiLen; + if ( !pKey->val ) + pKey->val = ( BYTE * ) hb_xgrab( uiLen + 1 ); + memcpy( pKey->val, pbVal, uiLen ); + pKey->val[ uiLen ] = '\0'; + } + pKey->rec = ulRec; + return pKey; +} + +/* + * store string0 value in index key + */ +static LPCDXKEY hb_cdxKeyPutC( LPCDXKEY pKey, char * szText, USHORT uiRealLen, ULONG ulRec ) +{ + USHORT uiLen; + + if ( !pKey ) + pKey = hb_cdxKeyNew(); + else + { + if ( pKey->val ) + { + hb_xfree( pKey->val ); + pKey->val = NULL; + pKey->len = 0; + } + } + uiLen = (USHORT) ( szText ? strlen( szText ) : 0 ); + if ( uiLen > uiRealLen ) + uiLen = uiRealLen; + pKey->len = ( BYTE ) uiRealLen; + pKey->val = ( BYTE * ) hb_xgrab( uiRealLen + 1 ); + if ( uiLen ) + memcpy( pKey->val, szText, uiLen ); + if ( uiLen < uiRealLen ) + memset( &pKey->val[ uiLen ], ' ', uiRealLen - uiLen ); + pKey->val[ uiRealLen ] = '\0'; + pKey->rec = ulRec; + return pKey; +} + +/* + * compare two values using Tag conditions (len & type) + */ +static int hb_cdxValCompare( LPCDXTAG pTag, BYTE * val1, BYTE len1, + BYTE * val2, BYTE len2, BOOL fExact ) +{ + int iLimit, iResult = 0; + + iLimit = (len1 > len2) ? len2 : len1; + + if ( pTag->uiType == 'C' ) + { +#ifndef HB_CDP_SUPPORT_OFF + if ( pTag->pIndex->pArea->bCdxSortTab ) + { + BYTE * pSort = pTag->pIndex->pArea->bCdxSortTab; + int iPos = 0; + while ( iResult == 0 && iPos < iLimit ) + { + iResult = pSort[ val1[ iPos ] ] - pSort[ val2[ iPos ] ]; + iPos++; + } + } + else +#endif + if ( iLimit > 0 ) + iResult = memcmp( val1, val2, iLimit ); + + if ( iResult == 0 ) + { + if ( len1 > len2 ) + iResult = 1; + else if ( len1 < len2 && fExact ) + iResult = -1; + } + } + else + { + if ( iLimit == 0 || (iResult = memcmp( val1, val2, iLimit )) == 0 ) + { + if ( len1 > len2 ) + iResult = 1; + else if ( len1 < len2 ) + iResult = -1; + } + } + return iResult; +} + +/* + * get CDX key type for given item + */ +static BYTE hb_cdxItemType( PHB_ITEM pItem ) +{ + switch( hb_itemType( pItem ) ) + { + case HB_IT_STRING: + case HB_IT_STRING | HB_IT_MEMO: + return 'C'; + + case HB_IT_INTEGER: + case HB_IT_LONG: + case HB_IT_DOUBLE: + return 'N'; + + case HB_IT_DATE: + return 'D'; + + case HB_IT_LOGICAL: + return 'L'; + + default: + return 'U'; + } +} + + +/* + * store Item in index key + * TODO: uiType check and generate RT error if necessary + */ +static LPCDXKEY hb_cdxKeyPutItem( LPCDXKEY pKey, PHB_ITEM pItem, ULONG ulRec, LPCDXTAG pTag, BOOL fTrans, BOOL fSize ) +{ + BYTE buf[CDX_MAXKEY], *ptr; + ULONG ulLen = 0; + double d; + + ptr = &buf[0]; + + switch ( hb_cdxItemType( pItem ) ) + { + case 'C': + ulLen = hb_itemGetCLen( pItem ); + if( ulLen > (ULONG) pTag->uiLen ) + ulLen = pTag->uiLen; + if ( fSize && ulLen < (ULONG) pTag->uiLen ) + { + memcpy( ptr, hb_itemGetCPtr( pItem ), ulLen ); + memset( ptr + ulLen, pTag->bTrail, pTag->uiLen - ulLen ); + ulLen = pTag->uiLen; + } + else + { + ptr = ( BYTE * ) hb_itemGetCPtr( pItem ); + } + break; + case 'N': + d = hb_itemGetND( pItem ); + HB_DBL2ORD( &d, ptr ); + ulLen = 8; + break; + case 'D': + d = (double) hb_itemGetDL( pItem ); + HB_DBL2ORD( &d, ptr ); + ulLen = 8; + break; + case 'L': + *ptr = (BYTE) ( hb_itemGetL( pItem ) ? 'T' : 'F' ); + ulLen = 1; + break; + default: + ptr = NULL; + break; + } + pKey = hb_cdxKeyPut( pKey, ptr, ( USHORT ) ulLen, ulRec ); +#ifndef HB_CDP_SUPPORT_OFF + if ( fTrans && pTag->uiType == 'C' ) + hb_cdpnTranslate( ( char * ) pKey->val, hb_cdp_page, pTag->pIndex->pArea->cdPage, pKey->len ); +#endif + return pKey; +} + +/* + * get Item from index key + */ +static PHB_ITEM hb_cdxKeyGetItem( LPCDXKEY pKey, PHB_ITEM pItem, LPCDXTAG pTag, BOOL fTrans ) +{ + double d; + + if ( pKey ) + { + switch( pTag->uiType ) + { + case 'C': +#ifndef HB_CDP_SUPPORT_OFF + if( fTrans && pTag->pIndex->pArea->cdPage != hb_cdp_page ) + { + char * pVal = ( char * ) hb_xgrab( pKey->len + 1 ); + memcpy( pVal, pKey->val, pKey->len ); + pVal[ pKey->len ] = '\0'; + hb_cdpnTranslate( pVal, pTag->pIndex->pArea->cdPage, hb_cdp_page, + pKey->len ); + pItem = hb_itemPutCPtr( pItem, pVal, pKey->len ); + } + else +#endif + { + pItem = hb_itemPutCL( pItem, ( char * ) pKey->val, pKey->len ); + } + break; + case 'N': + HB_ORD2DBL( pKey->val, &d ); + pItem = hb_itemPutND( pItem, d ); + break; + case 'D': + HB_ORD2DBL( pKey->val, &d ); + pItem = hb_itemPutDL( pItem, ( LONG ) d ); + break; + case 'L': + pItem = hb_itemPutL( pItem, pKey->val[0] == 'T' ); + break; + default: + if ( pItem ) + hb_itemClear( pItem ); + else + pItem = hb_itemNew( NULL ); + } + } + else if ( pItem ) + hb_itemClear( pItem ); + else + pItem = hb_itemNew( NULL ); + + return pItem; +} + +/* + * evaluate key expression and create new Key from the result + */ +static LPCDXKEY hb_cdxKeyEval( LPCDXKEY pKey, LPCDXTAG pTag ) +{ + CDXAREAP pArea = pTag->pIndex->pArea; + PHB_ITEM pItem; +#ifndef HB_CDP_SUPPORT_OFF + /* TODO: this hack is not thread safe, hb_cdp_page has to be thread specific */ + PHB_CODEPAGE cdpTmp = hb_cdp_page; + hb_cdp_page = pArea->cdPage; +#endif + + if ( pTag->nField ) + { + pItem = hb_itemNew( NULL ); + SELF_GETVALUE( ( AREAP ) pArea, pTag->nField, pItem ); + pKey = hb_cdxKeyPutItem( pKey, pItem, pArea->ulRecNo, pTag, FALSE, TRUE ); + hb_itemRelease( pItem ); + } + else + { + int iCurrArea = hb_rddGetCurrentWorkAreaNumber(); + + if ( iCurrArea != pArea->uiArea ) + hb_rddSelectWorkAreaNumber( pArea->uiArea ); + else + iCurrArea = 0; + + pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem ); + pKey = hb_cdxKeyPutItem( pKey, pItem, pArea->ulRecNo, pTag, FALSE, TRUE ); + + if ( iCurrArea ) + hb_rddSelectWorkAreaNumber( iCurrArea ); + } + +#ifndef HB_CDP_SUPPORT_OFF + hb_cdp_page = cdpTmp; +#endif + + return pKey; +} + +/* + * evaluate conditional expression and return the result + */ +static BOOL hb_cdxEvalCond( CDXAREAP pArea, PHB_ITEM pCondItem, BOOL fSetWA ) +{ + int iCurrArea = 0; + BOOL fRet; + + if ( fSetWA ) { + iCurrArea = hb_rddGetCurrentWorkAreaNumber(); + if ( iCurrArea != pArea->uiArea ) + hb_rddSelectWorkAreaNumber( pArea->uiArea ); + else + iCurrArea = 0; + } + + fRet = hb_itemGetL( hb_vmEvalBlockOrMacro( pCondItem ) ); + + if ( iCurrArea ) + hb_rddSelectWorkAreaNumber( iCurrArea ); + + return fRet; +} + +/* + * evaluate seek/skip block: {|key, rec| ... } + */ +static BOOL hb_cdxEvalSeekCond( LPCDXTAG pTag, PHB_ITEM pCondItem ) +{ + BOOL fRet; + PHB_ITEM pKeyVal, pKeyRec; + + pKeyVal = hb_cdxKeyGetItem( pTag->CurKey, NULL, pTag, TRUE ); + pKeyRec = hb_itemPutNInt( NULL, pTag->CurKey->rec ); + + fRet = hb_itemGetL( hb_vmEvalBlockV( pCondItem, 2, pKeyVal, pKeyRec ) ); + + hb_itemRelease( pKeyVal ); + hb_itemRelease( pKeyRec ); + + return fRet; +} + +/* + * check if Key is in top scope + */ +static BOOL hb_cdxTopScope( LPCDXTAG pTag ) +{ + LPCDXKEY pKey; + + if ( pTag->UsrAscend ) + { + pKey = pTag->topScopeKey; + return !pKey || !pKey->len || + hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, FALSE ) <= 0; + } + else + { + pKey = pTag->bottomScopeKey; + return !pKey || !pKey->len || + hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, FALSE ) >= 0; + } +} + +/* + * check if Key is in bottom scope + */ +static BOOL hb_cdxBottomScope( LPCDXTAG pTag ) +{ + LPCDXKEY pKey; + + if ( pTag->UsrAscend ) + { + pKey = pTag->bottomScopeKey; + return !pKey || !pKey->len || + hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, FALSE ) >= 0; + } + else + { + pKey = pTag->topScopeKey; + return !pKey || !pKey->len || + hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, FALSE ) <= 0; + } +} + +/* + * clear top or bottom scope + */ +static void hb_cdxTagClearScope( LPCDXTAG pTag, USHORT nScope ) +{ + CDXAREAP pArea = pTag->pIndex->pArea; + LPCDXKEY *pScopeKey; + PHB_ITEM *pScope; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxTagClearScope(%p, %hu)", pTag, nScope)); + + /* resolve any pending scope relations first */ + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + if ( pTag->UsrAscend ? nScope == 0 : nScope != 0 ) + { + pScope = &pTag->topScope; + pScopeKey = &pTag->topScopeKey; + } + else + { + pScope = &pTag->bottomScope; + pScopeKey = &pTag->bottomScopeKey; + } + if ( *pScope ) + { + hb_itemRelease( *pScope ); + *pScope = NULL; + } + if ( *pScopeKey ) + { + hb_cdxKeyFree( *pScopeKey ); + *pScopeKey = NULL; + pTag->curKeyState &= ~( CDX_CURKEY_RAWCNT | CDX_CURKEY_LOGCNT ); + if ( nScope == 0 ) + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS ); + } +} + +/* + * set top or bottom scope + */ +static void hb_cdxTagSetScope( LPCDXTAG pTag, USHORT nScope, PHB_ITEM pItem ) +{ + CDXAREAP pArea = pTag->pIndex->pArea; + PHB_ITEM pScopeVal; + + /* resolve any pending scope relations first */ + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pScopeVal = ( hb_itemType( pItem ) == HB_IT_BLOCK ) ? + hb_vmEvalBlock( pItem ) : pItem; + + if ( pTag->uiType == hb_cdxItemType( pScopeVal ) ) + { + PHB_ITEM *pScope; + LPCDXKEY *pScopeKey; + ULONG ulRec; + + if ( pTag->UsrAscend ? nScope == 0 : nScope != 0 ) + { + pScope = &(pTag->topScope); + pScopeKey = &(pTag->topScopeKey); + ulRec = CDX_IGNORE_REC_NUM; + } + else + { + pScope = &(pTag->bottomScope); + pScopeKey = &(pTag->bottomScopeKey); + ulRec = CDX_MAX_REC_NUM; + } + + if ( *pScope == NULL ) + *pScope = hb_itemNew( NULL ); + hb_itemCopy( *pScope, pItem ); + *pScopeKey = hb_cdxKeyPutItem( *pScopeKey, pScopeVal, ulRec, pTag, TRUE, FALSE ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWCNT | CDX_CURKEY_LOGCNT ); + if ( nScope == 0 ) + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS ); + } + else + { + /* TODO: !!! + * RT error: BMDBFCDX/1051 Scope Type Mismatch + * hb_cdxErrorRT + */ + } +} + +static void hb_cdxTagGetScope( LPCDXTAG pTag, USHORT nScope, PHB_ITEM pItem ) +{ + CDXAREAP pArea = pTag->pIndex->pArea; + PHB_ITEM *pScope; + + /* resolve any pending scoped relations first */ + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pScope = ( pTag->UsrAscend ? nScope == 0 : nScope != 0 ) ? + &(pTag->topScope) : &(pTag->bottomScope); + if ( *pScope ) + hb_itemCopy( pItem, *pScope ); + else + hb_itemClear( pItem ); +} + +/* + * refresh top and bottom scope value if set as codeblock + */ +static void hb_cdxTagRefreshScope( LPCDXTAG pTag ) +{ + PHB_ITEM pItem; + + if( pTag->pIndex->pArea->lpdbPendingRel && + pTag->pIndex->pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pTag->pIndex->pArea ); + + if ( hb_itemType( pTag->topScope ) == HB_IT_BLOCK ) + { + pItem = hb_vmEvalBlock( pTag->topScope ); + pTag->topScopeKey = hb_cdxKeyPutItem( pTag->topScopeKey, pItem, + pTag->topScopeKey->rec, pTag, TRUE, FALSE ); + } + if ( hb_itemType( pTag->bottomScope ) == HB_IT_BLOCK ) + { + pItem = hb_vmEvalBlock( pTag->bottomScope ); + pTag->bottomScopeKey = hb_cdxKeyPutItem( pTag->bottomScopeKey, pItem, + pTag->bottomScopeKey->rec, pTag, TRUE, FALSE ); + } +} + +#ifdef HB_CDX_DBGCODE_EXT +/* + * check internal integrity of page pool + */ +static void hb_cdxTagPoolCheck( LPCDXTAG pTag ) +{ + LPCDXPAGE pPage, pPrevPage; + + pPage = pTag->pagePool; + pPrevPage = NULL; + while ( pPage ) + { + if ( pPage->pPoolPrev != pPrevPage || pPage->TagParent != pTag ) + hb_cdxErrInternal( "hb_cdxTagPoolCheck: data integrity error." ); + pPrevPage = pPage; + pPage = pPage->pPoolNext; + } +} + +/* + * check if the Tag buffers was not changed without write lock + */ +static void hb_cdxTagCheckBuffers( LPCDXTAG pTag ) +{ + BOOL fChanged = FALSE; + + hb_cdxTagPoolCheck( pTag ); + if ( pTag->TagChanged ) + fChanged = TRUE; + else + { + LPCDXPAGE pPage = pTag->pagePool; + while ( pPage && !fChanged ) + { + fChanged = pPage->fChanged; + pPage = pPage->pPoolNext; + } + } + if ( fChanged ) + hb_cdxErrInternal( "hb_cdxTagCheckBuffers: modification without write lock." ); +} + +/* + * check if the Index buffers was not changed without write lock + */ +static void hb_cdxIndexCheckBuffers( LPCDXINDEX pIndex ) +{ + LPCDXTAG pTag; + + if ( pIndex->fChanged || ( pIndex->freeLst && pIndex->freeLst->fStat ) ) + hb_cdxErrInternal( "hb_cdxIndexCheckBuffers: modification without write lock." ); + + if ( pIndex->pCompound ) + hb_cdxTagCheckBuffers( pIndex->pCompound ); + pTag = pIndex->TagList; + while ( pTag ) + { + hb_cdxTagCheckBuffers( pTag ); + pTag = pTag->pNext; + } +} +#endif + +/* + * get free index page + */ +static ULONG hb_cdxIndexGetAvailPage( LPCDXINDEX pIndex, BOOL bHeader ) +{ + FHANDLE hFile = pIndex->hFile; + BYTE byBuf[4]; + ULONG ulPos; + + if ( pIndex->fReadonly ) + { + hb_errInternal( 9101, "hb_cdxIndexGetAvailPage on readonly database.", "", "" ); + } + if ( pIndex->fShared && !pIndex->lockWrite ) + { + hb_errInternal( 9102, "hb_cdxIndexGetAvailPage on not locked index file.", "", "" ); + } + + if ( pIndex->freePage != 0 && pIndex->freePage != CDX_DUMMYNODE && !bHeader ) + { + ulPos = pIndex->freePage; + if ( pIndex->freeLst != NULL ) + { + LPCDXLIST pLst = pIndex->freeLst; + pIndex->freePage = pLst->ulAddr; + pIndex->freeLst = pLst->pNext; + hb_xfree( pLst ); + } + else + { + if ( hb_fsSeekLarge( hFile, ulPos, FS_SET ) != ( HB_FOFFSET ) ulPos || + hb_fsRead( hFile, (BYTE *) byBuf, 4 ) != 4 ) + hb_errInternal( EDBF_READ, "hb_cdxIndexGetAvailPage: Read index page failed.", "", "" ); + pIndex->freePage = HB_GET_LE_UINT32( byBuf ); +#ifdef HB_CDX_DBGUPDT + cdxReadNO++; +#endif + } + } + else + { + int iCnt = ( bHeader ? CDX_HEADERPAGES : 1 ); + + if ( pIndex->nextAvail != CDX_DUMMYNODE ) + ulPos = pIndex->nextAvail; + else + ulPos = ( ULONG ) hb_fsSeekLarge( hFile, 0, FS_END ); + pIndex->nextAvail = ulPos + iCnt * CDX_PAGELEN; + + /* TODO: ### */ + if ( bHeader ) + { + BYTE byBuf[CDX_PAGELEN]; + memset( byBuf, 0, CDX_PAGELEN ); + if ( hb_fsSeekLarge( hFile, ulPos, FS_SET ) != ( HB_FOFFSET ) ulPos ) + hb_errInternal( EDBF_WRITE, "Write in index page failed.(1)", "", "" ); + while ( iCnt-- ) + { + if ( hb_fsWrite( hFile, byBuf, CDX_PAGELEN ) != CDX_PAGELEN ) + hb_errInternal( EDBF_WRITE, "Write in index page failed.(2)", "", "" ); + } + pIndex->fChanged = TRUE; + } + } + return ulPos; +} + +/* + * free index page + */ +static void hb_cdxIndexPutAvailPage( LPCDXINDEX pIndex, ULONG ulPos, BOOL bHeader ) +{ + if ( ulPos != 0 && ulPos != CDX_DUMMYNODE ) + { + int iCnt = ( bHeader ? CDX_HEADERPAGES : 1 ); + LPCDXLIST pLst; + + if ( pIndex->fReadonly ) + hb_errInternal( 9101, "hb_cdxIndexPutAvailPage on readonly database.", "", "" ); + if ( pIndex->fShared && !pIndex->lockWrite ) + hb_errInternal( 9102, "hb_cdxIndexPutAvailPage on not locked index file.", "", "" ); + + while ( iCnt-- ) + { + pLst = (LPCDXLIST) hb_xgrab( sizeof( CDXLIST ) ); + pLst->ulAddr = pIndex->freePage; + pIndex->freePage = ulPos; + pLst->fStat = TRUE; + pLst->pNext = pIndex->freeLst; + pIndex->freeLst = pLst; + ulPos += CDX_PAGELEN; + } + } +} + +/* + * flush list of free pages into index file + */ +static void hb_cdxIndexFlushAvailPage( LPCDXINDEX pIndex ) +{ + LPCDXLIST pLst = pIndex->freeLst; + BYTE byPageBuf[CDX_PAGELEN]; + ULONG ulPos; + BOOL fClean = TRUE; + + if ( pIndex->fReadonly ) + hb_errInternal( 9101, "hb_cdxIndexPutAvailPage on readonly database.", "", "" ); + if ( pIndex->fShared && !pIndex->lockWrite ) + hb_errInternal( 9102, "hb_cdxIndexPutAvailPage on not locked index file.", "", "" ); + + ulPos = pIndex->freePage; + while ( pLst && pLst->fStat ) + { + if ( fClean ) + { + memset( byPageBuf, 0, CDX_PAGELEN ); + fClean = FALSE; + } + HB_PUT_LE_UINT32( byPageBuf, pLst->ulAddr ); + if ( hb_fsSeekLarge( pIndex->hFile, ulPos, FS_SET ) != ( HB_FOFFSET ) ulPos || + hb_fsWrite( pIndex->hFile, byPageBuf, CDX_PAGELEN ) != CDX_PAGELEN ) + { + hb_errInternal( EDBF_WRITE, "Write in index page failed.", "", "" ); + } + pIndex->fChanged = TRUE; + ulPos = pLst->ulAddr; + pLst->fStat = FALSE; + pLst = pLst->pNext; +#ifdef HB_CDX_DBGUPDT + cdxWriteNO++; +#endif + } +} + +/* + * drop list of free pages in index file + */ +static void hb_cdxIndexDropAvailPage( LPCDXINDEX pIndex ) +{ + LPCDXLIST pLst; + + while ( pIndex->freeLst ) + { + pLst = pIndex->freeLst->pNext; + hb_xfree( pIndex->freeLst ); + pIndex->freeLst = pLst; + } +} + +/* + * write index page + */ +static void hb_cdxIndexPageWrite( LPCDXINDEX pIndex, ULONG ulPos, BYTE * pBuffer, + USHORT uiSize ) +{ + if ( pIndex->fReadonly ) + hb_errInternal( 9101, "hb_cdxIndexPageWrite on readonly database.", "", "" ); + if ( pIndex->fShared && !pIndex->lockWrite ) + hb_errInternal( 9102, "hb_cdxIndexPageWrite on not locked index file.", "", "" ); + + if ( hb_fsSeekLarge( pIndex->hFile, ulPos, FS_SET ) != ( HB_FOFFSET ) ulPos || + hb_fsWrite( pIndex->hFile, pBuffer, uiSize ) != uiSize ) + hb_errInternal( EDBF_WRITE, "Write in index page failed.", "", "" ); + pIndex->fChanged = TRUE; +#ifdef HB_CDX_DBGUPDT + cdxWriteNO++; +#endif +} + +/* + * read index page + */ +static void hb_cdxIndexPageRead( LPCDXINDEX pIndex, ULONG ulPos, BYTE * pBuffer, + USHORT uiSize ) +{ + if ( pIndex->fShared && !( pIndex->lockRead || pIndex->lockWrite ) ) + hb_errInternal( 9103, "hb_cdxIndexPageRead on not locked index file.", "", "" ); + + if ( hb_fsSeekLarge( pIndex->hFile, ulPos, FS_SET ) != ( HB_FOFFSET ) ulPos || + hb_fsRead( pIndex->hFile, pBuffer, uiSize ) != uiSize ) + hb_errInternal( EDBF_READ, "hb_cdxIndexPageRead: Read index page failed.", "", "" ); +#ifdef HB_CDX_DBGUPDT + cdxReadNO++; +#endif +} + +/* + * check if index was updated by other process and if it was discard buffers + */ +static void hb_cdxIndexCheckVersion( LPCDXINDEX pIndex ) +{ + BYTE byBuf[8]; + ULONG ulVer, ulFree; + + if ( hb_fsSeek( pIndex->hFile, 0x04, FS_SET ) != 0x04 || + hb_fsRead( pIndex->hFile, byBuf, 8 ) != 8 ) + { + if ( pIndex->lockWrite > 0 && hb_fsSeek( pIndex->hFile, 0, FS_END ) == 0 ) + memset( byBuf, 0, 8 ); + else + hb_errInternal( 2155, "hb_cdxIndexCheckVersion: Read error on index heading page.", "", "" ); + } +#ifdef HB_CDX_DBGUPDT + cdxReadNO++; +#endif + ulFree = HB_GET_LE_UINT32( &byBuf[0] ); + ulVer = HB_GET_BE_UINT32( &byBuf[4] ); + if ( !pIndex->fShared ) + pIndex->ulVersion = pIndex->freePage; + else if ( ulVer != pIndex->ulVersion || ulFree != pIndex->freePage ) + { + pIndex->nextAvail = CDX_DUMMYNODE; + pIndex->ulVersion = ulVer; + pIndex->freePage = ulFree; + hb_cdxIndexDiscardBuffers( pIndex ); + } + /* TODO: !!! ## remove it it's for test only */ + /* hb_cdxIndexDiscardBuffers( pIndex ); */ +} + +/* + * lock index for reading (shared lock) + */ +static BOOL hb_cdxIndexLockRead( LPCDXINDEX pIndex ) +{ + BOOL ret; + + if ( pIndex->lockRead > 0 || pIndex->lockWrite > 0 || + !pIndex->pArea->fShared || !pIndex->fShared ) + { + pIndex->lockRead++; + return TRUE; + } +#ifdef HB_CDX_DBGCODE + if ( pIndex->lockRead != 0 ) + hb_errInternal( 9105, "hb_cdxIndexLockRead: bad count of locks.", "", "" ); + + if ( pIndex->WrLck || pIndex->RdLck ) + hb_errInternal( 9107, "hb_cdxIndexLockRead: lock failure (*)", "", "" ); + pIndex->RdLck = TRUE; +#endif + + if ( bTurbo ) + { + pIndex->lockRead++; + hb_cdxIndexCheckVersion( pIndex ); + return TRUE; + } + else + { + ret = hb_dbfLockIdxFile( pIndex->hFile, pIndex->pArea->bLockType, + FL_LOCK | FLX_SHARED | FLX_WAIT, &pIndex->ulLockPos ); + if ( !ret ) + hb_cdxErrorRT( pIndex->pArea, EG_LOCK, EDBF_LOCK, pIndex->szFileName, hb_fsError(), 0 ); + + if ( ret ) + { + pIndex->lockRead++; + hb_cdxIndexCheckVersion( pIndex ); + } + return ret; + } + +} + +/* + * lock index for writing (exclusive lock) + */ +static BOOL hb_cdxIndexLockWrite( LPCDXINDEX pIndex ) +{ + BOOL ret; + + if ( pIndex->fReadonly ) + hb_errInternal( 9101, "hb_cdxIndexLockWrite: readonly index.", "", "" ); + if ( pIndex->lockRead ) + hb_errInternal( 9105, "hb_cdxIndexLockWrite: writeLock after readLock.", "", "" ); + if ( pIndex->lockWrite > 0 ) + { + pIndex->lockWrite++; + return TRUE; + } + if ( pIndex->lockWrite != 0 ) + hb_errInternal( 9105, "hb_cdxIndexLockWrite: bad count of locks.", "", "" ); + + if ( !pIndex->pArea->fShared || !pIndex->fShared ) + ret = TRUE; + else + { +#ifdef HB_CDX_DBGCODE + if ( pIndex->WrLck || pIndex->RdLck ) + hb_errInternal( 9107, "hb_cdxIndexLockWrite: lock failure (*)", "", "" ); + pIndex->WrLck = TRUE; +#endif + ret = hb_dbfLockIdxFile( pIndex->hFile, pIndex->pArea->bLockType, + FL_LOCK | FLX_EXCLUSIVE | FLX_WAIT, &pIndex->ulLockPos ); + } + if ( !ret ) + hb_cdxErrorRT( pIndex->pArea, EG_LOCK, EDBF_LOCK, pIndex->szFileName, hb_fsError(), 0 ); + + if ( ret ) + { + pIndex->lockWrite++; + if ( pIndex->fShared || pIndex->nextAvail == CDX_DUMMYNODE ) + hb_cdxIndexCheckVersion( pIndex ); + } + return ret; +} + +/* + * remove index read lock (shared lock) + */ +static BOOL hb_cdxIndexUnLockRead( LPCDXINDEX pIndex ) +{ + pIndex->lockRead--; + if ( pIndex->lockRead < 0 ) + { + hb_errInternal( 9106, "hb_cdxIndexUnLockRead: bad count of locks.", "", "" ); + } + if ( pIndex->lockRead || pIndex->lockWrite ) + { + return TRUE; + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxIndexCheckBuffers( pIndex ); +#endif + + hb_cdxIndexPoolFree( pIndex, CDX_PAGECACHESIZE ); + + if ( bTurbo ) + { +#ifdef HB_CDX_DBGCODE + if ( pIndex->pArea->fShared && pIndex->fShared ) + pIndex->RdLck = FALSE; +#endif + } + else + { + if ( pIndex->pArea->fShared && pIndex->fShared ) + { +#ifdef HB_CDX_DBGCODE + if ( pIndex->WrLck || ! pIndex->RdLck ) + hb_errInternal( 9108, "hb_cdxIndexUnLockRead: unlock error (*)", "", "" ); + pIndex->RdLck = FALSE; +#endif + if ( !hb_dbfLockIdxFile( pIndex->hFile, pIndex->pArea->bLockType, FL_UNLOCK, &pIndex->ulLockPos ) ) + { + hb_errInternal( 9108, "hb_cdxIndexUnLockRead: unlock error.", "", "" ); + } + } + } + return TRUE; +} + +/* + * remove index write lock (exclusive lock) + */ +static BOOL hb_cdxIndexUnLockWrite( LPCDXINDEX pIndex ) +{ + if ( pIndex->lockWrite > 1 ) + { + pIndex->lockWrite--; + return TRUE; + } + + if ( pIndex->lockWrite < 1 ) + { + hb_errInternal( 9106, "hb_cdxIndexUnLockWrite: bad count of locks.", "", "" ); + } + if ( pIndex->lockRead ) + { + hb_errInternal( 9105, "hb_cdxIndexUnLockWrite: writeUnLock before readUnLock.", "", "" ); + } + + hb_cdxIndexFlushBuffers( pIndex ); + hb_cdxIndexPoolFree( pIndex, CDX_PAGECACHESIZE ); + + pIndex->lockWrite--; + if ( pIndex->pArea->fShared && pIndex->fShared ) + { + if ( pIndex->fChanged ) + { + BYTE byBuf[8]; + (pIndex->ulVersion)++; + HB_PUT_LE_UINT32( &byBuf[0], pIndex->freePage ); + HB_PUT_BE_UINT32( &byBuf[4], pIndex->ulVersion ); + if ( hb_fsSeek( pIndex->hFile, 0x04, FS_SET ) != 0x04 || + hb_fsWrite( pIndex->hFile, byBuf, 8) != 8 ) + { + hb_errInternal( EDBF_WRITE, "Write in index page failed (ver)", "", "" ); + } + pIndex->fFlush = TRUE; + pIndex->fChanged = FALSE; + } +#ifdef HB_CDX_DBGCODE + if ( ! pIndex->WrLck || pIndex->RdLck ) + hb_errInternal( 9108, "hb_cdxIndexUnLockWrite: unlock error (*)", "", "" ); + pIndex->WrLck = FALSE; +#endif + if ( !hb_dbfLockIdxFile( pIndex->hFile, pIndex->pArea->bLockType, FL_UNLOCK, &pIndex->ulLockPos ) ) + { + hb_errInternal( 9108, "hb_cdxIndexUnLockWrite: unlock error.", "", "" ); + } + } + else + { + if ( pIndex->ulVersion != pIndex->freePage ) + { + BYTE byBuf[4]; + HB_PUT_LE_UINT32( &byBuf[0], pIndex->freePage ); + if ( hb_fsSeek( pIndex->hFile, 0x04, FS_SET ) != 0x04 || + hb_fsWrite( pIndex->hFile, byBuf, 4) != 4 ) + { + hb_errInternal( EDBF_WRITE, "Write in index page failed (ver.ex)", "", "" ); + } + pIndex->ulVersion = pIndex->freePage; + pIndex->fFlush = TRUE; +#ifdef HB_CDX_DBGUPDT + cdxWriteNO++; +#endif + } + else if ( pIndex->fChanged ) + { + pIndex->fFlush = TRUE; + } + pIndex->fChanged = FALSE; + } + return TRUE; +} + +/* + * discard all pages in cache (TagClose and TagPoolFree for all Tags) + */ +static void hb_cdxIndexDiscardBuffers( LPCDXINDEX pIndex ) +{ + LPCDXTAG pTag; + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxIndexCheckBuffers( pIndex ); +#endif + + hb_cdxIndexDropAvailPage( pIndex ); + if ( pIndex->pCompound ) + { + hb_cdxTagClose( pIndex->pCompound ); + hb_cdxTagPoolFree( pIndex->pCompound, 0 ); + pIndex->pCompound->fRePos = TRUE; + pIndex->pCompound->curKeyState = 0; + if ( pIndex->pCompound->CurKey ) + pIndex->pCompound->CurKey->rec = 0; + } + pTag = pIndex->TagList; + while ( pTag ) + { + hb_cdxTagClose( pTag ); + hb_cdxTagPoolFree( pTag, 0 ); + pTag->fRePos = TRUE; + pTag->curKeyState = 0; + if ( pTag->CurKey && !pTag->Custom ) + pTag->CurKey->rec = 0; + pTag = pTag->pNext; + } +} + +/* + * write all changed pages in cache (pagePool, pages in Tags and Tag Header) + */ +static void hb_cdxIndexFlushBuffers( LPCDXINDEX pIndex ) +{ + LPCDXTAG pTag; + + if ( pIndex->pCompound ) + { + hb_cdxTagPoolFlush( pIndex->pCompound ); + if ( pIndex->pCompound->TagChanged ) + hb_cdxTagHeaderStore( pIndex->pCompound ); + } + pTag = pIndex->TagList; + while ( pTag ) + { + hb_cdxTagPoolFlush( pTag ); + if ( pTag->TagChanged ) + hb_cdxTagHeaderStore( pTag ); + pTag = pTag->pNext; + } + hb_cdxIndexFlushAvailPage( pIndex ); +} + +/* + * free cached pages of index file + */ +static void hb_cdxIndexPoolFree( LPCDXINDEX pIndex, int nPagesLeft ) +{ + LPCDXTAG pTag; + + if ( pIndex->pCompound ) + { + hb_cdxTagPoolFree( pIndex->pCompound, nPagesLeft ); + } + pTag = pIndex->TagList; + while ( pTag ) + { + hb_cdxTagPoolFree( pTag, nPagesLeft ); + pTag = pTag->pNext; + } +} + +/* + * get key value ptr from index page + */ +static BYTE * hb_cdxPageGetKeyVal( LPCDXPAGE pPage, SHORT iKey ) +{ +#ifdef HB_CDX_DBGCODE + if ( iKey < 0 || iKey >= pPage->iKeys ) + hb_cdxErrInternal( "hb_cdxPageGetKeyVal: wrong iKey index." ); +#endif + if ( pPage->pKeyBuf ) + return &pPage->pKeyBuf[ iKey * ( pPage->TagParent->uiLen + 6 ) ]; + else if ( pPage->PageType & CDX_NODE_LEAF ) + { + SHORT iPos, iLen, iTmp, iTrl, iDup; + BYTE bTrail; + + iLen = pPage->TagParent->uiLen; + bTrail = pPage->TagParent->bTrail; + if ( iKey < pPage->bufKeyNum - 1 ) + pPage->bufKeyNum = 0; + if ( pPage->bufKeyNum == 0 ) + { + pPage->bufKeyPos = CDX_EXT_FREESPACE; + pPage->bufKeyLen = iLen; + } + while ( pPage->bufKeyNum <= iKey ) + { + iPos = pPage->bufKeyNum * pPage->ReqByte; + iTmp = HB_GET_LE_UINT16( &pPage->node.extNode.keyPool[ iPos + pPage->ReqByte - 2 ] ) >> + ( 16 - pPage->TCBits - pPage->DCBits ); + iDup = ( pPage->bufKeyNum == 0 ) ? 0 : ( iTmp & pPage->DCMask ); + iTrl = ( iTmp >> pPage->DCBits ) & pPage->TCMask; + if ( ( iTmp = iLen - iDup - iTrl ) > 0 ) + { + pPage->bufKeyPos -= iTmp; + memcpy( &pPage->bufKeyVal[ iDup ], + &pPage->node.extNode.keyPool[ pPage->bufKeyPos ], iTmp ); + } +#ifdef HB_CDX_DBGCODE + else if ( iTmp < 0 ) + { + printf("\r\npPage->Page=%lx, iLen=%d, iDup=%d, iTrl=%d", pPage->Page, iLen, iDup, iTrl); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageGetKeyVal: index corrupted." ); + } +#endif + if ( iTrl > 0 && ( iTmp = pPage->bufKeyLen - iLen + iTrl ) > 0 ) + memset( &pPage->bufKeyVal[ iLen - iTrl ], bTrail, iTmp ); + pPage->bufKeyLen = iLen - iTrl; + pPage->bufKeyNum++; + } + return pPage->bufKeyVal; + } + else + return &pPage->node.intNode.keyPool[ iKey * ( pPage->TagParent->uiLen + 8 ) ]; +} + +/* + * get record number from index page + */ +static ULONG hb_cdxPageGetKeyRec( LPCDXPAGE pPage, SHORT iKey ) +{ +#ifdef HB_CDX_DBGCODE + if ( iKey < 0 || iKey >= pPage->iKeys ) + hb_cdxErrInternal( "hb_cdxPageGetKeyRec: wrong iKey index." ); +#endif + if ( pPage->pKeyBuf ) + return HB_GET_LE_UINT32( &pPage->pKeyBuf[ ( iKey + 1 ) * ( pPage->TagParent->uiLen + 6 ) - 6 ] ); + else if ( pPage->PageType & CDX_NODE_LEAF ) + return HB_GET_LE_UINT32( &pPage->node.extNode.keyPool[ iKey * pPage->ReqByte ] ) & pPage->RNMask; + else + return HB_GET_BE_UINT32( &pPage->node.intNode.keyPool[ + ( iKey + 1 ) * ( pPage->TagParent->uiLen + 8 ) - 8 ] ); +} + +/* + * get child page number from interrior index page + */ +static ULONG hb_cdxPageGetKeyPage( LPCDXPAGE pPage, SHORT iKey ) +{ +#ifdef HB_CDX_DBGCODE + if ( iKey < 0 || iKey >= pPage->iKeys ) + hb_cdxErrInternal( "hb_cdxPageGetKeyPage: wrong iKey index." ); + if ( pPage->PageType & CDX_NODE_LEAF ) + hb_cdxErrInternal( "hb_cdxPageGetKeyPage: page is a leaf." ); +#endif + return HB_GET_BE_UINT32( &pPage->node.intNode.keyPool[ + ( iKey + 1 ) * ( pPage->TagParent->uiLen + 8 ) - 4 ] ); +} + +#if 0 +/* + * get key from uncompressed page + */ +static LPCDXKEY hb_cdxPageGetKey( LPCDXPAGE pPage, SHORT iKey, LPCDXKEY pKey ) +{ + return hb_cdxKeyPut( pKey, + hb_cdxPageGetKeyVal( pPage, iKey ), + pPage->TagParent->uiLen, + hb_cdxPageGetKeyRec( pPage, iKey ) ); +} +#endif + +#ifdef HB_CDX_DBGCODE_EXT +/* + * check if keys are sorted in proper order + */ +static void hb_cdxPageCheckKeys( LPCDXPAGE pPage ) +{ + SHORT i, K, iLen = pPage->TagParent->uiLen; + ULONG ulRec, ulRecPrev; + BYTE * pbVal, pbValPrev[CDX_MAXKEY]; + + if ( pPage->iKeys > 1 ) + { + pPage->bufKeyNum = 0; + pbVal = hb_cdxPageGetKeyVal( pPage, 0 ); + ulRec = hb_cdxPageGetKeyRec( pPage, 0 ); + for ( i = 1; i < pPage->iKeys; i++ ) + { + memcpy( pbValPrev, pbVal, iLen ); + ulRecPrev = ulRec; + pbVal = hb_cdxPageGetKeyVal( pPage, i ); + ulRec = hb_cdxPageGetKeyRec( pPage, i ); + K = hb_cdxValCompare( pPage->TagParent, + pbValPrev, iLen, + pbVal, iLen, TRUE ); + if ( K > 0 || ( K == 0 && ulRecPrev >= ulRec ) ) + { + printf( "\r\nikey=%d, pPage->iKeys=%d, K=%d, ulRecPrev=%ld, ulRec=%ld", + i, pPage->iKeys, K, ulRecPrev, ulRec );fflush(stdout); + printf( "\r\npbValPrev=[%s] pbVal=[%s], [%d], pPage->pKeyBuf=%p, pPage->iCurKey=%d", + pbValPrev, pbVal, memcmp( pbValPrev, pbVal, iLen ), + pPage->pKeyBuf, pPage->iCurKey );fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageCheckKeys: index corrupted." ); + } + } + } +} + +/* + * Check decoded leaf page if all trailing and duplicate characters are set + */ +static void hb_cdxPageCheckDupTrl( LPCDXPAGE pPage, BYTE * pKeyBuf, SHORT iKeys, BOOL fSpc ) +{ + SHORT iNum = pPage->TagParent->uiLen, iKey, iPos, iFree = CDX_EXT_FREESPACE; + SHORT iLen = iNum + 6; + BYTE bDup, bTrl; + BYTE bTrail = pPage->TagParent->bTrail; + BOOL bErr = FALSE; + + for ( iKey = 0; iKey < iKeys; iKey++ ) + { + iPos = iKey * iLen; + bTrl = bDup = 0; + while ( bTrl < iNum && pKeyBuf[ iPos + iNum - bTrl - 1 ] == bTrail ) + ++bTrl; + if ( iKey > 0 ) + { +#ifdef HB_CDX_PACKTRAIL + SHORT iMax = iNum - bTrl; +#else + SHORT iMax = iNum - HB_MAX( pKeyBuf[ iPos - 1 ], bTrl ); +#endif + while ( bDup < iMax && pKeyBuf[ iPos + bDup ] == + pKeyBuf[ iPos - iLen + bDup ] ) + ++bDup; + } + if ( bTrl != pKeyBuf[ iPos + iNum + 5 ] ) + { + printf("\r\nbTrl=%d, keybuf->bTrl=%d, iKey=%d/%d\r\n", bTrl, pKeyBuf[ iPos + iNum + 5 ], iKey, iKeys ); + fflush(stdout); + bErr = TRUE; + } + if ( bDup != ( iKey == 0 ? 0 : pKeyBuf[ iPos + iNum + 4 ] ) ) + { + printf("\r\nbDup=%d, keybuf->bDup=%d (bTrl=%d), iKey=%d/%d\r\n", bDup, pKeyBuf[ iPos + iNum + 4 ], bTrl, iKey, iKeys ); + fflush(stdout); + bErr = TRUE; + } + if ( iKey > 0 ) + { + SHORT K; + K = hb_cdxValCompare( pPage->TagParent, + &pKeyBuf[ iPos - iLen ], iNum, + &pKeyBuf[ iPos ], iNum, TRUE ); + if ( K > 0 || ( K == 0 && + HB_GET_LE_UINT32( &pKeyBuf[ iPos + iNum - iLen ] ) >= + HB_GET_LE_UINT32( &pKeyBuf[ iPos + iNum ] ) ) ) + { + printf( "\r\nikey=%d, iKeys=%d, K=%d, ulRecPrev=%ld, ulRec=%ld", + iKey, iKeys, K, + (ULONG) HB_GET_LE_UINT32( &pKeyBuf[ iPos + iNum - iLen ] ), + (ULONG) HB_GET_LE_UINT32( &pKeyBuf[ iPos + iNum ] ) ); + printf( "\r\npbValPrev=[%s] pbVal=[%s], [%d], pKeyBuf=%p", + &pKeyBuf[ iPos - iLen ], &pKeyBuf[ iPos ], + memcmp( &pKeyBuf[ iPos - iLen ], &pKeyBuf[ iPos ], iNum ), + pKeyBuf ); + fflush(stdout); + bErr = TRUE; + } + } + iFree -= iNum + pPage->ReqByte - bDup - bTrl; + } + if ( fSpc && ( iFree != pPage->iFree /* || iFree < 0 */ ) ) + { + printf( "\r\nFreeSpace calculated wrong! iFree=%d, pPage->iFree=%d", + iFree, pPage->iFree ); + fflush(stdout); + bErr = TRUE; + } + if ( bErr ) + { + printf("\r\nPage=%lx, Page->iFree=%d, iLen=%d\r\n", pPage->Page, pPage->iFree, iNum ); + fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageCheckDupTrl: index corrupted." ); + } +} + +static void hb_cdxPageLeafDecode( LPCDXPAGE pPage, BYTE * pKeyBuf ); +static void hb_cdxPageCheckDupTrlRaw( LPCDXPAGE pPage ) +{ + BYTE *pKeyBuf = (BYTE *) hb_xgrab( pPage->iKeys * ( pPage->TagParent->uiLen + 6 ) ); + + hb_cdxPageLeafDecode( pPage, pKeyBuf ); + hb_cdxPageCheckDupTrl( pPage, pKeyBuf, pPage->iKeys, TRUE ); + hb_xfree( pKeyBuf ); +} +#endif + +/* + * put record and duplicate + trailing counters into leaf page + */ +static void hb_cdxSetLeafRecord( BYTE *pDst, ULONG ulRec, int iDup, int iTrl, + int iReq, int iDCbits, int iTCbits ) +{ + int i; + USHORT usBit; + + usBit = ( ( iTrl << iDCbits ) | iDup ) << ( 16 - iTCbits - iDCbits ); + for ( i = 0; i < iReq; i++, ulRec >>= 8 ) + { + if ( i < iReq - 2 ) + pDst[ i ] = ( BYTE ) ( ulRec & 0xff ); + else if ( i == iReq - 2 ) + pDst[ i ] = ( BYTE ) ( ulRec & 0xff ) | ( usBit & 0xff ); + else + pDst[ i ] = ( BYTE ) ( ulRec & 0xff ) | ( ( usBit >> 8 ) & 0xff ); + } +} + +/* + * encode keys in buffer into cdx leaf node + */ +static void hb_cdxPageLeafEncode( LPCDXPAGE pPage, BYTE * pKeyBuf, SHORT iKeys ) +{ + int iKey, iTrl, iDup, iReq, iTmp, iNum, iLen; + BYTE *pKeyPos, *pRecPos, *pSrc; + +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 ) + { + printf("\r\npPage->Page=%lx. left=%lx, right=%lx", + pPage->Page, pPage->Left, pPage->Right); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageLeafEncode: page is not a leaf." ); + } +#endif +#ifdef HB_CDX_DBGCODE_EXT + if ( ! pKeyBuf ) + hb_cdxErrInternal( "hb_cdxPageLeafEncode: page has no buffer." ); + hb_cdxPageCheckDupTrl( pPage, pKeyBuf, iKeys, TRUE ); +#endif + iNum = pPage->TagParent->uiLen; + iLen = iNum + 6; + iReq = pPage->ReqByte; + pKeyPos = &pPage->node.extNode.keyPool[ CDX_EXT_FREESPACE ]; + pRecPos = &pPage->node.extNode.keyPool[ 0 ]; + pSrc = &pKeyBuf[ 0 ]; + for ( iKey = 0; iKey < iKeys; iKey++, pSrc += iLen, pRecPos += iReq ) + { + iDup = pSrc[ iNum + 4 ]; + iTrl = pSrc[ iNum + 5 ]; + iTmp = iNum - iTrl - iDup; + hb_cdxSetLeafRecord( pRecPos, HB_GET_LE_UINT32( &pSrc[ iNum ] ), iDup, iTrl, + iReq, pPage->DCBits, pPage->TCBits ); + if ( iTmp > 0 ) + { + pKeyPos -= iTmp; + memcpy( pKeyPos, &pSrc[ iDup ], iTmp ); + } +#ifdef HB_CDX_DBGCODE + else if ( iTmp < 0 ) + { + printf("\r\n[%s][%s]", pSrc - iLen, pSrc); + printf("\r\npPage->Page=0x%lx, iKey=%d, iNum=%d, iDup=%d, iTrl=%d", pPage->Page, iKey, iNum, iDup, iTrl); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageLeafEncode: index corrupted." ); + } +#endif + } + if ( pRecPos < pKeyPos ) + memset( pRecPos, 0, pKeyPos - pRecPos ); +#ifdef HB_CDX_DBGCODE + if ( pKeyPos - pRecPos != pPage->iFree ) + { + printf("\r\nPage=0x%lx, calc=%d, iFree=%d, req=%d, keys=%d, keyLen=%d\r\n", + pPage->Page, (int) (pKeyPos - pRecPos), pPage->iFree, pPage->ReqByte, iKeys, iNum ); + fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageLeafEncode: FreeSpace calculated wrong!." ); + } + if ( pPage->iFree < 0 ) + hb_cdxErrInternal( "hb_cdxPageLeafEncode: FreeSpace calculated wrong!!." ); +#endif + pPage->iKeys = iKeys; + pPage->fChanged = TRUE; + pPage->bufKeyNum = 0; +#ifdef HB_CDX_DBGCODE_EXT_EXT + { + BYTE * pKeyBf = pPage->pKeyBuf; + pPage->pKeyBuf = NULL; + hb_cdxPageCheckKeys( pPage ); + pPage->pKeyBuf = pKeyBf; + } + hb_cdxPageCheckKeys( pPage ); + hb_cdxPageCheckDupTrl( pPage, pKeyBuf, pPage->iKeys, TRUE ); +#endif +} + +/* + * decode keys in page into buffer + */ +static void hb_cdxPageLeafDecode( LPCDXPAGE pPage, BYTE * pKeyBuf ) +{ + int iKey, iTmp, iBits, iDup, iTrl, iNew, iReq, iLen = pPage->TagParent->uiLen; + BYTE *pDst, *pSrc, *pRec, *pTmp, bTrail = pPage->TagParent->bTrail; + ULONG ulRec; + +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 ) + { + printf("\r\npPage->Page=%lx", pPage->Page); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageLeafDecode: page is not a leaf." ); + } +#endif + iBits = ( 16 - pPage->TCBits - pPage->DCBits ); + pDst = pKeyBuf; + pSrc = &pPage->node.extNode.keyPool[ CDX_EXT_FREESPACE ]; + pRec = pPage->node.extNode.keyPool; + iReq = pPage->ReqByte; + for ( iKey = 0; iKey < pPage->iKeys; iKey++, pRec += iReq ) + { + pTmp = &pRec[ iReq - 2 ]; + iTmp = HB_GET_LE_UINT16( pTmp ) >> iBits; + iDup = ( iKey == 0 ) ? 0 : ( iTmp & pPage->DCMask ); + iTrl = ( iTmp >> pPage->DCBits ) & pPage->TCMask; + iNew = iLen - iDup - iTrl; + if ( iDup > 0 ) + { + memcpy( pDst, pDst - iLen - 6, iDup ); + pDst += iDup; + } + if ( iNew > 0 ) + { + pSrc -= iNew; + memcpy( pDst, pSrc, iNew ); + pDst += iNew; + } +#ifdef HB_CDX_DBGCODE + else if ( iNew < 0 ) + { + printf("\r\npPage->Page=%lx, iLen=%d, iDup=%d, iTrl=%d", pPage->Page, iLen, iDup, iTrl); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageLeafDecode: index corrupted." ); + } +#endif + if ( iTrl > 0 ) + { + memset( pDst, bTrail, iTrl ); + pDst += iTrl; + } + ulRec = HB_GET_LE_UINT32( pRec ) & pPage->RNMask; + HB_PUT_LE_UINT32( pDst, ulRec ); + pDst += 4; + *(pDst++) = ( BYTE ) iDup; + *(pDst++) = ( BYTE ) iTrl; + } +#ifdef HB_CDX_DBGCODE_EXT + { + BOOL fChg = pPage->fChanged; + hb_cdxPageLeafEncode( pPage, pKeyBuf, pPage->iKeys ); + pPage->fChanged = fChg; + } +#endif +} + +/* + * init space leaf page + */ +static void hb_cdxPageLeafInitSpace( LPCDXPAGE pPage ) +{ + SHORT iLen = pPage->TagParent->uiLen; + BYTE bBits; + + for ( bBits = 0; iLen; bBits++, iLen >>= 1 ) {} + pPage->ReqByte = 3; + pPage->RNBits = 24 - ( bBits << 1 ); + pPage->DCBits = pPage->TCBits = bBits; + pPage->DCMask = pPage->TCMask = (BYTE) HB_CDXBITMASK( bBits ); + pPage->RNMask = HB_CDXBITMASK( pPage->RNBits ); + pPage->iFree = CDX_EXT_FREESPACE; +} + +/* + * calculate the size of keys stored in buffer, return + * the number of keys wich can be stored in the page + */ +static void hb_cdxPageCalcLeafSpace( LPCDXPAGE pPage, BYTE * pKeyBuf, SHORT iKeys ) +{ + SHORT iNum = pPage->TagParent->uiLen, iKey, iSize; + SHORT iLen = iNum + 6; + BYTE bDup, bTrl, ReqByte, *bPtr; + ULONG ulRec, RNMask; + + hb_cdxPageLeafInitSpace( pPage ); + pPage->iKeys = 0; + RNMask = pPage->RNMask; + ReqByte = pPage->ReqByte; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckDupTrl( pPage, pKeyBuf, iKeys, FALSE ); +#endif + for ( iKey = 0; iKey < iKeys; iKey++ ) + { + bPtr = &pKeyBuf[ iKey * iLen + iNum ]; + bTrl = bPtr[ 5 ]; + if ( iKey == 0 ) + bDup = bPtr[ 4 ] = 0; + else + bDup = bPtr[ 4 ]; + ulRec = HB_GET_LE_UINT32( bPtr ); + iSize = ReqByte + iNum - bTrl - bDup; + if ( ulRec > RNMask ) + { + BYTE RNBits = pPage->RNBits; + while ( ulRec > RNMask ) + { + ReqByte++; + RNBits += 8; + RNMask = ( RNMask << 8 ) | 0xFF; + iSize += ( iKey + 1 ); + } + if ( iSize > pPage->iFree ) + break; +#ifdef HB_CDX_DSPDBG_INFO_X + printf("\r\npPage->Page=%lx, ulRec=%lx, RNMask=%lx/%lx, RNBits=%d/%d, DCB=%d, TCB=%d (%lx), iKey=%d/%d", + pPage->Page, ulRec, RNMask, pPage->RNMask, RNBits, pPage->RNBits, + pPage->DCBits, pPage->TCBits, HB_CDXBITMASK( RNBits ), iKey, iKeys); + fflush(stdout); +#endif + pPage->RNMask = RNMask; + pPage->RNBits = RNBits; + pPage->ReqByte = ReqByte; + } + else if ( iSize > pPage->iFree ) + break; + pPage->iFree -= iSize; + pPage->iKeys++; + } +} + +/* + * remove key from page + */ +static int hb_cdxPageLeafDelKey( LPCDXPAGE pPage ) +{ + SHORT iKey = pPage->iCurKey, iLen = pPage->TagParent->uiLen + 6, iSpc; + int iRet = 0; + +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 ) + hb_cdxErrInternal( "hb_cdxPageLeafDelKey: page is not a leaf." ); + if ( iKey < 0 || iKey >= pPage->iKeys ) + hb_cdxErrInternal( "hb_cdxPageLeafDelKey: wrong iKey index." ); +#endif + if ( !pPage->pKeyBuf ) + { + BYTE *pKeyBuf = (BYTE *) hb_xgrab( ( pPage->iKeys ) * iLen ); + hb_cdxPageLeafDecode( pPage, pKeyBuf ); + pPage->pKeyBuf = pKeyBuf; + } +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\ndelkey: Page=%lx, iKey=%d/%d, rec=%ld, iFree=%d", + pPage->Page, iKey, pPage->iKeys, + (ULONG) HB_GET_LE_UINT32( &pPage->pKeyBuf[ ( iKey + 1 ) * iLen - 6 ] ), + pPage->iFree ); + fflush(stdout); +#endif + iSpc = pPage->ReqByte + pPage->TagParent->uiLen - + pPage->pKeyBuf[ ( iKey + 1 ) * iLen - 2 ] - + pPage->pKeyBuf[ ( iKey + 1 ) * iLen - 1 ]; + if ( iKey < pPage->iKeys - 1 ) + { + SHORT iPos = ( iKey + 2 ) * iLen - 2, iDup = 0; + iSpc -= pPage->pKeyBuf[ iPos ]; + if ( iKey > 0 ) + { + SHORT iPrev = ( iKey - 1 ) * iLen, iNext = ( iKey + 1 ) * iLen, + iNum = pPage->TagParent->uiLen; +#ifdef HB_CDX_PACKTRAIL + iNum -= pPage->pKeyBuf[ iNext + iLen - 1 ]; +#else + iNum -= HB_MAX( pPage->pKeyBuf[ iNext + iLen - 1 ], + pPage->pKeyBuf[ iPrev + iLen - 1 ] ); +#endif + iDup = HB_MIN( pPage->pKeyBuf[ iPos ], + pPage->pKeyBuf[ iNext - 2] ); + if ( iDup > iNum ) + { + iDup = iNum; + } + else + { + while ( iDup < iNum && pPage->pKeyBuf[ iPrev + iDup ] == + pPage->pKeyBuf[ iNext + iDup ] ) + ++iDup; + } +#ifdef HB_CDX_DSPDBG_INFO + printf("+%d=%d", iSpc+iDup, pPage->iFree+iSpc+iDup ); + if ( iSpc+iDup < 0 ) + printf( " iLen=%d, iDup=%d, iNum=%d pd=%d pt=%d cd=%d ct=%d nd=%d nt=%d", + iLen-6, iDup, iNum, + pPage->pKeyBuf[ iPrev + iLen - 2 ], + pPage->pKeyBuf[ iPrev + iLen - 1 ], + pPage->pKeyBuf[ ( iKey + 1 ) * iLen - 2 ], + pPage->pKeyBuf[ ( iKey + 1 ) * iLen - 1 ], + pPage->pKeyBuf[ iNext + iLen - 2 ], + pPage->pKeyBuf[ iNext + iLen - 1 ] ); + fflush(stdout); +#endif + } + iSpc += ( pPage->pKeyBuf[ iPos ] = ( BYTE ) iDup ); + } + pPage->iFree += iSpc; + if ( --pPage->iKeys > iKey ) + { + memmove( &pPage->pKeyBuf[ iKey * iLen ], + &pPage->pKeyBuf[ ( iKey + 1 ) * iLen ], + ( pPage->iKeys - iKey ) * iLen ); + } + pPage->fBufChanged = pPage->fChanged = TRUE; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); + hb_cdxPageCheckDupTrl( pPage, pPage->pKeyBuf, pPage->iKeys, TRUE ); +#endif + if ( iKey >= pPage->iKeys ) + iRet |= NODE_NEWLASTKEY; + if ( pPage->iKeys == 0 ) + iRet |= NODE_JOIN; + else if ( pPage->iFree < 0 ) + iRet |= NODE_SPLIT; + /* if ( pPage->iFree >= CDX_EXT_FREESPACE / 2 ) */ + if ( pPage->iFree >= pPage->ReqByte ) + iRet |= NODE_BALANCE; + return iRet; +} + +/* + * add key to page at current position + */ +static int hb_cdxPageLeafAddKey( LPCDXPAGE pPage, LPCDXKEY pKey ) +{ + SHORT iKey, iNum = pPage->TagParent->uiLen; + SHORT iLen = iNum + 6, iSpc, iTrl, iDup, iMax, iPos; + BYTE bTrail = pPage->TagParent->bTrail; + int iRet = 0; + +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\naddkey: Page=%lx, iKey=%d/%d, rec=%ld", + pPage->Page, pPage->iCurKey, pPage->iKeys, pKey->rec); + fflush(stdout); +#endif +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 ) + hb_cdxErrInternal( "hb_cdxPageLeafAddKey: page is not a leaf." ); + if ( pPage->iCurKey < 0 || pPage->iCurKey > pPage->iKeys ) + hb_cdxErrInternal( "hb_cdxPageLeafAddKey: wrong iKey index." ); +#endif + if ( !pPage->pKeyBuf ) + { + BYTE *pKeyBuf = (BYTE *) hb_xgrab( ( pPage->iKeys + 1 ) * iLen ); + hb_cdxPageLeafDecode( pPage, pKeyBuf ); + pPage->pKeyBuf = pKeyBuf; + } + else + { + pPage->pKeyBuf = (BYTE*) hb_xrealloc( pPage->pKeyBuf, ( pPage->iKeys + 1 ) * iLen ); + } + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); + hb_cdxPageCheckDupTrl( pPage, pPage->pKeyBuf, pPage->iKeys, TRUE ); +#endif + + iTrl = iDup = 0; + iKey = pPage->iCurKey; + iPos = iKey * iLen; + if ( iKey < pPage->iKeys ) + { + iDup = pPage->pKeyBuf[ iPos + iNum + 4 ]; + memmove( &pPage->pKeyBuf[ iPos + iLen ], &pPage->pKeyBuf[ iPos ], + iLen * ( pPage->iKeys - iKey ) ); + } + if ( pKey->len >= iNum ) + memcpy( &pPage->pKeyBuf[ iPos ], pKey->val, iNum ); + else + { + memcpy( &pPage->pKeyBuf[ iPos ], pKey->val, pKey->len ); + memset( &pPage->pKeyBuf[ iPos + pKey->len ], bTrail, iNum - pKey->len ); + } + HB_PUT_LE_UINT32( &pPage->pKeyBuf[ iPos + iNum ], pKey->rec ); + while ( iTrl < iNum && pPage->pKeyBuf[ iPos + iNum - iTrl - 1 ] == bTrail ) + ++iTrl; + if ( iKey > 0 ) + { +#ifdef HB_CDX_PACKTRAIL + iMax = iNum - iTrl; +#else + iMax = iNum - HB_MAX( iTrl, pPage->pKeyBuf[ iPos - 1 ] ); +#endif + if ( iDup > iMax ) + { + iDup = iMax; + } + else + { + while ( iDup < iMax && pPage->pKeyBuf[ iPos + iDup ] == + pPage->pKeyBuf[ iPos + iDup - iLen ] ) + ++iDup; + } + } + pPage->pKeyBuf[ iPos + iNum + 4 ] = (BYTE) iDup; + pPage->pKeyBuf[ iPos + iNum + 5 ] = (BYTE) iTrl; + iSpc = pPage->ReqByte + iNum - iTrl - iDup; + if ( iKey < pPage->iKeys ) + { +#ifdef HB_CDX_PACKTRAIL + iMax = iNum - pPage->pKeyBuf[ iPos + iLen + iLen - 1 ]; +#else + iMax = iNum - HB_MAX( iTrl, pPage->pKeyBuf[ iPos + iLen + iLen - 1 ] ); +#endif + iSpc += pPage->pKeyBuf[ iPos + iLen + iLen - 2 ]; + iDup = 0; + while ( iDup < iMax && pPage->pKeyBuf[ iPos + iDup ] == + pPage->pKeyBuf[ iPos + iDup + iLen ] ) + ++iDup; + iSpc -= ( pPage->pKeyBuf[ iPos + iLen + iLen - 2 ] = ( BYTE ) iDup ); + } + pPage->iKeys++; + while ( pKey->rec > pPage->RNMask ) + { + pPage->RNMask = ( pPage->RNMask << 8 ) | 0xFF; + pPage->ReqByte++; + pPage->RNBits += 8; + iSpc += pPage->iKeys; + } + pPage->iFree -= iSpc; + pPage->fBufChanged = pPage->fChanged = TRUE; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); + hb_cdxPageCheckDupTrl( pPage, pPage->pKeyBuf, pPage->iKeys, TRUE ); +#endif + if ( iKey >= pPage->iKeys - 1 ) + iRet |= NODE_NEWLASTKEY; + if ( pPage->iFree < 0 ) + iRet |= NODE_SPLIT; + if ( pPage->iFree >= pPage->ReqByte && + pPage->Left != CDX_DUMMYNODE && pPage->Right != CDX_DUMMYNODE ) + iRet |= NODE_BALANCE; + return iRet; +} + +/* + * set (insert) key in interior node record to (with) given value + */ +static void hb_cdxPageIntSetKey( LPCDXPAGE pPage, SHORT iKey, BOOL fIns, BYTE * pbVal, ULONG ulRec, ULONG ulPag ) +{ + SHORT iLen = pPage->TagParent->uiLen; + SHORT iPos = iKey * ( iLen + 8 ); + +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nintSetKey (%s): Page=%lx, iKey=%d/%d, ulPag=%lx", + fIns ? "ins" : "set", pPage->Page, iKey, pPage->iKeys, ulPag); + fflush(stdout); +#endif +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + hb_cdxErrInternal( "hb_cdxPageIntSetKey: page is a leaf!" ); + if ( iKey < 0 || iKey >= pPage->iKeys + ( fIns ? 1 : 0 ) ) + { + hb_cdxErrInternal( "hb_cdxPageIntSetKey: wrong iKey index." ); + } +#endif + if ( fIns ) + { + if ( iKey < pPage->iKeys ) + { + memmove( &pPage->node.intNode.keyPool[ iPos + iLen + 8 ], + &pPage->node.intNode.keyPool[ iPos ], + ( iLen + 8 ) * ( pPage->iKeys - iKey ) ); + } + pPage->iKeys++; + } + if ( pbVal ) + memcpy( &pPage->node.intNode.keyPool[ iPos ], pbVal, iLen ); + else if ( fIns ) + memset( &pPage->node.intNode.keyPool[ iPos ], + pPage->TagParent->bTrail, iLen ); + if ( ulRec ) + HB_PUT_BE_UINT32( &pPage->node.intNode.keyPool[ iPos + iLen ], ulRec ); + HB_PUT_BE_UINT32( &pPage->node.intNode.keyPool[ iPos + iLen + 4 ], ulPag ); + pPage->fChanged = TRUE; +} + +/* + * delete key in interior node record + */ +static void hb_cdxPageIntDelKey( LPCDXPAGE pPage, SHORT iKey ) +{ + SHORT iLen = pPage->TagParent->uiLen + 8; + +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nintDelKey: Page=%lx, iKey=%d/%d, ulPag=%lx", + pPage->Page, iKey, pPage->iKeys, + (ULONG) HB_GET_BE_UINT32( &pPage->node.intNode.keyPool[ (iKey+1) * iLen - 4 ] ) ); + fflush(stdout); +#endif +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + hb_cdxErrInternal( "hb_cdxPageIntDelKey: page is a leaf!" ); + if ( iKey < 0 || iKey >= pPage->iKeys ) + { + hb_cdxErrInternal( "hb_cdxPageIntDelKey: wrong iKey index." ); + } +#endif + pPage->iKeys--; + if ( pPage->iKeys > iKey ) + { + memmove( &pPage->node.intNode.keyPool[ iKey * iLen ], + &pPage->node.intNode.keyPool[ ( iKey + 1 ) * iLen ], ( pPage->iKeys - iKey ) * iLen ); + } + memset( &pPage->node.intNode.keyPool[ pPage->iKeys * iLen ], 0, iLen ); + pPage->fChanged = TRUE; +} + +/* + * (re)load CDX page from index file + */ +static void hb_cdxPageLoad( LPCDXPAGE pPage ) +{ + if ( pPage->pKeyBuf ) + { + hb_xfree( pPage->pKeyBuf ); + pPage->pKeyBuf = NULL; + pPage->fBufChanged = FALSE; + } + hb_cdxIndexPageRead( pPage->TagParent->pIndex, pPage->Page, (BYTE *) &pPage->node, sizeof( CDXNODE ) ); + pPage->PageType = ( BYTE ) HB_GET_LE_UINT16( pPage->node.intNode.attr ); + pPage->Left = HB_GET_LE_UINT32( pPage->node.intNode.leftPtr ); + pPage->Right = HB_GET_LE_UINT32( pPage->node.intNode.rightPtr ); + pPage->iKeys = HB_GET_LE_UINT16( pPage->node.intNode.nKeys ); + pPage->fChanged = FALSE; + + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + { + pPage->iFree = HB_GET_LE_UINT16( pPage->node.extNode.freeSpc ); + pPage->RNMask = HB_GET_LE_UINT32( pPage->node.extNode.recMask ); + /* TODO: redundant, use it directly */ + pPage->DCMask = pPage->node.extNode.dupMask; + pPage->TCMask = pPage->node.extNode.trlMask; + pPage->RNBits = pPage->node.extNode.recBits; + pPage->DCBits = pPage->node.extNode.dupBits; + pPage->TCBits = pPage->node.extNode.trlBits; + pPage->ReqByte = pPage->node.extNode.keyBytes; + pPage->bufKeyNum = 0; +#if 0 + if ( !pPage->pKeyBuf ) + { + BYTE *pKeyBuf = (BYTE *) hb_xgrab( ( pPage->iKeys + 1 ) * ( pPage->TagParent->uiLen + 6 ) ); + hb_cdxPageLeafDecode( pPage, pKeyBuf ); + pPage->pKeyBuf = pKeyBuf; + } +#endif + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif +} + +/* + * store page into index file + */ +static void hb_cdxPageStore( LPCDXPAGE pPage ) +{ +#ifdef HB_CDX_DBGCODE + if ( pPage->Page == 0 || pPage->Page == CDX_DUMMYNODE ) + hb_cdxErrInternal( "hb_cdxPageStore: Page number wrong!." ); + if ( pPage->PageType & CDX_NODE_LEAF ) + { + if ( pPage->iFree < 0 ) + hb_cdxErrInternal( "hb_cdxPageStore: FreeSpace calculated wrong!." ); + } + else if ( pPage->iKeys > pPage->TagParent->MaxKeys ) + hb_cdxErrInternal( "hb_cdxPageStore: number of keys exceed!." ); +#endif + HB_PUT_LE_UINT16( pPage->node.intNode.attr, pPage->PageType ); + HB_PUT_LE_UINT16( pPage->node.intNode.nKeys, pPage->iKeys ); + HB_PUT_LE_UINT32( pPage->node.intNode.leftPtr, pPage->Left ); + HB_PUT_LE_UINT32( pPage->node.intNode.rightPtr, pPage->Right ); + + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + { + HB_PUT_LE_UINT16( pPage->node.extNode.freeSpc, pPage->iFree ); + HB_PUT_LE_UINT32( pPage->node.extNode.recMask, pPage->RNMask ); + /* TODO: redundant, use it directly */ + pPage->node.extNode.dupMask = pPage->DCMask; + pPage->node.extNode.trlMask = pPage->TCMask; + pPage->node.extNode.recBits = pPage->RNBits; + pPage->node.extNode.dupBits = pPage->DCBits; + pPage->node.extNode.trlBits = pPage->TCBits; + pPage->node.extNode.keyBytes = pPage->ReqByte; + + if ( pPage->pKeyBuf && pPage->fBufChanged ) + { + hb_cdxPageLeafEncode( pPage, pPage->pKeyBuf, pPage->iKeys ); + pPage->fBufChanged = FALSE; + } +#ifdef HB_CDX_DBGCODE_EXT + if ( pPage->pKeyBuf ) + { + hb_xfree( pPage->pKeyBuf ); + pPage->pKeyBuf = NULL; + } +#endif + } + hb_cdxIndexPageWrite( pPage->TagParent->pIndex, pPage->Page, (BYTE *) &pPage->node, sizeof( CDXNODE ) ); +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + pPage->fChanged = FALSE; +} + +/* + * create new empty page and allocate space for it in index file if ulPage == 0 + * or load it from index file if ulPage != CDX_DUMMYNODE + */ +static LPCDXPAGE hb_cdxPageNew( LPCDXTAG pTag, LPCDXPAGE pOwnerPage, ULONG ulPage ) +{ + LPCDXPAGE pPage = NULL; + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif + if ( ulPage && ulPage != CDX_DUMMYNODE && pTag->pagePool ) + { + pPage = pTag->pagePool; + while ( pPage && pPage->Page != ulPage ) + { + pPage = pPage->pPoolNext; + } + } + if ( pPage ) + { + if ( pPage->pPoolPrev ) + { + pPage->pPoolPrev->pPoolNext = pPage->pPoolNext; + if ( pPage->pPoolNext ) + { + pPage->pPoolNext->pPoolPrev = pPage->pPoolPrev; + } + pPage->pPoolPrev = NULL; + pPage->pPoolNext = pTag->pagePool; + pPage->pPoolNext->pPoolPrev = pPage; + pTag->pagePool = pPage; + } + } + else + { + pPage = ( LPCDXPAGE ) hb_xgrab( sizeof( CDXPAGE ) ); + memset( pPage, 0, sizeof( CDXPAGE ) ); + pPage->PageType = CDX_NODE_UNUSED; + pPage->Left = pPage->Right = CDX_DUMMYNODE; + pPage->TagParent = pTag; + + if ( ulPage && ulPage != CDX_DUMMYNODE ) + { + pPage->Page = ulPage; + hb_cdxPageLoad( pPage ); + } + else if ( ! ulPage ) + { + pPage->Page = hb_cdxIndexGetAvailPage( pTag->pIndex, FALSE ); + pPage->fChanged = TRUE; + } + pPage->pPoolPrev = NULL; + pPage->pPoolNext = pTag->pagePool; + pTag->pagePool = pPage; + if ( pPage->pPoolNext ) + pPage->pPoolNext->pPoolPrev = pPage; + } + pPage->Owner = pOwnerPage; + pPage->iCurKey = -1; + pPage->bUsed = 1; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif + return pPage; +} + +/* + * free single page + */ +static void hb_cdxPageFree( LPCDXPAGE pPage, BOOL fReal ) +{ +#ifdef HB_CDX_DBGCODE_EXT + LPCDXTAG pTag = pPage->TagParent; + hb_cdxTagPoolCheck( pTag ); +#endif + if ( pPage->Child != NULL ) + { + hb_cdxPageFree( pPage->Child, fReal ); + pPage->Child = NULL; + } + + if ( pPage->PageType == CDX_NODE_UNUSED ) + { + fReal = TRUE; + pPage->fChanged = FALSE; + } + + if ( fReal ) + { + if ( pPage->fChanged ) + hb_cdxPageStore( pPage ); + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif + if ( pPage->pPoolPrev ) + { + pPage->pPoolPrev->pPoolNext = pPage->pPoolNext; + if ( pPage->pPoolNext ) + { + pPage->pPoolNext->pPoolPrev = pPage->pPoolPrev; + } + } + else + { + pPage->TagParent->pagePool = pPage->pPoolNext; + if ( pPage->pPoolNext ) + { + pPage->pPoolNext->pPoolPrev = NULL; + } + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif + } + + if ( pPage->Owner != NULL && pPage->Owner->Child == pPage ) + pPage->Owner->Child = NULL; + pPage->Owner = NULL; + pPage->bUsed = 0; + + if ( fReal ) + { + if ( pPage->PageType == CDX_NODE_UNUSED ) + hb_cdxIndexPutAvailPage( pPage->TagParent->pIndex, pPage->Page, FALSE ); + if ( pPage->pKeyBuf ) + hb_xfree( pPage->pKeyBuf ); + hb_xfree( pPage ); + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif +} + +/* + * read child page + */ +static void hb_cdxPageGetChild( LPCDXPAGE pPage ) +{ + ULONG ulPage; + +#ifdef HB_CDX_DBGCODE + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + hb_cdxErrInternal( "hb_cdxPageGetChild: index corrupted." ); +#endif + + ulPage = hb_cdxPageGetKeyPage( pPage, pPage->iCurKey ); + if ( pPage->Child != NULL ) + { + if ( pPage->Child->Page != ulPage ) + { + hb_cdxPageFree( pPage->Child, FALSE ); + pPage->Child = NULL; + } + } +#ifdef HB_CDX_DSPDBG_INFO + printf("GetChild: Parent=%lx, Child=%lx\r\n", pPage->Page, ulPage); fflush(stdout); +#endif + if ( pPage->Child == NULL ) + pPage->Child = hb_cdxPageNew( pPage->TagParent, pPage, ulPage ); +} + +static int hb_cdxPageKeyLeafBalance( LPCDXPAGE pPage, int iChildRet ) +{ + LPCDXPAGE childs[ CDX_BALANCE_LEAFPAGES + 2 ], lpTmpPage; + SHORT iChKeys[ CDX_BALANCE_LEAFPAGES + 2 ], + iChFree[ CDX_BALANCE_LEAFPAGES + 2 ]; + SHORT iFirstKey, iBlncKeys = CDX_BALANCE_LEAFPAGES; + SHORT iLen = pPage->TagParent->uiLen + 6, + iKeys = 0, iFree = 0, iSkip = 0, iBufSize = 0; + BYTE * pKeyPool = NULL, * pPtr; + BOOL fIns; + ULONG ulPage; + int iRet = 0, i; + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + + if ( pPage->iCurKey > 0 ) + iFirstKey = pPage->iCurKey - 1; + else + { + iFirstKey = 0; + --iBlncKeys; + if ( pPage->Left != CDX_DUMMYNODE ) + iRet |= NODE_BALANCE; + } + if ( iBlncKeys > pPage->iKeys - iFirstKey ) + { + iBlncKeys = pPage->iKeys - iFirstKey; + if ( pPage->Right != CDX_DUMMYNODE ) + iRet |= NODE_BALANCE; + } + +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nleaf balance: Page=%lx (%d/%d)", pPage->Page, iFirstKey, iBlncKeys); + fflush(stdout); +#endif + + if ( ( iChildRet & ( NODE_SPLIT | NODE_JOIN ) ) == 0 && + ( iBlncKeys < 2 || ( iChildRet & NODE_BALANCE ) == 0 ) ) + return iRet; + + for ( i = 0; i < iBlncKeys; i++ ) + { + ulPage = hb_cdxPageGetKeyPage( pPage, iFirstKey + i ); + if ( pPage->Child && pPage->Child->Page == ulPage ) + { + childs[i] = pPage->Child; + pPage->Child = NULL; + } + else + { + childs[i] = hb_cdxPageNew( pPage->TagParent, pPage, ulPage ); + } +#ifdef HB_CDX_DBGCODE + if ( i > 0 && ( childs[i]->Page != childs[i-1]->Right || + childs[i]->Left != childs[i-1]->Page ) ) + { + printf("\r\nchilds[%d]->Page=%lx, childs[%d]->Right=%lx, childs[%d]->Page=%lx, childs[%d]->Left=%lx", + i-1, childs[i-1]->Page, i-1, childs[i-1]->Right, + i, childs[i]->Page, i, childs[i]->Left); + fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageKeyLeafBalance: index corrupted." ); + } +#endif + iChKeys[i] = childs[i]->iKeys; + iChFree[i] = childs[i]->iFree; + if ( childs[i]->iFree >= childs[i]->ReqByte ) /* TODO: increase limit for last page */ + iFree += childs[i]->iFree; + else if ( childs[i]->iFree >= 0 ) + { + if ( i == iSkip ) + ++iSkip; +#if 1 + else if ( i + 1 == iBlncKeys && ( iChildRet & NODE_SPLIT ) == 0 ) + { + iBlncKeys--; + hb_cdxPageFree( childs[i], FALSE ); + } +#endif + } + if ( i >= iSkip && i < iBlncKeys ) + iKeys += childs[i]->iKeys; + +#ifdef HB_CDX_DSPDBG_INFO + printf(", childs[%d]->Page=%lx(%d/%d)", i, childs[i]->Page, childs[i]->iKeys, childs[i]->iFree); + printf("(%d/%d/%d:%d,%lx)", i, iSkip, iBlncKeys, iKeys, childs[i]->Right); + fflush(stdout); +#endif + } + if ( ( iChildRet & NODE_SPLIT ) == 0 ) + { + for ( i = iBlncKeys - 1; i > iSkip && childs[i]->iFree >= 0 && childs[i]->iFree < childs[i]->ReqByte; i-- ) + { + iKeys -= childs[i]->iKeys; + hb_cdxPageFree( childs[i], FALSE ); + iBlncKeys--; + } + } + if ( ( iChildRet & ( NODE_SPLIT | NODE_JOIN ) ) == 0 && + ( iBlncKeys < 2 || iFree < CDX_EXT_FREESPACE ) ) + { + for ( i = 0; i < iBlncKeys; i++ ) + hb_cdxPageFree( childs[i], FALSE ); + return iRet; + } +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nleaf balance: Page=%lx iKeys=%d", pPage->Page, iKeys); + fflush(stdout); +#endif + if ( iKeys > 0 ) + { + iBufSize = iKeys; + pPtr = pKeyPool = (BYTE *) hb_xgrab( iBufSize * iLen ); + for ( i = iSkip; i < iBlncKeys && iKeys > 0; i++ ) + { + if ( childs[i]->iKeys > 0 ) + { + if ( childs[i]->pKeyBuf ) + memcpy( pPtr, childs[i]->pKeyBuf, childs[i]->iKeys * iLen ); + else + hb_cdxPageLeafDecode( childs[i], pPtr ); + /* update number of duplicate characters when join pages */ + if ( pPtr > pKeyPool ) + { + BYTE bDup = 0, bMax; +#ifdef HB_CDX_PACKTRAIL + bMax = iLen - 6 - pPtr[ iLen - 1 ]; +#else + bMax = iLen - 6 - HB_MAX( pPtr[ iLen - 1 ], pPtr[ -1 ] ); +#endif + while ( bDup < bMax && pPtr[ bDup ] == pPtr[ bDup - iLen ] ) + ++bDup; + pPtr[ iLen - 2 ] = bDup; + if ( iSkip == i - 1 && childs[iSkip]->iFree >= 0 && + iLen - 6 - bDup - pPtr[ iLen - 1 ] > + childs[iSkip]->iFree - childs[iSkip]->ReqByte ) + { + memmove( pKeyPool, pPtr, childs[i]->iKeys * iLen ); + pPtr = pKeyPool; + iKeys -= childs[i-1]->iKeys; + iSkip++; +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\niSkip=%d, iBlncKeys=%d", iSkip, iBlncKeys); + fflush(stdout); +#endif + } + } + pPtr += childs[i]->iKeys * iLen; +#ifdef HB_CDX_DSPDBG_INFO + printf(", childs[%d]->iKeys=%d", i, childs[i]->iKeys); + fflush(stdout); +#endif + } + } + } + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckDupTrl( pPage, pKeyPool, iKeys, FALSE ); +#endif + pPtr = pKeyPool; + fIns = FALSE; + i = iSkip; + while ( iKeys > 0 ) + { + if ( i == iBlncKeys ) + { + if ( childs[i-1]->Right != CDX_DUMMYNODE ) + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, childs[i-1]->Right ); + else + lpTmpPage = NULL; + +#if 1 + if ( !fIns && lpTmpPage != NULL ) + { + SHORT j, iSize = 0, iMaxReq; + ULONG ulMaxRec = 0, ul; + BYTE * pbKey, bMax; + + for ( j = 0; j < iKeys; j++ ) + { + if ( ulMaxRec < ( ul = HB_GET_LE_UINT32( &pPtr[ ( j + 1 ) * iLen - 6 ] ) ) ) + ulMaxRec = ul; + iSize += iLen - 6 - ( j == 0 ? 0 : pPtr[ ( j + 1 ) * iLen - 2 ] ) - pPtr[ ( j + 1 ) * iLen - 1 ]; + } + pbKey = hb_cdxPageGetKeyVal( lpTmpPage, 0 ); + bMax = ( HB_GET_LE_UINT16( &lpTmpPage->node.extNode.keyPool[ lpTmpPage->ReqByte - 2 ] ) + >> ( 16 - lpTmpPage->TCBits ) ) & lpTmpPage->TCMask; +#ifdef HB_CDX_PACKTRAIL + bMax = iLen - 6 - bMax; +#else + bMax = iLen - 6 - HB_MAX( pPtr[ iKeys * iLen - 1 ], bMax ); +#endif + for ( j = 0; j < bMax && + pPtr[ ( iKeys - 1 ) * iLen + j ] == pbKey[ j ]; j++ ) {} +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nbDup=%d, bTrl=%d ", j, iLen - 6 - bMax ); fflush(stdout); +#endif + iSize -= j; + iMaxReq = lpTmpPage->ReqByte; + ul = lpTmpPage->RNMask; + while ( ulMaxRec > ul ) + { + ++iMaxReq; + ul = ( ul << 8 ) | 0xFF; + } + iSize += iKeys * iMaxReq; + iSize = lpTmpPage->iFree - iSize - + ( iMaxReq - lpTmpPage->ReqByte ) * lpTmpPage->iKeys; + if ( iSize < 0 ) + fIns = TRUE; + else + { +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\ninserting bDup=%d #keys=%d/%d (%d) parent=%lx, child=%lx (%d), rec=%ld", + j, iKeys, lpTmpPage->iKeys, i, pPage->Page, lpTmpPage->Page, iSize, (ULONG) HB_GET_LE_UINT32( pPtr + iLen - 6 )); + fflush(stdout); +#endif + if ( iBufSize >= iKeys + lpTmpPage->iKeys ) + { + memmove( pKeyPool, pPtr, iKeys * iLen ); + } + else + { + BYTE * pTmp; + iBufSize = iKeys + lpTmpPage->iKeys; + pTmp = (BYTE *) hb_xgrab( iBufSize * iLen ); + memcpy( pTmp, pPtr, iKeys * iLen ); + hb_xfree( pKeyPool ); + pKeyPool = pTmp; + } + if ( lpTmpPage->iKeys > 0 ) + { + BYTE bDup = 0, bMax; + pPtr = &pKeyPool[ iKeys * iLen ]; + if ( lpTmpPage->pKeyBuf ) + memcpy( pPtr, lpTmpPage->pKeyBuf, lpTmpPage->iKeys * iLen ); + else + hb_cdxPageLeafDecode( lpTmpPage, pPtr ); +#ifdef HB_CDX_PACKTRAIL + bMax = iLen - 6 - pPtr[ iLen - 1 ]; +#else + bMax = iLen - 6 - HB_MAX( pPtr[ iLen - 1 ], pPtr[ -1 ] ); +#endif + while ( bDup < bMax && pPtr[ bDup ] == pPtr[ bDup - iLen ] ) + ++bDup; + pPtr[ iLen - 2 ] = bDup; + iKeys += lpTmpPage->iKeys; +#ifdef HB_CDX_DSPDBG_INFO + printf(" bDup2=%d, bTrl2=%d ", bDup, pPtr[ iLen - 1 ] ); fflush(stdout); +#endif + } + pPtr = pKeyPool; + childs[i] = lpTmpPage; + if ( iFirstKey + i >= pPage->iKeys ) + iRet |= NODE_NEWLASTKEY; +#ifdef HB_CDX_DBGCODE_EXT + childs[i]->iKeys = 0; + if ( childs[i]->pKeyBuf ) + { + hb_xfree( childs[i]->pKeyBuf ); + childs[i]->pKeyBuf = NULL; + childs[i]->fBufChanged = FALSE; + } + hb_cdxPageCalcLeafSpace( childs[i], pPtr, iKeys ); + hb_cdxPageLeafEncode( childs[i], pPtr, childs[i]->iKeys ); + iSize += ( iMaxReq - childs[i]->ReqByte ) * childs[i]->iKeys; + if ( iSize != childs[i]->iFree ) + { + printf("\r\ninserting, iSize=%d, childs[i]->iFree=%d", iSize, childs[i]->iFree); fflush(stdout); + printf("\r\niKeys=%d, iMaxReq=%d", iKeys, iMaxReq); fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageGetChild: index corrupted." ); + } +#endif + } +#endif + } + else + fIns = TRUE; + + if ( fIns ) + { + childs[ i ] = hb_cdxPageNew( pPage->TagParent, pPage, 0 ); + childs[ i ]->PageType = CDX_NODE_LEAF; + /* Update siblings links */ + childs[ i ]->Left = childs[i-1]->Page; + childs[ i ]->Right = childs[i-1]->Right; + childs[i-1]->Right = childs[ i ]->Page; + childs[i-1]->fChanged = TRUE; + if ( lpTmpPage != NULL ) + { + lpTmpPage->Left = childs[i]->Page; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } + iBlncKeys++; + iRet |= NODE_BALANCE; +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nleaf balance: new child[%d]->Page=%lx",i,childs[i]->Page); + fflush(stdout); +#endif + } + } + childs[i]->iKeys = 0; + if ( childs[i]->pKeyBuf ) + { + hb_xfree( childs[i]->pKeyBuf ); + childs[i]->pKeyBuf = NULL; + childs[i]->fBufChanged = FALSE; + } + hb_cdxPageCalcLeafSpace( childs[i], pPtr, iKeys ); + if ( i == iSkip && i < iBlncKeys && !childs[i]->fChanged && + childs[i]->iKeys == iChKeys[i] && + childs[i]->iFree == iChFree[i] ) + { +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\niskip++\r\n");fflush(stdout); +#endif + iSkip++; + } + else + { + hb_cdxPageLeafEncode( childs[i], pPtr, childs[i]->iKeys ); + } + pPtr += childs[i]->iKeys * iLen; + iKeys -= childs[i]->iKeys; + /* update parent key */ + if ( i < iBlncKeys ) + hb_cdxPageIntSetKey( pPage, iFirstKey + i, fIns, + pPtr - iLen, HB_GET_LE_UINT32( pPtr - 6 ), + childs[i]->Page ); + else + iBlncKeys++; +#ifdef HB_CDX_DSPDBG_INFO + printf(" (%d/%d)", childs[i]->iKeys,childs[i]->iFree); + fflush(stdout); +#endif +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( childs[i] ); +#endif + i++; + } + if ( i < iBlncKeys ) + { + /* Update siblings links */ +#if 1 + if ( childs[iBlncKeys-1]->Right != CDX_DUMMYNODE && + ( i > 1 || ( i == 1 && childs[0]->Left == CDX_DUMMYNODE ) ) ) + { + ULONG Page; + Page = childs[iBlncKeys-1]->Page; + childs[iBlncKeys-1]->Page = childs[i-1]->Page; + childs[i-1]->Page = Page; + hb_cdxPageIntSetKey( pPage, iFirstKey + i - 1, FALSE, NULL, 0, Page ); + childs[i-1]->Right = childs[iBlncKeys-1]->Right; + childs[i-1]->fChanged = TRUE; + if ( i > 1 ) + { + childs[i-2]->Right = Page; + childs[i-2]->fChanged = TRUE; + } + } + else +#endif + { + ULONG Left, Right; + Right = childs[iBlncKeys-1]->Right; + if ( i > 0 ) + { + Left = childs[i-1]->Page; + childs[i-1]->Right = Right; + childs[i-1]->fChanged = TRUE; + } + else + { + Left = childs[0]->Left; + if ( Left != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, Left ); + lpTmpPage->Right = Right; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } + } + if ( Right != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, Right ); + lpTmpPage->Left = Left; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } + } + /* Unlink empty pages from parent */ + while ( i < iBlncKeys ) + { + /* Delete parent key */ + iBlncKeys--; +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nleaf balance: free child[%d]->Page=%lx", iBlncKeys, childs[iBlncKeys]->Page); + fflush(stdout); +#endif + if ( childs[iBlncKeys]->pKeyBuf ) + { + hb_xfree( childs[iBlncKeys ]->pKeyBuf ); + childs[iBlncKeys]->pKeyBuf = NULL; + childs[iBlncKeys]->fBufChanged = FALSE; + } + hb_cdxPageIntDelKey( pPage, iFirstKey + iBlncKeys ); + childs[iBlncKeys]->Owner = NULL; + childs[iBlncKeys]->fChanged = FALSE; + childs[iBlncKeys]->PageType = CDX_NODE_UNUSED; + childs[iBlncKeys]->Left = CDX_DUMMYNODE; + childs[iBlncKeys]->Right = CDX_DUMMYNODE; + hb_cdxPageFree( childs[iBlncKeys], FALSE ); + } + iRet |= NODE_BALANCE; + } + for ( i = 0; i < iBlncKeys; i++ ) + hb_cdxPageFree( childs[i], FALSE ); + + if ( pKeyPool ) + hb_xfree( pKeyPool ); + pPage->fChanged = TRUE; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + if ( pPage->iKeys > pPage->TagParent->MaxKeys ) + iRet |= NODE_SPLIT; + return iRet; +} + +static int hb_cdxPageKeyIntBalance( LPCDXPAGE pPage, int iChildRet ) +{ + LPCDXPAGE childs[ CDX_BALANCE_INTPAGES + 2 ], lpTmpPage; + SHORT iFirstKey, iBlncKeys = CDX_BALANCE_INTPAGES; + SHORT iLen = pPage->TagParent->uiLen + 8, iKeys = 0, iNeedKeys, iNodeKeys, + iMin = pPage->TagParent->MaxKeys, iMax = 0, iDiv; + ULONG ulPage; + BYTE * pKeyPool = NULL, *pPtr; + BOOL fForce = ( iChildRet & ( NODE_SPLIT | NODE_JOIN ) ) != 0; + int iRet = 0, i; + + if ( !fForce && ( iChildRet & NODE_BALANCE ) == 0 ) + return iRet; + + if ( pPage->Child && pPage->Child->Child ) + hb_cdxPageFree( pPage->Child->Child, FALSE ); + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + + if ( pPage->iKeys <= iBlncKeys || pPage->iCurKey <= iBlncKeys / 2 ) + iFirstKey = 0; + else if ( pPage->iCurKey + ( iBlncKeys >> 1 ) >= pPage->iKeys ) + iFirstKey = pPage->iKeys - iBlncKeys; + else + iFirstKey = pPage->iCurKey - ( iBlncKeys >> 1 ); + if ( iBlncKeys > pPage->iKeys - iFirstKey ) + { + iBlncKeys = pPage->iKeys - iFirstKey; + iRet |= NODE_BALANCE; + } + +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nbalance: Page=%lx(%d) (%d/%d)", pPage->Page, pPage->iKeys, iFirstKey, iBlncKeys); + fflush(stdout); +#endif + + if ( !fForce && iBlncKeys < 2 ) + return iRet; + + for ( i = 0; i < iBlncKeys; i++ ) + { + ulPage = hb_cdxPageGetKeyPage( pPage, iFirstKey + i ); + if ( pPage->Child && pPage->Child->Page == ulPage ) + { + childs[i] = pPage->Child; + pPage->Child = NULL; + } + else + { + childs[i] = hb_cdxPageNew( pPage->TagParent, pPage, ulPage ); + } +#ifdef HB_CDX_DBGCODE + if ( i > 0 && ( childs[i]->Page != childs[i-1]->Right || + childs[i]->Left != childs[i-1]->Page ) ) + { + printf("\r\nchilds[%d]->Page=%lx, childs[%d]->Right=%lx, childs[%d]->Page=%lx, childs[%d]->Left=%lx", + i-1, childs[i-1]->Page, i-1, childs[i-1]->Right, + i, childs[i]->Page, i, childs[i]->Left); + fflush(stdout); + hb_cdxErrInternal( "hb_cdxPageKeyIntBalance: index corrupted." ); + } +#endif + iKeys += childs[i]->iKeys; + + if ( childs[i]->iKeys > iMax ) + iMax = childs[i]->iKeys; + if ( childs[i]->iKeys < iMin ) + iMin = childs[i]->iKeys; +#ifdef HB_CDX_DSPDBG_INFO + printf(", childs[%d]->Page=%lx(%d)", i, childs[i]->Page, childs[i]->iKeys); + fflush(stdout); +#endif + } + iNeedKeys = ( iKeys + pPage->TagParent->MaxKeys - 1 ) + / pPage->TagParent->MaxKeys; +#if 1 + if ( iNeedKeys == 1 && iBlncKeys > 1 && childs[0]->Left != CDX_DUMMYNODE && + childs[iBlncKeys-1]->Right != CDX_DUMMYNODE && + iKeys >= ( CDX_BALANCE_INTPAGES << 1 ) && + iKeys > ( ( pPage->TagParent->MaxKeys * 3 ) >> 1 ) ) + { + iNeedKeys = 2; + } +#endif +#if 1 + iDiv = HB_MAX( iMax - iMin - ( pPage->TagParent->MaxKeys >> 1 ) + 1, + iBlncKeys - iNeedKeys ); +#else + iDiv = iMax - iMin; +#endif + if ( iKeys > 0 && ( iDiv >= 2 || fForce ) ) + { +#if 1 + if ( iBlncKeys == 1 && iKeys > pPage->TagParent->MaxKeys && + childs[0]->Right != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, childs[0]->Right ); + iKeys += lpTmpPage->iKeys; + childs[iBlncKeys++] = lpTmpPage; + if ( iFirstKey + iBlncKeys > pPage->iKeys ) + iRet |= NODE_NEWLASTKEY; + iNeedKeys = ( iKeys + pPage->TagParent->MaxKeys - 1 ) + / pPage->TagParent->MaxKeys; + } + else +#endif + { + iMin = HB_MAX( iKeys / iNeedKeys, 2 ); + iMax = HB_MAX( ( iKeys + iNeedKeys - 1 ) / iNeedKeys, iMin ); + for ( i = iBlncKeys - 1; i > 1 && + childs[i]->iKeys >= iMin && childs[i]->iKeys <= iMax; i-- ) + { + iKeys -= childs[i]->iKeys; + hb_cdxPageFree( childs[i], FALSE ); + iBlncKeys--; + iMin = HB_MAX( iKeys / iNeedKeys, 2 ); + iMax = HB_MAX( ( iKeys + iNeedKeys - 1 ) / iNeedKeys, iMin ); + } + while ( iBlncKeys > 2 && childs[0]->iKeys >= iMin && childs[0]->iKeys <= iMax ) + { + iKeys -= childs[0]->iKeys; + hb_cdxPageFree( childs[0], FALSE ); + iBlncKeys--; + iFirstKey++; + for ( i = 0; i < iBlncKeys; i++ ) + { + childs[i] = childs[i+1]; + } + iMin = HB_MAX( iKeys / iNeedKeys, 2 ); + iMax = HB_MAX( ( iKeys + iNeedKeys - 1 ) / iNeedKeys, iMin ); + } + } + } + if ( !fForce && ( iBlncKeys < 2 || iDiv < 2 ) ) + { + for ( i = 0; i < iBlncKeys; i++ ) + hb_cdxPageFree( childs[i], FALSE ); + return iRet; + } + + if ( iKeys > 0 ) + { + pPtr = pKeyPool = (BYTE*) hb_xgrab( iKeys * iLen ); + for ( i = 0; i < iBlncKeys; i++ ) + { + if ( childs[i]->iKeys > 0 ) + { + memcpy( pPtr, childs[i]->node.intNode.keyPool, childs[i]->iKeys * iLen ); + pPtr += childs[i]->iKeys * iLen; + } + } + } + + if ( iNeedKeys > iBlncKeys ) + { + if ( iBlncKeys < 2 ) + i = iBlncKeys; + else + { + i = iBlncKeys - 1; + childs[iBlncKeys] = childs[i]; + } + childs[ i ] = hb_cdxPageNew( pPage->TagParent, pPage, 0 ); + childs[ i ]->PageType = CDX_NODE_BRANCH; + childs[ i ]->iKeys = 0; + childs[ i ]->fChanged = TRUE; + /* Add new parent key */ + hb_cdxPageIntSetKey( pPage, iFirstKey + i, TRUE, + NULL, 0, childs[iBlncKeys]->Page ); + /* Update siblings links */ + childs[ i ]->Left = childs[i-1]->Page; + childs[ i ]->Right = childs[i-1]->Right; + childs[i-1]->Right = childs[ i ]->Page; + if ( i < iBlncKeys ) + childs[i+1]->Left = childs[i]->Page; + else if ( childs[i]->Right != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, childs[iBlncKeys]->Right ); + lpTmpPage->Left = childs[i]->Page; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nint balance: new child[%d]->Page=%lx",iBlncKeys,childs[iBlncKeys]->Page); + fflush(stdout); +#endif + iBlncKeys++; + iRet |= NODE_BALANCE; + } + else if ( iNeedKeys < iBlncKeys ) + { + ULONG Left, Right; + + /* Update siblings links */ + if ( iNeedKeys > 1 ) + { + childs[iNeedKeys-2]->Right = childs[iBlncKeys-1]->Page; + childs[iBlncKeys-1]->Left = childs[iNeedKeys-2]->Page; + lpTmpPage = childs[iBlncKeys-1]; + childs[iBlncKeys-1] = childs[iNeedKeys-1]; + childs[iNeedKeys-1] = lpTmpPage; + } + else if ( iNeedKeys > 0 && childs[0]->Left == CDX_DUMMYNODE ) + { + lpTmpPage = childs[iBlncKeys-1]; + childs[iBlncKeys-1] = childs[0]; + childs[0] = lpTmpPage; + childs[0]->Left = CDX_DUMMYNODE; + } + else + { + Right = childs[iBlncKeys-1]->Right; + if ( iNeedKeys > 0 ) + { + Left = childs[iNeedKeys-1]->Page; + childs[iNeedKeys-1]->Right = Right; + } + else + { + Left = childs[0]->Left; + if ( Left != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, Left ); + lpTmpPage->Right = Right; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } + } + if ( Right != CDX_DUMMYNODE ) + { + lpTmpPage = hb_cdxPageNew( pPage->TagParent, pPage, Right ); + lpTmpPage->Left = Left; + lpTmpPage->fChanged = TRUE; + hb_cdxPageFree( lpTmpPage, FALSE ); + } + } + /* Unlink empty pages from parent */ + for ( i = iBlncKeys - 1; i >= iNeedKeys; i-- ) + { + /* Delete parent key */ +#ifdef HB_CDX_DSPDBG_INFO + printf("\r\nbalance: free child[%d]->Page=%lx",i,childs[i]->Page); + fflush(stdout); +#endif + hb_cdxPageIntDelKey( pPage, iFirstKey + i ); + childs[i]->Owner = NULL; + childs[i]->fChanged = FALSE; + childs[i]->PageType = CDX_NODE_UNUSED; + childs[i]->Left = CDX_DUMMYNODE; + childs[i]->Right = CDX_DUMMYNODE; + childs[i]->iKeys = 0; + hb_cdxPageFree( childs[i], FALSE ); + } + iBlncKeys = iNeedKeys; + iRet |= NODE_BALANCE; + } + + /* + * Redistribute childs internal node's keys and update parent keys + */ + if ( iKeys > 0 ) + { + pPtr = pKeyPool; + for ( i = 0; i < iBlncKeys; i++ ) + { + iNodeKeys = ( iKeys + iBlncKeys - i - 1 ) / ( iBlncKeys - i ); +#ifdef HB_CDX_DBGCODE + if ( iNodeKeys > pPage->TagParent->MaxKeys ) + hb_cdxErrInternal( "hb_cdxPageKeyIntBalance: iNodeKeys calculated wrong!." ); +#endif + /* TODO: do nothing if iNodeKeys == childs[i]->iKeys && i == iSkip */ + memcpy( childs[i]->node.intNode.keyPool, pPtr, iNodeKeys * iLen ); + childs[i]->iKeys = iNodeKeys; + childs[i]->fChanged = TRUE; + pPtr += iNodeKeys * iLen; + iKeys -= iNodeKeys; + /* update parent key */ + if ( iFirstKey + i < pPage->iKeys ) + { + hb_cdxPageIntSetKey( pPage, iFirstKey + i, FALSE, + pPtr - iLen, HB_GET_BE_UINT32( pPtr - 8 ), + childs[i]->Page ); + } +#ifdef HB_CDX_DSPDBG_INFO + printf(" (%d)", childs[i]->iKeys); +#endif +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( childs[i] ); +#endif + hb_cdxPageFree( childs[i], FALSE ); + } + hb_xfree( pKeyPool ); + } + pPage->fChanged = TRUE; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + if ( pPage->iKeys > pPage->TagParent->MaxKeys ) + iRet |= NODE_SPLIT; + return iRet; +} + +/* + * balance keys in child pages + */ +static int hb_cdxPageBalance( LPCDXPAGE pPage, int iChildRet ) +{ + int iRet = 0; + + if ( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + iRet = iChildRet; + else + { + if ( iChildRet & NODE_NEWLASTKEY ) + { + if ( pPage->Child->iKeys == 0 ) + { + iChildRet |= NODE_JOIN; + iRet |= NODE_NEWLASTKEY; + } + else + { + hb_cdxPageIntSetKey( pPage, pPage->iCurKey, FALSE, + hb_cdxPageGetKeyVal( pPage->Child, pPage->Child->iKeys-1 ), + hb_cdxPageGetKeyRec( pPage->Child, pPage->Child->iKeys-1 ), + pPage->Child->Page ); +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pPage ); +#endif + pPage->fChanged = TRUE; + if ( pPage->iCurKey >= pPage->iKeys - 1 ) + iRet |= NODE_NEWLASTKEY; + } + } + if ( ( pPage->Child->PageType & CDX_NODE_LEAF ) != 0 ) + iRet |= hb_cdxPageKeyLeafBalance( pPage, iChildRet ); + else + iRet |= hb_cdxPageKeyIntBalance( pPage, iChildRet ); + } + if ( !pPage->Owner ) + { + if ( pPage->iKeys == 0 ) + { + pPage->PageType |= CDX_NODE_LEAF; + hb_cdxPageLeafInitSpace( pPage ); + } + else if ( iRet & NODE_SPLIT ) + iRet = hb_cdxPageRootSplit( pPage ); + } + return iRet; +} + +/* + * split Root Page + */ +static int hb_cdxPageRootSplit( LPCDXPAGE pPage ) +{ + LPCDXPAGE pNewRoot; + ULONG ulPage; + + pNewRoot = hb_cdxPageNew( pPage->TagParent, NULL, 0 ); + /* + * do not change root page address if it's unnecessary + * so we don't have to update Tag header + */ + pPage->TagParent->RootPage = pNewRoot; + ulPage = pNewRoot->Page; + pNewRoot->Page = pPage->Page; + pPage->Page = ulPage; + + pPage->Owner = pNewRoot; + pPage->PageType &= ~CDX_NODE_ROOT; + pNewRoot->PageType = CDX_NODE_ROOT | CDX_NODE_BRANCH; + pNewRoot->fChanged = TRUE; + pNewRoot->Child = pPage; + pNewRoot->iCurKey = 0; + hb_cdxPageIntSetKey( pNewRoot, 0, TRUE, + hb_cdxPageGetKeyVal( pPage, pPage->iKeys-1 ), + hb_cdxPageGetKeyRec( pPage, pPage->iKeys-1 ), + pPage->Page ); +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckKeys( pNewRoot ); + hb_cdxTagPoolCheck( pPage->TagParent ); +#endif + hb_cdxPageBalance( pNewRoot, NODE_SPLIT ); + return 0; +} + +/* + * remove current Key from Tag + */ +static int hb_cdxPageKeyRemove( LPCDXPAGE pPage ) +{ + int iChildRet; + + if ( pPage->PageType & CDX_NODE_LEAF ) + iChildRet = hb_cdxPageLeafDelKey( pPage ); + else /* interior node */ + iChildRet = hb_cdxPageKeyRemove( pPage->Child ); + return hb_cdxPageBalance( pPage, iChildRet ); +} + +/* + * add Key to Tag at current position + */ +static int hb_cdxPageKeyInsert( LPCDXPAGE pPage, LPCDXKEY pKey ) +{ + int iChildRet; + + if ( pPage->PageType & CDX_NODE_LEAF ) + iChildRet = hb_cdxPageLeafAddKey( pPage, pKey ); + else /* interior node */ + iChildRet = hb_cdxPageKeyInsert( pPage->Child, pKey ); +#ifdef HB_CDX_DBGUPDT + cdxTmpStackSize++; +#endif + return hb_cdxPageBalance( pPage, iChildRet ); +} + +/* + * Store Tag header to index files + */ +static void hb_cdxTagHeaderStore( LPCDXTAG pTag ) +{ + USHORT uiKeyLen, uiForLen; + CDXTAGHEADER tagHeader; + + if ( !pTag->TagChanged ) + return; + + /* + * TODO: !!! read the following field from the index file, + * at least freePtr has to be read for pTag->TagBlock == 0 + * tagHeader.freePtr [ 4 ] offset of list of free pages or -1 + * tagHeader.reserved1[ 4 ] Version number ??? + * tagHeader.reserved2[ 486 ] + */ + + pTag->TagChanged = FALSE; + if ( pTag->UniqueKey ) + pTag->OptFlags |= CDX_TYPE_UNIQUE; + if ( pTag->Temporary ) + pTag->OptFlags |= CDX_TYPE_TEMPORARY; + if ( pTag->Custom ) + pTag->OptFlags |= CDX_TYPE_CUSTOM; + if ( pTag->pForItem != NULL ) + pTag->OptFlags |= CDX_TYPE_FORFILTER; + + memset( &tagHeader, 0, sizeof( CDXTAGHEADER ) ); + HB_PUT_LE_UINT32( tagHeader.rootPtr, pTag->RootBlock ); + HB_PUT_LE_UINT16( tagHeader.keySize, pTag->uiLen ); + tagHeader.indexOpt = pTag->OptFlags; + tagHeader.indexSig = 1; + if ( !pTag->AscendKey ) + HB_PUT_LE_UINT16( tagHeader.ascendFlg, 1 ); + + uiKeyLen = pTag->KeyExpr == NULL ? 0 : strlen( pTag->KeyExpr ); + uiForLen = pTag->ForExpr == NULL ? 0 : strlen( pTag->ForExpr ); + + HB_PUT_LE_UINT16( tagHeader.keyExpPos, 0 ); + HB_PUT_LE_UINT16( tagHeader.keyExpLen, uiKeyLen + 1 ); + HB_PUT_LE_UINT16( tagHeader.forExpPos, uiKeyLen + 1 ); + HB_PUT_LE_UINT16( tagHeader.forExpLen, uiForLen + 1 ); + if ( uiKeyLen > 0 ) + { + memcpy( tagHeader.keyExpPool, pTag->KeyExpr, uiKeyLen + 1 ); + } + if ( uiForLen > 0 ) + { + memcpy( tagHeader.keyExpPool + uiKeyLen + 1, pTag->ForExpr, uiForLen + 1 ); + } + hb_cdxIndexPageWrite( pTag->pIndex, pTag->TagBlock, (BYTE *) &tagHeader, sizeof( CDXTAGHEADER ) ); +} + +/* + * Read a tag definition from the index file + */ +static void hb_cdxTagLoad( LPCDXTAG pTag ) +{ + CDXTAGHEADER tagHeader; + USHORT uiForPos, uiForLen, uiKeyPos, uiKeyLen; + ULONG ulRecNo; + + /* read the page from a file */ + hb_cdxIndexPageRead( pTag->pIndex, pTag->TagBlock, (BYTE *) &tagHeader, sizeof( CDXTAGHEADER ) ); + + uiForPos = HB_GET_LE_UINT16( tagHeader.forExpPos ); + uiForLen = HB_GET_LE_UINT16( tagHeader.forExpLen ); + uiKeyPos = HB_GET_LE_UINT16( tagHeader.keyExpPos ); + uiKeyLen = HB_GET_LE_UINT16( tagHeader.keyExpLen ); + + pTag->RootBlock = HB_GET_LE_UINT32( tagHeader.rootPtr ); + + /* Return if: + * no root page allocated + * invalid root page offset (position inside an index file) + * invalid key value length + */ + if ( pTag->RootBlock == 0 || pTag->RootBlock % CDX_PAGELEN != 0 || + ( HB_FOFFSET ) pTag->RootBlock >= hb_fsSeekLarge( pTag->pIndex->hFile, 0, FS_END ) || + HB_GET_LE_UINT16( tagHeader.keySize ) > CDX_MAXKEY || + uiForPos + uiForLen > CDX_HEADEREXPLEN || + uiKeyPos + uiKeyLen > CDX_HEADEREXPLEN ) + { + pTag->RootBlock = 0; /* To force RT error - index corrupted */ + return; + } + pTag->uiLen = HB_GET_LE_UINT16( tagHeader.keySize ); + pTag->MaxKeys = CDX_INT_FREESPACE / ( pTag->uiLen + 8 ); + pTag->OptFlags = tagHeader.indexOpt; + pTag->UniqueKey = ( pTag->OptFlags & CDX_TYPE_UNIQUE ) != 0; + pTag->Temporary = ( pTag->OptFlags & CDX_TYPE_TEMPORARY ) != 0; + pTag->Custom = ( pTag->OptFlags & CDX_TYPE_CUSTOM ) != 0; + pTag->AscendKey = pTag->UsrAscend = ( HB_GET_LE_UINT16( tagHeader.ascendFlg ) == 0 ); + pTag->UsrUnique = FALSE; + pTag->KeyExpr = ( char * ) hb_xgrab( CDX_MAXEXP + 1 ); + hb_strncpyTrim( pTag->KeyExpr, ( const char * ) tagHeader.keyExpPool, CDX_MAXEXP ); + + if ( pTag->OptFlags & CDX_TYPE_STRUCTURE || ! *pTag->KeyExpr ) + return; + + if ( SELF_COMPILE( ( AREAP ) pTag->pIndex->pArea, ( BYTE * ) pTag->KeyExpr ) == FAILURE ) + { + pTag->RootBlock = 0; /* To force RT error - index corrupted */ + return; + } + pTag->pKeyItem = pTag->pIndex->pArea->valResult; + pTag->pIndex->pArea->valResult = NULL; + + /* go to a blank record before testing expression */ + ulRecNo = pTag->pIndex->pArea->ulRecNo; + SELF_GOTO( ( AREAP ) pTag->pIndex->pArea, 0 ); + + pTag->uiType = hb_cdxItemType( hb_vmEvalBlockOrMacro( pTag->pKeyItem ) ); + pTag->bTrail = ( pTag->uiType == 'C' ) ? ' ' : '\0'; + if ( pTag->uiType == 'C' ) + hb_cdxMakeSortTab( pTag->pIndex->pArea ); + + pTag->nField = hb_rddFieldExpIndex( ( AREAP ) pTag->pIndex->pArea, + pTag->KeyExpr ); + + /* Check if there is a FOR expression: pTag->OptFlags & CDX_TYPE_FORFILTER */ + if ( tagHeader.keyExpPool[ uiForPos ] != 0 ) + { + pTag->ForExpr = ( char * ) hb_xgrab( CDX_MAXEXP + 1 ); + hb_strncpyTrim( pTag->ForExpr, ( const char * ) tagHeader.keyExpPool + + uiForPos, CDX_MAXEXP ); + if ( SELF_COMPILE( ( AREAP ) pTag->pIndex->pArea, ( BYTE * ) pTag->ForExpr ) == FAILURE ) + pTag->RootBlock = 0; /* To force RT error - index corrupted */ + else + { + pTag->pForItem = pTag->pIndex->pArea->valResult; + pTag->pIndex->pArea->valResult = NULL; + if ( hb_cdxItemType( hb_vmEvalBlockOrMacro( pTag->pForItem ) ) != 'L' ) + pTag->RootBlock = 0; /* To force RT error - index corrupted */ + } + } + SELF_GOTO( ( AREAP ) pTag->pIndex->pArea, ulRecNo ); + + if ( pTag->uiLen > CDX_MAXKEY || pTag->uiType == 'U' || + ( pTag->uiType == 'N' && pTag->uiLen != 8 ) || + ( pTag->uiType == 'D' && pTag->uiLen != 8 ) || + ( pTag->uiType == 'L' && pTag->uiLen != 1 ) ) + { + pTag->RootBlock = 0; /* To force RT error - index corrupted */ + } +} + +/* + * release structure with a tag information from memory + */ +static void hb_cdxTagFree( LPCDXTAG pTag ) +{ + if ( pTag->RootPage != NULL ) + { + hb_cdxPageFree( pTag->RootPage, FALSE ); + pTag->RootPage = NULL; + } + hb_cdxTagPoolFlush( pTag ); + hb_cdxTagPoolFree( pTag, 0 ); + if ( pTag->TagChanged ) + hb_cdxTagHeaderStore( pTag ); + if ( pTag->szName != NULL ) + hb_xfree( pTag->szName ); + if ( pTag->KeyExpr != NULL ) + hb_xfree( pTag->KeyExpr ); + if ( pTag->pKeyItem != NULL ) + hb_vmDestroyBlockOrMacro( pTag->pKeyItem ); + if ( pTag->ForExpr != NULL ) + hb_xfree( pTag->ForExpr ); + if ( pTag->pForItem != NULL ) + hb_vmDestroyBlockOrMacro( pTag->pForItem ); + hb_cdxKeyFree( pTag->CurKey ); + if ( pTag->HotKey ) + hb_cdxKeyFree( pTag->HotKey ); + hb_cdxTagClearScope( pTag, 0); + hb_cdxTagClearScope( pTag, 1); + hb_xfree( pTag ); +} + +/* + * Creates a new structure with a tag information + * TagHdr = offset of index page where a tag header is stored + * if CDX_DUMMYNODE then allocate space ofor a new tag header + */ +static LPCDXTAG hb_cdxTagNew( LPCDXINDEX pIndex, char *szTagName, ULONG TagHdr ) +{ + LPCDXTAG pTag; + char szName[ CDX_MAXTAGNAMELEN + 1 ]; + + pTag = ( LPCDXTAG ) hb_xgrab( sizeof( CDXTAG ) ); + memset( pTag, 0, sizeof( CDXTAG ) ); + hb_strncpyUpperTrim( szName, szTagName, CDX_MAXTAGNAMELEN ); + pTag->szName = hb_strdup( szName ); + pTag->pIndex = pIndex; + pTag->AscendKey = pTag->UsrAscend = TRUE; + pTag->UsrUnique = FALSE; + pTag->uiType = 'C'; + pTag->bTrail = ' '; + pTag->CurKey = hb_cdxKeyNew(); + if ( TagHdr == CDX_DUMMYNODE ) + { + pTag->TagBlock = hb_cdxIndexGetAvailPage( pIndex, TRUE ); + pTag->TagChanged = TRUE; + pTag->OptFlags = CDX_TYPE_COMPACT | CDX_TYPE_COMPOUND; + } + else + { + pTag->TagBlock = TagHdr; + hb_cdxTagLoad( pTag ); + if ( pTag->RootBlock == 0 ) + { + /* index file is corrupted */ + hb_cdxTagFree( pTag ); + pTag = NULL; + } + } + return pTag; +} + +/* + * close Tag (free used pages into page pool) + */ +static void hb_cdxTagClose( LPCDXTAG pTag ) +{ + if ( pTag->RootPage != NULL ) + { + hb_cdxPageFree( pTag->RootPage, FALSE ); + pTag->RootPage = NULL; + } + if ( pTag->TagChanged ) + { + hb_cdxTagHeaderStore( pTag ); + } + pTag->fRePos = TRUE; +} + +/* + * (re)open Tag + */ +static void hb_cdxTagOpen( LPCDXTAG pTag ) +{ + CDXTAGHEADER tagHeader; + + if ( !pTag->RootPage ) + { + hb_cdxIndexPageRead( pTag->pIndex, pTag->TagBlock, (BYTE *) &tagHeader, sizeof( CDXTAGHEADER ) ); + pTag->RootBlock = HB_GET_LE_UINT32( tagHeader.rootPtr ); + if ( pTag->RootBlock && pTag->RootBlock != CDX_DUMMYNODE ) + pTag->RootPage = hb_cdxPageNew( pTag, NULL, pTag->RootBlock ); + if ( !pTag->RootPage ) + hb_cdxErrInternal("hb_cdxTagOpen: index corrupted"); + } +} + +/* + * free Tag pages from cache + */ +static void hb_cdxTagPoolFree( LPCDXTAG pTag, int nPagesLeft ) +{ + LPCDXPAGE pPage, pPageNext; + +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif + pPage = pTag->pagePool; + while ( nPagesLeft && pPage ) + { + pPage = pPage->pPoolNext; + nPagesLeft--; + } + while ( pPage ) + { + pPageNext = pPage->pPoolNext; + if ( ! pPage->bUsed ) + { + hb_cdxPageFree( pPage, TRUE ); + } + pPage = pPageNext; + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif +} + +/* + * write all changed pages in tag cache + */ +static void hb_cdxTagPoolFlush( LPCDXTAG pTag ) +{ + LPCDXPAGE pPage; + + pPage = pTag->pagePool; + while ( pPage ) + { + if ( pPage->fChanged ) + { + hb_cdxPageStore( pPage ); + } + pPage = pPage->pPoolNext; + } +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxTagPoolCheck( pTag ); +#endif +} + +/* + * retrive CurKey from current Tag possition + */ +static void hb_cdxSetCurKey( LPCDXPAGE pPage ) +{ + while ( pPage->Child ) + pPage = pPage->Child; + + hb_cdxKeyPut( pPage->TagParent->CurKey, + hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), + pPage->TagParent->uiLen, + hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ) ); +} + +/* + * seek given Key in the Page or in its children + */ +static int hb_cdxPageSeekKey( LPCDXPAGE pPage, LPCDXKEY pKey, ULONG ulKeyRec, BOOL fExact ) +{ + int l, r, n, k; + BOOL fLeaf = ( pPage->PageType & CDX_NODE_LEAF ) != 0; + + if ( fLeaf && !pPage->pKeyBuf && pPage->iKeys > 0 ) + { + SHORT iLen = pPage->TagParent->uiLen + 6; + BYTE *pKeyBuf = (BYTE *) hb_xgrab( pPage->iKeys * iLen ); + hb_cdxPageLeafDecode( pPage, pKeyBuf ); + pPage->pKeyBuf = pKeyBuf; + } + + k = ( ulKeyRec == CDX_MAX_REC_NUM ) ? -1 : 1; + n = -1; + l = 0; + r = pPage->iKeys - 1; + while ( l < r ) + { + n = (l + r ) >> 1; + k = hb_cdxValCompare( pPage->TagParent, pKey->val, pKey->len, + hb_cdxPageGetKeyVal( pPage, n ), + (BYTE) pPage->TagParent->uiLen, fExact ); + if ( k == 0 ) + { + if ( ulKeyRec == CDX_MAX_REC_NUM ) + k = 1; + else if ( ulKeyRec != CDX_IGNORE_REC_NUM ) + { + ULONG ulRec = hb_cdxPageGetKeyRec( pPage, n ); + if ( ulKeyRec > ulRec ) + k = 1; + else if ( ulKeyRec < ulRec ) + k = -1; + } + } + if ( k > 0 ) + l = n + 1; + else + r = n; + } + pPage->iCurKey = l; + if ( r < 0 ) + return k; + + if ( !fLeaf ) + { + hb_cdxPageGetChild( pPage ); +#ifdef HB_CDX_DBGCODE + if ( memcmp( hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), + hb_cdxPageGetKeyVal( pPage->Child, pPage->Child->iKeys-1 ), + pPage->TagParent->uiLen ) != 0 || + hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ) != + hb_cdxPageGetKeyRec( pPage->Child, pPage->Child->iKeys-1 ) ) + { + printf("\r\nkeyLen=%d", pPage->TagParent->uiLen); + printf("\r\nparent=%lx, iKey=%d, rec=%ld", pPage->Page, pPage->iCurKey, hb_cdxPageGetKeyRec( pPage, pPage->iCurKey )); + printf("\r\n child=%lx, iKey=%d, rec=%ld", pPage->Child->Page, pPage->Child->iKeys-1, hb_cdxPageGetKeyRec( pPage->Child, pPage->Child->iKeys-1 )); + printf("\r\nparent val=[%s]", hb_cdxPageGetKeyVal( pPage, pPage->iCurKey )); + printf("\r\n child val=[%s]", hb_cdxPageGetKeyVal( pPage->Child, pPage->Child->iKeys-1 )); + fflush(stdout); + hb_cdxErrInternal("hb_cdxPageSeekKey: wrong parent key."); + } +#endif + k = hb_cdxPageSeekKey( pPage->Child, pKey, ulKeyRec, fExact ); + } + else if ( l != n || ulKeyRec == CDX_MAX_REC_NUM ) + { + k = hb_cdxValCompare( pPage->TagParent, pKey->val, pKey->len, + hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), + (BYTE) pPage->TagParent->uiLen, fExact ); + if ( k == 0 && ulKeyRec != CDX_MAX_REC_NUM && + ulKeyRec != CDX_IGNORE_REC_NUM ) + { + ULONG ulRec = hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ); + if ( ulKeyRec > ulRec ) + k = 1; + else if ( ulKeyRec < ulRec ) + k = -1; + } + } + if ( ulKeyRec == CDX_MAX_REC_NUM ) + { + if ( pPage->iCurKey > 0 && k < 0 ) + { + pPage->iCurKey--; + if ( !fLeaf ) + { + hb_cdxPageGetChild( pPage ); + k = hb_cdxPageSeekKey( pPage->Child, pKey, ulKeyRec, fExact ); + } + else + k = hb_cdxValCompare( pPage->TagParent, pKey->val, pKey->len, + hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), + (BYTE) pPage->TagParent->uiLen, fExact ); + } + } + else if ( k > 0 && fLeaf ) + pPage->iCurKey++; + return k; +} + +/* + * an interface for fast check record number in record filter + */ +static BOOL hb_cdxCheckRecordScope( CDXAREAP pArea, ULONG ulRec ) +{ + LONG lRecNo = ( LONG ) ulRec; + + if ( SELF_COUNTSCOPE( ( AREAP ) pArea, NULL, &lRecNo ) == SUCCESS && lRecNo == 0 ) + { + return FALSE; + } + return TRUE; +} + +/* + * check and avaluate record filter + */ +static BOOL hb_cdxCheckRecordFilter( CDXAREAP pArea, ULONG ulRecNo ) +{ + BOOL lResult = FALSE; + + if ( pArea->dbfi.fFilter && pArea->dbfi.fOptimized ) + { + if ( BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, ulRecNo ) ) + { + if( pArea->ulRecNo != ulRecNo || pArea->lpdbPendingRel ) + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + + if( hb_set.HB_SET_DELETED ) + SUPER_DELETED( ( AREAP ) pArea, &lResult ); + + if( !lResult && pArea->dbfi.itmCobExpr ) + { + PHB_ITEM pResult = hb_vmEvalBlock( pArea->dbfi.itmCobExpr ); + lResult = HB_IS_LOGICAL( pResult ) && !hb_itemGetL( pResult ); + if ( lResult ) + { + LPCDXTAG pTag; + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, ulRecNo ); + pTag = hb_cdxGetActiveTag( pArea ); + if ( pTag && CURKEY_LOGCNT(pTag) ) + CURKEY_SETLOGCNT( pTag, pTag->logKeyCount - 1 ) + } + } + } + else + lResult = TRUE; + } + else if ( pArea->dbfi.itmCobExpr || hb_set.HB_SET_DELETED ) + { + if( pArea->ulRecNo != ulRecNo || pArea->lpdbPendingRel ) + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + + if( hb_set.HB_SET_DELETED ) + SUPER_DELETED( ( AREAP ) pArea, &lResult ); + + if( !lResult && pArea->dbfi.itmCobExpr ) + { + PHB_ITEM pResult = hb_vmEvalBlock( pArea->dbfi.itmCobExpr ); + lResult = HB_IS_LOGICAL( pResult ) && !hb_itemGetL( pResult ); + } + } + return !lResult; +} + +/* + * read Top Key from Page or its children + */ +static BOOL hb_cdxPageReadTopKey( LPCDXPAGE pPage ) +{ + while ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 && pPage->iKeys > 0 ) + { + pPage->iCurKey = 0; + hb_cdxPageGetChild( pPage ); + pPage = pPage->Child; + } + if ( pPage->iKeys == 0 ) + return FALSE; + pPage->iCurKey = 0; + + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read Bottom Key from Page or its children + */ +static BOOL hb_cdxPageReadBottomKey( LPCDXPAGE pPage ) +{ + while ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 && pPage->iKeys > 0 ) + { + pPage->iCurKey = pPage->iKeys - 1; + hb_cdxPageGetChild( pPage ); + pPage = pPage->Child; + } + if ( pPage->iKeys == 0 ) + return FALSE; + pPage->iCurKey = pPage->iKeys - 1; + + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read Previous Key from Page or its children + */ +static BOOL hb_cdxPageReadPrevKey( LPCDXPAGE pPage ) +{ + LPCDXPAGE pOwnerPage = NULL; + + while ( pPage->Child ) + { + pOwnerPage = pPage; + pPage = pPage->Child; + } + + do + { + pPage->iCurKey--; + while ( pPage->iCurKey < 0 ) + { + if ( pPage->Left == CDX_DUMMYNODE || !pOwnerPage ) + { + pPage->iCurKey = 0; + if ( pPage->iKeys > 0 ) + hb_cdxSetCurKey( pPage ); + return FALSE; + } + pOwnerPage->Child = hb_cdxPageNew( pPage->TagParent, pPage->Owner, pPage->Left ); + hb_cdxPageFree( pPage, !pPage->fChanged ); + pPage = pOwnerPage->Child; + pPage->iCurKey = pPage->iKeys - 1; + } + if( pPage->iCurKey == 0 ) + { + hb_cdxSetCurKey( pPage ); + if( !hb_cdxTopScope( pPage->TagParent ) || + !hb_cdxBottomScope( pPage->TagParent ) ) + break; + } + } + while( !hb_cdxCheckRecordScope( pPage->TagParent->pIndex->pArea, + hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ) ) ); + if( pPage->iCurKey != 0 ) + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read Next Key from Page or its children + */ +static BOOL hb_cdxPageReadNextKey( LPCDXPAGE pPage ) +{ + LPCDXPAGE pOwnerPage = NULL; + + while ( pPage->Child ) + { + pOwnerPage = pPage; + pPage = pPage->Child; + } + + do + { + pPage->iCurKey++; + while ( pPage->iCurKey >= pPage->iKeys ) + { + if ( pPage->Right == CDX_DUMMYNODE || !pOwnerPage ) + { + pPage->iCurKey = pPage->iKeys; + return FALSE; + } + pOwnerPage->Child = hb_cdxPageNew( pPage->TagParent, pPage->Owner, pPage->Right ); + hb_cdxPageFree( pPage, !pPage->fChanged ); + pPage = pOwnerPage->Child; + pPage->iCurKey = 0; + } + if( pPage->iCurKey == 0 ) + { + hb_cdxSetCurKey( pPage ); + if( !hb_cdxTopScope( pPage->TagParent ) || + !hb_cdxBottomScope( pPage->TagParent ) ) + break; + } + } + while( !hb_cdxCheckRecordScope( pPage->TagParent->pIndex->pArea, + hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ) ) ); + if( pPage->iCurKey != 0 ) + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read Previous Unique Key from Page or its children + */ +static BOOL hb_cdxPageReadPrevUniqKey( LPCDXPAGE pPage ) +{ + LPCDXPAGE pOwnerPage = NULL; + + while ( pPage->Child ) + { + pOwnerPage = pPage; + pPage = pPage->Child; + } + while ( pPage->iCurKey < 0 || memcmp( pPage->TagParent->CurKey->val, hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), pPage->TagParent->uiLen ) == 0 ) + { + if ( pPage->iCurKey > 0 ) + pPage->iCurKey--; + else + { + if ( pPage->Left == CDX_DUMMYNODE || !pOwnerPage ) + { + pPage->iCurKey = 0; + if ( pPage->iKeys > 0 ) + hb_cdxSetCurKey( pPage ); + return FALSE; + } + pOwnerPage->Child = hb_cdxPageNew( pPage->TagParent, pPage->Owner, pPage->Left ); + hb_cdxPageFree( pPage, !pPage->fChanged ); + pPage = pOwnerPage->Child; + pPage->iCurKey = pPage->iKeys - 1; + } + } + + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read Next Unique Key from Page or its children + */ +static BOOL hb_cdxPageReadNextUniqKey( LPCDXPAGE pPage ) +{ + LPCDXPAGE pOwnerPage = NULL; + /* BYTE pbVal[CDX_MAXKEY]; */ + + while ( pPage->Child ) + { + pOwnerPage = pPage; + pPage = pPage->Child; + } +/* + memcpy( pbVal, hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), pPage->TagParent->uiLen ); + pPage->iCurKey++; + while ( pPage->iCurKey >= pPage->iKeys || memcmp( pbVal, hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), pPage->TagParent->uiLen ) == 0 ) +*/ + while ( pPage->iCurKey >= pPage->iKeys || memcmp( pPage->TagParent->CurKey->val, hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ), pPage->TagParent->uiLen ) == 0 ) + { + if ( pPage->iCurKey < pPage->iKeys - 1 ) + pPage->iCurKey++; + else + { + if ( pPage->Right == CDX_DUMMYNODE || !pOwnerPage ) + { + pPage->iCurKey = pPage->iKeys - 1; + if ( pPage->iKeys > 0 ) + hb_cdxSetCurKey( pPage ); + return FALSE; + } + pOwnerPage->Child = hb_cdxPageNew( pPage->TagParent, pPage->Owner, pPage->Right ); + hb_cdxPageFree( pPage, !pPage->fChanged ); + pPage = pOwnerPage->Child; + pPage->iCurKey = 0; + } + } + hb_cdxSetCurKey( pPage ); + return TRUE; +} + +/* + * read the TOP/BOTTOM/NEXT/PREVIOUS Key from Tag + */ +static void hb_cdxTagKeyRead( LPCDXTAG pTag, BYTE bTypRead ) +{ + BOOL fAfter = FALSE, fBof, fEof; + + pTag->CurKey->rec = 0; + pTag->fRePos = FALSE; + hb_cdxTagOpen( pTag ); + + if ( pTag->UsrUnique ) + { + switch( bTypRead ) + { + case NEXT_RECORD: + bTypRead = NXTU_RECORD; + break; + + case PREV_RECORD: + bTypRead = PRVU_RECORD; + case BTTM_RECORD: + fAfter = TRUE; + break; + } + } + if ( pTag->UsrAscend ) + { + fBof = pTag->TagBOF; + fEof = pTag->TagEOF; + } + else + { + fBof = pTag->TagEOF; + fEof = pTag->TagBOF; + switch( bTypRead ) + { + case TOP_RECORD: + bTypRead = BTTM_RECORD; + break; + + case BTTM_RECORD: + bTypRead = TOP_RECORD; + break; + + case PREV_RECORD: + bTypRead = NEXT_RECORD; + break; + + case NEXT_RECORD: + bTypRead = PREV_RECORD; + break; + + case PRVU_RECORD: + bTypRead = NXTU_RECORD; + break; + + case NXTU_RECORD: + bTypRead = PRVU_RECORD; + break; + } + } + switch( bTypRead ) + { + case TOP_RECORD: + fBof = fEof = !hb_cdxPageReadTopKey( pTag->RootPage ); + break; + + case BTTM_RECORD: + fBof = fEof = !hb_cdxPageReadBottomKey( pTag->RootPage ); + break; + + case PREV_RECORD: + if ( !fBof ) + fBof = !hb_cdxPageReadPrevKey( pTag->RootPage ); + break; + + case NEXT_RECORD: + if ( !fEof ) + fEof = !hb_cdxPageReadNextKey( pTag->RootPage ); + break; + + case PRVU_RECORD: + if ( !fBof ) + fBof = !hb_cdxPageReadPrevUniqKey( pTag->RootPage ); + break; + + case NXTU_RECORD: + if ( !fEof ) + fEof = !hb_cdxPageReadNextUniqKey( pTag->RootPage ); + break; + } + + if ( fEof ) + pTag->CurKey->rec = 0; + else if ( fAfter && !fBof ) + { + if ( pTag->UsrAscend ) + { + if ( hb_cdxPageReadPrevUniqKey( pTag->RootPage ) ) + hb_cdxPageReadNextKey( pTag->RootPage ); + } + else + { + if ( hb_cdxPageReadNextUniqKey( pTag->RootPage ) ) + hb_cdxPageReadPrevKey( pTag->RootPage ); + } + } + + if ( pTag->UsrAscend ) + { + pTag->TagBOF = fBof; + pTag->TagEOF = fEof; + } + else + { + pTag->TagBOF = fEof; + pTag->TagEOF = fBof; + } +} + +/* + * find pKey in pTag return 0 or TagNO + */ +static ULONG hb_cdxTagKeyFind( LPCDXTAG pTag, LPCDXKEY pKey ) +{ + int K; + ULONG ulKeyRec = pKey->rec; + + pTag->fRePos = FALSE; + hb_cdxTagOpen( pTag ); + + pTag->TagBOF = pTag->TagEOF = FALSE; + K = hb_cdxPageSeekKey( pTag->RootPage, pKey, ulKeyRec, FALSE ); + if ( ulKeyRec == CDX_MAX_REC_NUM ) + K = - K; + + if ( K > 0 ) + { + pTag->CurKey->rec = 0; + pTag->TagEOF = TRUE; + } + else + { + hb_cdxSetCurKey( pTag->RootPage ); + if ( K == 0 ) + return pTag->CurKey->rec; + } + return 0; +} + +/* + * add the Key into the Tag + */ +static BOOL hb_cdxTagKeyAdd( LPCDXTAG pTag, LPCDXKEY pKey ) +{ + hb_cdxTagOpen( pTag ); + if( hb_cdxPageSeekKey( pTag->RootPage, pKey, + pTag->UniqueKey ? CDX_IGNORE_REC_NUM : pKey->rec, + TRUE ) != 0 || ( pTag->Custom && !pTag->UniqueKey ) ) + { + hb_cdxPageKeyInsert( pTag->RootPage, pKey ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS | + CDX_CURKEY_RAWCNT | CDX_CURKEY_LOGCNT ); + pTag->fRePos = TRUE; + /* TODO: !!! remove when page leaf balance can save CurKey */ + hb_cdxTagKeyFind( pTag, pKey ); + return TRUE; + } + return FALSE; +} + +/* + * delete the Key from the Tag + */ +static BOOL hb_cdxTagKeyDel( LPCDXTAG pTag, LPCDXKEY pKey ) +{ + if ( hb_cdxTagKeyFind( pTag, pKey ) != 0 ) + { + hb_cdxPageKeyRemove( pTag->RootPage ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS | + CDX_CURKEY_RAWCNT | CDX_CURKEY_LOGCNT ); + pTag->CurKey->rec = 0; + return TRUE; + } + return FALSE; +} + +/* + * Go to the first visible record in Tag + */ +static void hb_cdxTagGoTop( LPCDXTAG pTag ) +{ + LPCDXKEY pKey = pTag->UsrAscend ? pTag->topScopeKey : pTag->bottomScopeKey; + ULONG ulPos = 1; + + if ( pKey ) + hb_cdxTagKeyFind( pTag, pKey ); + else + hb_cdxTagKeyRead( pTag, TOP_RECORD ); + + do + { + if ( pTag->CurKey->rec == 0 || pTag->TagEOF || ! hb_cdxBottomScope( pTag ) ) + { + pTag->TagBOF = pTag->TagEOF = TRUE; + pTag->CurKey->rec = 0; + break; + } + else if ( ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) != 0 || + hb_cdxCheckRecordScope( pTag->pIndex->pArea, pTag->CurKey->rec ) ) + { + pTag->rawKeyPos = ulPos; + CURKEY_SETRAWPOS( pTag ); + break; + } + hb_cdxTagKeyRead( pTag, NEXT_RECORD ); + ulPos++; + } + while ( TRUE ); +} + +/* + * Go to the last visible record in Tag + */ +static void hb_cdxTagGoBottom( LPCDXTAG pTag ) +{ + LPCDXKEY pKey = pTag->UsrAscend ? pTag->bottomScopeKey : pTag->topScopeKey; + ULONG ulPos = 0; + + if ( pKey ) + hb_cdxTagKeyFind( pTag, pKey ); + else + hb_cdxTagKeyRead( pTag, BTTM_RECORD ); + + do + { + if ( pTag->CurKey->rec == 0 || pTag->TagBOF || ! hb_cdxTopScope( pTag ) ) + { + pTag->TagBOF = pTag->TagEOF = TRUE; + pTag->CurKey->rec = 0; + break; + } + else if ( ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) != 0 || + hb_cdxCheckRecordScope( pTag->pIndex->pArea, pTag->CurKey->rec ) ) + { + if ( CURKEY_RAWCNT( pTag ) ) + { + pTag->rawKeyPos = pTag->rawKeyCount - ulPos; + CURKEY_SETRAWPOS( pTag ); + } + break; + } + hb_cdxTagKeyRead( pTag, PREV_RECORD ); + ulPos++; + } + while ( TRUE ); +} + +/* + * skip to Next Key in the Tag + */ +static void hb_cdxTagSkipNext( LPCDXTAG pTag ) +{ + BOOL fPos = CURKEY_RAWPOS( pTag ), fEof = FALSE; + ULONG ulSkip = 1; + + if ( pTag->CurKey->rec != 0 ) + { + if ( !hb_cdxTopScope( pTag ) ) + { + ulSkip = 0; + hb_cdxTagGoTop( pTag ); + } + else + hb_cdxTagKeyRead( pTag, NEXT_RECORD ); + } + + while ( !fEof ) + { + if ( pTag->TagEOF || pTag->CurKey->rec == 0 || + !hb_cdxBottomScope( pTag ) || !hb_cdxTopScope( pTag ) ) + fEof = TRUE; + else if ( ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) != 0 || + hb_cdxCheckRecordScope( pTag->pIndex->pArea, pTag->CurKey->rec ) ) + break; + hb_cdxTagKeyRead( pTag, NEXT_RECORD ); + ulSkip++; + } + + if ( fEof ) + { + pTag->CurKey->rec = 0; + pTag->TagEOF = TRUE; + } + else if ( fPos ) + { + pTag->rawKeyPos += ulSkip; + CURKEY_SETRAWPOS( pTag ); + } +} + +/* + * skip to Previous Key in the Tag + */ +static void hb_cdxTagSkipPrev( LPCDXTAG pTag ) +{ + BOOL fPos = CURKEY_RAWPOS( pTag ), fBof = FALSE; + ULONG ulSkip = 1; + + if ( pTag->CurKey->rec == 0 ) + { + ulSkip = 0; + hb_cdxTagGoBottom( pTag ); + } + else + hb_cdxTagKeyRead( pTag, PREV_RECORD ); + + while ( !fBof ) + { + if ( pTag->TagBOF || pTag->CurKey->rec == 0 || + !hb_cdxBottomScope( pTag ) || !hb_cdxTopScope( pTag ) ) + fBof = TRUE; + else if ( ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) != 0 || + hb_cdxCheckRecordScope( pTag->pIndex->pArea, pTag->CurKey->rec ) ) + break; + hb_cdxTagKeyRead( pTag, PREV_RECORD ); + ulSkip++; + } + + + if ( fBof ) + { + hb_cdxTagGoTop( pTag ); + pTag->TagBOF = TRUE; + } + else if ( fPos ) + { + pTag->rawKeyPos -= ulSkip; + CURKEY_SETRAWPOS( pTag ); + } +} + +/* + * Reorder the Tag list by their position in index file (not names) + * to be Clipper compatible + */ +static void hb_cdxReorderTagList( LPCDXTAG * TagListPtr ) +{ + LPCDXTAG *pTagPtr, pTagTmp; + BOOL fRepeat = TRUE; + + while ( fRepeat ) + { + fRepeat = FALSE; + pTagPtr = TagListPtr; + while ( *pTagPtr && (*pTagPtr)->pNext ) + { + if ( (*pTagPtr)->TagBlock > (*pTagPtr)->pNext->TagBlock ) + { + pTagTmp = (*pTagPtr); + (*pTagPtr) = (*pTagPtr)->pNext; + pTagTmp->pNext = (*pTagPtr)->pNext; + (*pTagPtr)->pNext = pTagTmp; + fRepeat = TRUE; + } + pTagPtr = &(*pTagPtr)->pNext; + } + } +} + +/* + * create new order header, store it and then make an order + */ +static LPCDXTAG hb_cdxIndexCreateTag( BOOL fStruct, LPCDXINDEX pIndex, + char * szTagName, + char * KeyExp, PHB_ITEM pKeyItem, + BYTE bType, USHORT uiLen, + char * ForExp, PHB_ITEM pForItem, + BOOL fAscnd, BOOL fUniq, BOOL fCustom, + BOOL fReindex ) +{ + LPCDXTAG pTag; + + pTag = hb_cdxTagNew( pIndex, szTagName, CDX_DUMMYNODE ); + + if ( fStruct ) + pTag->OptFlags |= CDX_TYPE_STRUCTURE; + + if ( bType == 'C' ) + hb_cdxMakeSortTab( pTag->pIndex->pArea ); + if ( KeyExp != NULL ) + { + pTag->KeyExpr = ( char * ) hb_xgrab( CDX_MAXEXP + 1 ); + hb_strncpyTrim( pTag->KeyExpr, KeyExp, CDX_MAXEXP ); + pTag->nField = hb_rddFieldExpIndex( ( AREAP ) pTag->pIndex->pArea, + pTag->KeyExpr ); + } + pTag->pKeyItem = pKeyItem; + if ( ForExp != NULL ) + { + pTag->ForExpr = ( char * ) hb_xgrab( CDX_MAXEXP + 1 ); + hb_strncpyTrim( pTag->ForExpr, ForExp, CDX_MAXEXP ); + } + pTag->pForItem = pForItem; + pTag->AscendKey = pTag->UsrAscend = fAscnd; + pTag->UniqueKey = fUniq; + pTag->UsrUnique = FALSE; + pTag->Custom = fCustom; + pTag->uiType = bType; + pTag->bTrail = ( pTag->uiType == 'C' ) ? ' ' : '\0'; + pTag->uiLen = uiLen; + pTag->MaxKeys = CDX_INT_FREESPACE / ( uiLen + 8 ); + pTag->TagChanged = TRUE; + hb_cdxTagDoIndex( pTag, fReindex ); + + return pTag; +} + +/* + * create structural (compound) tag + */ +static void hb_cdxIndexCreateStruct( LPCDXINDEX pIndex, char * szTagName ) +{ + /* here we can change default tag name */ + pIndex->pCompound = hb_cdxIndexCreateTag( TRUE, pIndex, szTagName, + NULL, NULL, 'C', CDX_MAXTAGNAMELEN, NULL, NULL, + TRUE, FALSE, FALSE, FALSE ); +} + +/* + * free page and all child pages + */ +static void hb_cdxIndexFreePages( LPCDXPAGE pPage ) +{ + if ( ( pPage->PageType & CDX_NODE_LEAF ) == 0 ) + { + LPCDXPAGE pChildPage; + SHORT iKey; + + for ( iKey = 0; iKey < pPage->iKeys; iKey++ ) + { + pChildPage = hb_cdxPageNew( pPage->TagParent, NULL, + hb_cdxPageGetKeyPage( pPage, iKey ) ); + if ( pChildPage ) + hb_cdxIndexFreePages( pChildPage ); + } + } + pPage->PageType = CDX_NODE_UNUSED; + hb_cdxPageFree( pPage, FALSE ); +} + +/* + * remove Tag from Bag + */ +static void hb_cdxIndexDelTag( LPCDXINDEX pIndex, char * szTagName ) +{ + LPCDXTAG *pTagPtr = &pIndex->TagList; + + while ( *pTagPtr && hb_stricmp( (*pTagPtr)->szName, szTagName ) != 0 ) + pTagPtr = &(*pTagPtr)->pNext; + + if ( *pTagPtr ) + { + LPCDXTAG pTag = *pTagPtr; + LPCDXKEY pKey = hb_cdxKeyPutC( NULL, szTagName, pIndex->pCompound->uiLen, + pTag->TagBlock ); + if ( hb_cdxTagKeyDel( pIndex->pCompound, pKey ) ) + { + if ( pTag != pIndex->TagList || pTag->pNext != NULL ) + { + LPCDXPAGE pPage; + + hb_cdxTagOpen( pTag ); + pPage = pTag->RootPage; + hb_cdxTagClose( pTag ); + if ( ! pIndex->fShared ) + { + if ( pPage ) + hb_cdxIndexFreePages( pPage ); + hb_cdxIndexPutAvailPage( pIndex, pTag->TagBlock, TRUE ); + } + pTag->TagChanged = FALSE; + } + } + *pTagPtr = pTag->pNext; + hb_cdxTagFree( pTag ); + hb_cdxKeyFree( pKey ); + } +} + +/* + * add tag to order bag + */ +static LPCDXTAG hb_cdxIndexAddTag( LPCDXINDEX pIndex, char * szTagName, + char * szKeyExp, PHB_ITEM pKeyItem, + BYTE bType, USHORT uiLen, + char * szForExp, PHB_ITEM pForItem, + BOOL fAscend, BOOL fUnique, BOOL fCustom, + BOOL fReindex ) +{ + LPCDXTAG pTag, *pTagPtr; + LPCDXKEY pKey; + + /* Delete previous tag first to free the place for new one + * its redundant Tag should be already deleted + */ + hb_cdxIndexDelTag( pIndex, szTagName ); + + /* Create new tag an add to tag list */ + pTag = hb_cdxIndexCreateTag( FALSE, pIndex, szTagName, szKeyExp, pKeyItem, + bType, uiLen, szForExp, pForItem, + fAscend, fUnique, fCustom, fReindex ); + pTagPtr = &pIndex->TagList; + while ( *pTagPtr ) + pTagPtr = &(*pTagPtr)->pNext; + *pTagPtr = pTag; + pKey = hb_cdxKeyPutC( NULL, szTagName, pIndex->pCompound->uiLen, pTag->TagBlock ); + hb_cdxTagKeyAdd( pIndex->pCompound, pKey ); + hb_cdxKeyFree( pKey ); + return pTag; +} + +/* + * rebuild from scratch all orders in index file + */ +static void hb_cdxIndexReindex( LPCDXINDEX pIndex ) +{ + LPCDXTAG pCompound, pTagList, pTag; + + hb_cdxIndexLockWrite( pIndex ); + hb_cdxIndexDiscardBuffers( pIndex ); + + pCompound = pIndex->pCompound; + pTagList = pIndex->TagList; + pIndex->pCompound = NULL; + pIndex->TagList = NULL; + + pIndex->ulVersion = 0; + pIndex->nextAvail = 0; + pIndex->freePage = 0; + hb_fsSeek( pIndex->hFile, 0, FS_SET ); + hb_fsWrite( pIndex->hFile, NULL, 0 ); + pIndex->fChanged = TRUE; + + /* Rebuild the compound (master) tag */ + if ( pCompound ) + { + hb_cdxIndexCreateStruct( pIndex, pCompound->szName ); + hb_cdxTagFree( pCompound ); + } + + /* Rebuild each tag */ + while ( pTagList ) + { + pTag = pTagList; + hb_cdxIndexAddTag( pIndex, pTag->szName, pTag->KeyExpr, pTag->pKeyItem, + (BYTE) pTag->uiType, pTag->uiLen, pTag->ForExpr, pTag->pForItem, + pTag->AscendKey, pTag->UniqueKey, pTag->Custom, TRUE ); + pTagList = pTag->pNext; + pTag->pKeyItem = pTag->pForItem = NULL; + hb_cdxTagFree( pTag ); + } + hb_cdxIndexUnLockWrite( pIndex ); +} + +/* + * create new index structure + */ +static LPCDXINDEX hb_cdxIndexNew( CDXAREAP pArea ) +{ + LPCDXINDEX pIndex; + + pIndex = ( LPCDXINDEX ) hb_xgrab( sizeof( CDXINDEX ) ); + memset( pIndex, 0, sizeof( CDXINDEX ) ); + pIndex->hFile = FS_ERROR; + pIndex->pArea = pArea; + pIndex->nextAvail = CDX_DUMMYNODE; + return pIndex; +} + +/* + * free (close) all tag in index file + */ +static void hb_cdxIndexFreeTags( LPCDXINDEX pIndex ) +{ + LPCDXTAG pTag; + + /* Free Compound tag */ + if ( pIndex->pCompound != NULL ) + { + hb_cdxTagFree( pIndex->pCompound ); + pIndex->pCompound = NULL; + } + + while ( pIndex->TagList ) + { + pTag = pIndex->TagList; + pIndex->TagList = pTag->pNext; + hb_cdxTagFree( pTag ); + } +} + +/* + * free (close) index and all tags in it + */ +static void hb_cdxIndexFree( LPCDXINDEX pIndex ) +{ + /* Free List of Free Pages */ + hb_cdxIndexDropAvailPage( pIndex ); + + /* free all tags */ + hb_cdxIndexFreeTags( pIndex ); + + /* Close file */ + if( pIndex->hFile != FS_ERROR ) + { + hb_fsClose( pIndex->hFile ); + if( pIndex->fDelete ) + { + hb_fsDelete( ( BYTE * ) ( pIndex->szRealName ? + pIndex->szRealName : pIndex->szFileName ) ); + } + } + +#ifdef HB_CDX_DBGCODE + if( pIndex->fShared && ( pIndex->lockWrite || pIndex->lockRead ) && + hb_vmRequestQuery() == 0 ) + hb_errInternal( 9104, "hb_cdxIndexFree: index file still locked.", "", "" ); + + if( ( pIndex->WrLck || pIndex->RdLck ) && + hb_vmRequestQuery() == 0 ) + hb_errInternal( 9104, "hb_cdxIndexFree: index file still locked (*)", "", "" ); +#endif + + if ( pIndex->szFileName != NULL ) + hb_xfree( pIndex->szFileName ); + if( pIndex->szRealName ) + hb_xfree( pIndex->szRealName ); + + hb_xfree( pIndex ); +} + +/* + * load orders from index file + */ +static BOOL hb_cdxIndexLoad( LPCDXINDEX pIndex, char * szBaseName ) +{ + LPCDXTAG TagList, * pTagPtr; + BOOL fResult = FALSE; + + TagList = NULL; + pTagPtr = &TagList; + + hb_cdxIndexLockRead( pIndex ); + /* load the tags*/ + pIndex->pCompound = hb_cdxTagNew( pIndex, szBaseName, 0L ); + + /* check if index is not corrupted */ + if ( pIndex->pCompound ) + { + fResult = TRUE; + pIndex->pCompound->OptFlags = CDX_TYPE_COMPACT | CDX_TYPE_COMPOUND | CDX_TYPE_STRUCTURE; + hb_cdxTagGoTop( pIndex->pCompound ); + while ( !pIndex->pCompound->TagEOF ) + { + *pTagPtr = hb_cdxTagNew( pIndex, (char *) pIndex->pCompound->CurKey->val, + pIndex->pCompound->CurKey->rec ); + /* tag is corrupted - break tags loading */ + if ( *pTagPtr == NULL ) + { + fResult = FALSE; + break; + } + pTagPtr = &(*pTagPtr)->pNext; + hb_cdxTagSkipNext( pIndex->pCompound ); + } + } + + hb_cdxIndexUnLockRead( pIndex ); + hb_cdxReorderTagList( &TagList ); + pTagPtr = &pIndex->TagList; + while ( *pTagPtr != NULL ) + pTagPtr = &(*pTagPtr)->pNext; + (*pTagPtr) = TagList; + +#ifdef HB_CDX_DSPDBG_INFO + hb_cdxDspTags( pIndex ); +#endif + + return fResult; +} + +/* + * create index file name + */ +static void hb_cdxCreateFName( CDXAREAP pArea, char * szBagName, BOOL * fProd, + char * szFileName, char * szBaseName ) +{ + PHB_FNAME pFileName; + PHB_ITEM pExt = NULL; + BOOL fName = szBagName && *szBagName; + + pFileName = hb_fsFNameSplit( fName ? szBagName : pArea->szDataFileName ); + + if( szBaseName ) + { + if( pFileName->szName ) + hb_strncpyUpperTrim( szBaseName, pFileName->szName, CDX_MAXTAGNAMELEN ); + else + szBaseName[ 0 ] = '\0'; + } + + if( !pFileName->szExtension || !fName ) + { + DBORDERINFO pExtInfo; + memset( &pExtInfo, 0, sizeof( pExtInfo ) ); + pExt = pExtInfo.itmResult = hb_itemPutC( NULL, "" ); + if( SELF_ORDINFO( ( AREAP ) pArea, DBOI_BAGEXT, &pExtInfo ) == SUCCESS && + hb_itemGetCLen( pExt ) > 0 ) + { + pFileName->szExtension = hb_itemGetCPtr( pExt ); + } + } + hb_fsFNameMerge( szFileName, pFileName ); + + if( fProd ) + { + if( ! pFileName->szName ) + *fProd = FALSE; + else if( !fName ) + *fProd = TRUE; + else + { + PHB_FNAME pTableFileName = hb_fsFNameSplit( pArea->szDataFileName ); + + *fProd = pTableFileName->szName && + hb_stricmp( pTableFileName->szName, pFileName->szName ) == 0; + if( *fProd && pFileName->szExtension && ! pExt ) + { + DBORDERINFO pExtInfo; + memset( &pExtInfo, 0, sizeof( pExtInfo ) ); + pExt = pExtInfo.itmResult = hb_itemPutC( NULL, "" ); + if( SELF_ORDINFO( ( AREAP ) pArea, DBOI_BAGEXT, &pExtInfo ) == SUCCESS ) + { + *fProd = hb_stricmp( pFileName->szExtension, + hb_itemGetCPtr( pExt ) ) == 0; + } + } + hb_xfree( pTableFileName ); + } + } + hb_xfree( pFileName ); + if( pExt ) + hb_itemRelease( pExt ); +} + +/* + * free (close) used indexes, if not fAll then keep structure index + */ +static void hb_cdxOrdListClear( CDXAREAP pArea, BOOL fAll, LPCDXINDEX pKeepInd ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrdListClear(%p, %d)", pArea, (int) fAll)); + + if ( pArea->lpIndexes ) + { + LPCDXINDEX pIndex, * pIndexPtr; + + if ( !fAll ) + { + /* TODO: we have to control this on open */ + PHB_FNAME pFileNameDbf, pFileNameCdx; + pFileNameDbf = hb_fsFNameSplit( pArea->szDataFileName ); + pFileNameCdx = hb_fsFNameSplit( pArea->lpIndexes->szFileName ); + fAll = hb_stricmp( pFileNameDbf->szName ? pFileNameDbf->szName : "", + pFileNameCdx->szName ? pFileNameCdx->szName : "" ) != 0; + if( !fAll ) + { + DBORDERINFO pExtInfo; + PHB_ITEM pExt; + + memset( &pExtInfo, 0, sizeof( pExtInfo ) ); + pExt = pExtInfo.itmResult = hb_itemPutC( NULL, "" ); + if( SELF_ORDINFO( ( AREAP ) pArea, DBOI_BAGEXT, &pExtInfo ) == SUCCESS ) + { + fAll = hb_stricmp( pFileNameCdx->szExtension, + hb_itemGetCPtr( pExt ) ) != 0; + } + hb_itemRelease( pExt ); + } + hb_xfree( pFileNameDbf ); + hb_xfree( pFileNameCdx ); + } + pIndexPtr = fAll ? &pArea->lpIndexes : &pArea->lpIndexes->pNext; + while ( *pIndexPtr ) + { + pIndex = *pIndexPtr; + if ( pKeepInd == pIndex ) + pIndexPtr = &pIndex->pNext; + else + { + *pIndexPtr = pIndex->pNext; + hb_cdxIndexFree( pIndex ); + } + } + } +} + + +/* + * find order bag by its name + */ +static LPCDXINDEX hb_cdxFindBag( CDXAREAP pArea, char * szBagName ) +{ + LPCDXINDEX pIndex; + PHB_FNAME pFileName; + char * szBaseName, * szBasePath, * szBaseExt; + + pFileName = hb_fsFNameSplit( szBagName ); + szBaseName = hb_strdup( pFileName->szName ? pFileName->szName : "" ); + szBasePath = pFileName->szPath ? hb_strdup( pFileName->szPath ) : NULL; + szBaseExt = pFileName->szExtension ? hb_strdup( pFileName->szExtension ) : NULL; + hb_strUpper( szBaseName, strlen(szBaseName) ); + + pIndex = pArea->lpIndexes; + while ( pIndex ) + { + hb_xfree( pFileName ); + pFileName = hb_fsFNameSplit( pIndex->szFileName ); + if ( !hb_stricmp( pFileName->szName ? pFileName->szName : "", szBaseName ) && + ( !szBasePath || + ( pFileName->szPath && !hb_stricmp( pFileName->szPath, szBasePath ) ) ) && + ( !szBaseExt || + ( pFileName->szExtension && !hb_stricmp( pFileName->szExtension, szBaseExt ) ) ) ) + break; + pIndex = pIndex->pNext; + } + hb_xfree( pFileName ); + hb_xfree( szBaseName ); + if ( szBasePath ) + hb_xfree( szBasePath ); + if ( szBaseExt ) + hb_xfree( szBaseExt ); + return pIndex; +} + +/* + * get Tag by number + */ +static LPCDXTAG hb_cdxGetTagByNumber( CDXAREAP pArea, USHORT uiTag ) +{ + LPCDXTAG pTag = NULL; + LPCDXINDEX pIndex = pArea->lpIndexes; + + while ( uiTag && pIndex ) + { + pTag = pIndex->TagList; + while ( uiTag && pTag ) + { + if ( --uiTag ) + pTag = pTag->pNext; + } + pIndex = pIndex->pNext; + } + return pTag; +} + +/* + * get Tag number + */ +static USHORT hb_cdxGetTagNumber( CDXAREAP pArea, LPCDXTAG pFindTag ) +{ + USHORT uiTag = 0; + LPCDXTAG pTag = NULL; + LPCDXINDEX pIndex = pArea->lpIndexes; + + if ( pFindTag ) + { + while ( pIndex && ( pTag != pFindTag ) ) + { + pTag = pIndex->TagList; + while ( pTag ) + { + uiTag++; + if ( pTag == pFindTag ) + break; + pTag = pTag->pNext; + } + pIndex = pIndex->pNext; + } + if ( !pTag ) + uiTag = 0; + } + return uiTag; +} + +/* + * find Tag in tag list + */ +static LPCDXTAG hb_cdxFindTag( CDXAREAP pArea, PHB_ITEM pTagItem, + PHB_ITEM pBagItem, USHORT *puiTag ) +{ + LPCDXTAG pTag = NULL; + int iTag = 0, iFind = 0; + char szTag[ CDX_MAXTAGNAMELEN + 1 ]; + LPCDXINDEX pIndex = pArea->lpIndexes; + BOOL fBag; + + hb_strncpyUpperTrim( szTag, hb_itemGetCPtr( pTagItem ), CDX_MAXTAGNAMELEN ); + if( ! szTag[0] ) + iFind = hb_itemGetNI( pTagItem ); + + fBag = hb_itemGetCLen( pBagItem ) > 0; + if( fBag ) + { + if( szTag[ 0 ] ) + pIndex = hb_cdxFindBag( pArea, hb_itemGetCPtr( pBagItem ) ); + } + else + { + int iBag = hb_itemGetNI( pBagItem ); + + if( iBag > 0 ) + { + fBag = TRUE; + while( pIndex ) + { + if( --iBag == 0 ) + break; + pIndex = pIndex->pNext; + } + } + else if( iBag < 0 ) + { + pIndex = NULL; + } + } + + if( pIndex && ( iFind > 0 || szTag[0] ) ) + { + do + { + pTag = pIndex->TagList; + while( pTag ) + { + iTag++; + if ( ( iFind != 0 ? iTag == iFind : !hb_stricmp( pTag->szName, szTag ) ) ) + break; + pTag = pTag->pNext; + } + if ( pTag || fBag ) + break; + pIndex = pIndex->pNext; + } while ( pIndex ); + } + + if( puiTag ) + { + if( !pTag ) + *puiTag = 0; + else if( fBag ) + *puiTag = hb_cdxGetTagNumber( pArea, pTag ); + else + *puiTag = iTag; + } + + return pTag; +} + +/* + * get current active Tag + */ +static LPCDXTAG hb_cdxGetActiveTag( CDXAREAP pArea ) +{ + LPCDXTAG pTag; + + if ( !pArea->uiTag ) + return NULL; + pTag = hb_cdxGetTagByNumber( pArea, pArea->uiTag ); + if ( !pTag ) + pArea->uiTag = 0; + return pTag; +} + +/* + * refresh CurKey value and set proper path from RootPage to LeafPage + */ +static BOOL hb_cdxCurKeyRefresh( CDXAREAP pArea, LPCDXTAG pTag ) +{ + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + if ( !pArea->fPositioned ) + { + pTag->TagEOF = TRUE; + pTag->fRePos = FALSE; + pTag->CurKey->rec = 0; + return FALSE; + } + else if ( pTag->fRePos || pTag->CurKey->rec != pArea->ulRecNo ) + { + BYTE buf[CDX_MAXKEY]; + BOOL fBuf = FALSE; + LPCDXKEY pKey = NULL; + + /* Try to find previous if it's key for the same record */ + if ( pTag->CurKey->rec == pArea->ulRecNo ) + { + fBuf = TRUE; + memcpy( buf, pTag->CurKey->val, pTag->CurKey->len ); + pKey = hb_cdxKeyCopy( pKey, pTag->CurKey ); + hb_cdxTagKeyFind( pTag, pKey ); + } + if ( pTag->CurKey->rec != pArea->ulRecNo ) + { + BOOL fValidBuf = pArea->fValidBuffer; + /* not found, create new key from DBF and if differs seek again */ + pKey = hb_cdxKeyEval( pKey, pTag ); + if ( !fBuf || memcmp( buf, pKey->val, pKey->len ) != 0 ) + { + hb_cdxTagKeyFind( pTag, pKey ); + } + /* not found, if key was generated from DBF buffer then force to + * update it, create the new key and if differs seek again */ + if ( pTag->CurKey->rec != pArea->ulRecNo && fValidBuf ) + { + SELF_GOTO( ( AREAP ) pArea, pArea->ulRecNo ); + memcpy( buf, pKey->val, pKey->len ); + pKey = hb_cdxKeyEval( pKey, pTag ); + if ( memcmp( buf, pKey->val, pKey->len ) != 0 ) + hb_cdxTagKeyFind( pTag, pKey ); + } + } + hb_cdxKeyFree( pKey ); + return ( pTag->CurKey->rec != 0 && pTag->CurKey->rec == pArea->ulRecNo ); + } + return TRUE; +} + +/* + * skip to next/previous unique key + */ +static ERRCODE hb_cdxDBOISkipUnique( CDXAREAP pArea, LPCDXTAG pTag, BOOL fForward ) +{ + ERRCODE retval; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxDBOISkipUnique(%p, %p, %i)", pArea, pTag, fForward)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( ! pTag ) + return SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ); + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + + if ( !pArea->fPositioned ) + { + if ( fForward ) + retval = SELF_GOTO( ( AREAP ) pArea, 0 ); + else + retval = SELF_GOBOTTOM( ( AREAP ) pArea ); + } + else + { + LPCDXKEY pKey = NULL; + BOOL fOut = FALSE; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( pTag->TagEOF || ( fForward ? !hb_cdxBottomScope( pTag ) : + !hb_cdxTopScope( pTag ) ) ) + fOut = TRUE; + else if ( ( fForward ? pTag->UsrAscend && hb_cdxTopScope( pTag ) : + !pTag->UsrAscend && hb_cdxBottomScope( pTag ) ) && + pTag->CurKey->rec != 0 ) + { + pKey = hb_cdxKeyEval( pKey, pTag ); + } + } + if ( fForward ) + { + if ( pArea->fPositioned && !pTag->TagEOF ) + { + if ( !pKey ) + { + pKey = hb_cdxKeyCopy( NULL, pTag->CurKey ); + hb_cdxTagSkipNext( pTag ); + } + while ( !pTag->TagEOF ) + { + if ( hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, TRUE ) != 0 ) + { + SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + break; + } + hb_cdxTagSkipNext( pTag ); + } + } + retval = SELF_GOTO( ( AREAP ) pArea, ( !pArea->fPositioned || pTag->TagEOF ) + ? 0 : pTag->CurKey->rec ); + } + else + { + if ( !fOut && !pTag->TagBOF ) + { + if ( !pKey ) + { + pKey = hb_cdxKeyCopy( NULL, pTag->CurKey ); + hb_cdxTagSkipPrev( pTag ); + } + while ( !pTag->TagBOF ) + { + if ( hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, TRUE ) != 0 ) + { + SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + SELF_SKIPFILTER( ( AREAP ) pArea, -1 ); + break; + } + hb_cdxTagSkipPrev( pTag ); + } + } + + if ( fOut || pTag->TagBOF ) + { + retval = SELF_GOTOP( ( AREAP ) pArea ); + pArea->fBof = TRUE; + } + else + { + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + if ( pKey ) + hb_cdxKeyFree( pKey ); + } + /* Update Bof and Eof flags */ + if( fForward ) + pArea->fBof = FALSE; + else + pArea->fEof = FALSE; + + return retval; +} + +/* + * skip while code block doesn't return TRUE + */ +static BOOL hb_cdxDBOISkipEval( CDXAREAP pArea, LPCDXTAG pTag, BOOL fForward, + PHB_ITEM pEval ) +{ + BOOL fFound = FALSE, fFirst = TRUE; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxDBOISkipEval(%p, %p, %i, %p)", pArea, pTag, fForward, pEval)); + + if( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FALSE; + + if( ! pTag || hb_itemType( pEval ) != HB_IT_BLOCK ) + { + if( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) == FAILURE ) + return FALSE; + return fForward ? !pArea->fEof : !pArea->fBof; + } + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( !pTag->TagEOF && pTag->CurKey->rec != 0 && + ( fForward ? pTag->UsrAscend : !pTag->UsrAscend ) && + hb_cdxTopScope( pTag ) && hb_cdxBottomScope( pTag ) ) + fFirst = FALSE; + } + if ( fForward ) + { + if ( fFirst ) + hb_cdxTagSkipNext( pTag ); + while ( !pTag->TagEOF ) + { + if ( SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ) == FAILURE ) + break; + if ( hb_cdxEvalSeekCond( pTag, pEval ) ) + { + ULONG ulRecNo = pArea->ulRecNo; + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + if ( pArea->ulRecNo == ulRecNo || hb_cdxEvalSeekCond( pTag, pEval ) ) + { + fFound = TRUE; + break; + } + } + hb_cdxTagSkipNext( pTag ); + } + if ( !fFound ) + SELF_GOTO( ( AREAP ) pArea, 0 ); + } + else + { + if ( fFirst ) + hb_cdxTagSkipPrev( pTag ); + while ( !pTag->TagBOF ) + { + if ( SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ) == FAILURE ) + break; + if ( hb_cdxEvalSeekCond( pTag, pEval ) ) + { + ULONG ulRecNo = pArea->ulRecNo; + SELF_SKIPFILTER( ( AREAP ) pArea, -1 ); + if ( pArea->ulRecNo == ulRecNo || hb_cdxEvalSeekCond( pTag, pEval ) ) + { + fFound = TRUE; + break; + } + } + hb_cdxTagSkipPrev( pTag ); + } + if ( !fFound ) + { + SELF_GOTOP( ( AREAP ) pArea ); + pArea->fBof = TRUE; + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + + /* Update Bof and Eof flags */ + if( fForward ) + pArea->fBof = FALSE; + else + pArea->fEof = FALSE; + + return fFound; +} + +/* + * skip while comparison with given pattern with wildcards doesn't return TRUE + */ +static BOOL hb_cdxDBOISkipWild( CDXAREAP pArea, LPCDXTAG pTag, BOOL fForward, + PHB_ITEM pWildItm ) +{ + BOOL fFound = FALSE, fFirst = TRUE; + char *szPattern = hb_itemGetCPtr( pWildItm ); + int iFixed = 0, iStop; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxDBOISkipWild(%p, %p, %i, %p)", pArea, pTag, fForward, pWildItm)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FALSE; + + if ( ! pTag || pTag->uiType != 'C' || !szPattern || !*szPattern ) + { + if ( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) == FAILURE ) + return FALSE; + return fForward ? pArea->fPositioned : !pArea->fBof; + } + +#ifndef HB_CDP_SUPPORT_OFF + if( pArea->cdPage != hb_cdp_page ) + { + szPattern = hb_strdup( szPattern ); + hb_cdpTranslate( szPattern, hb_cdp_page, pArea->cdPage ); + } +#endif + while( iFixed < pTag->uiLen && szPattern[ iFixed ] && + szPattern[ iFixed ] != '*' && szPattern[ iFixed ] != '?' ) + { + ++iFixed; + } + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( !pTag->TagEOF && pTag->CurKey->rec != 0 && + ( fForward ? pTag->UsrAscend : !pTag->UsrAscend ) && + hb_cdxTopScope( pTag ) && hb_cdxBottomScope( pTag ) ) + fFirst = FALSE; + } + + iStop = pTag->UsrAscend ? -1 : 1; + if( !fForward ) + iStop = -iStop; + + if( iFixed && !pTag->TagEOF && pTag->CurKey->rec != 0 && + hb_cdxValCompare( pTag, ( BYTE * ) szPattern, iFixed, + pTag->CurKey->val, iFixed, FALSE ) == -iStop ) + { + LPCDXKEY pKey; + + pKey = hb_cdxKeyPut( NULL, ( BYTE * ) szPattern, iFixed, + pTag->UsrAscend ? CDX_IGNORE_REC_NUM : CDX_MAX_REC_NUM ); + if( !hb_cdxTagKeyFind( pTag, pKey ) ) + { + if( fForward ) + pTag->TagEOF = TRUE; + else + pTag->TagBOF = TRUE; + } + hb_cdxKeyFree( pKey ); + fFirst = FALSE; + } + + if ( fForward ) + { + if ( fFirst ) + hb_cdxTagSkipNext( pTag ); + while ( !pTag->TagEOF ) + { + if ( hb_strMatchWild( (const char *) pTag->CurKey->val, szPattern ) ) + { + ULONG ulRecNo = pTag->CurKey->rec; + if( SELF_GOTO( ( AREAP ) pArea, ulRecNo ) != SUCCESS ) + break; + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + if ( pArea->ulRecNo == ulRecNo || + hb_strMatchWild( (const char *) pTag->CurKey->val, szPattern ) ) + { + fFound = TRUE; + break; + } + } + if( iFixed && hb_cdxValCompare( pTag, ( BYTE * ) szPattern, iFixed, + pTag->CurKey->val, iFixed, FALSE ) == iStop ) + { + break; + } + hb_cdxTagSkipNext( pTag ); + } + if( !fFound ) + SELF_GOTO( ( AREAP ) pArea, 0 ); + } + else + { + if ( fFirst ) + hb_cdxTagSkipPrev( pTag ); + while ( !pTag->TagBOF ) + { + if ( hb_strMatchWild( (const char *) pTag->CurKey->val, szPattern ) ) + { + ULONG ulRecNo = pTag->CurKey->rec; + if( SELF_GOTO( ( AREAP ) pArea, ulRecNo ) != SUCCESS ) + break; + SELF_SKIPFILTER( ( AREAP ) pArea, -1 ); + if ( pArea->ulRecNo == ulRecNo || + hb_strMatchWild( (const char *) pTag->CurKey->val, szPattern ) ) + { + fFound = TRUE; + break; + } + } + if( iFixed && hb_cdxValCompare( pTag, ( BYTE * ) szPattern, iFixed, + pTag->CurKey->val, iFixed, FALSE ) == iStop ) + { + break; + } + hb_cdxTagSkipPrev( pTag ); + } + if ( !fFound ) + { + SELF_GOTOP( ( AREAP ) pArea ); + pArea->fBof = TRUE; + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + + /* Update Bof and Eof flags */ + if( fForward ) + pArea->fBof = FALSE; + else + pArea->fEof = FALSE; + +#ifndef HB_CDP_SUPPORT_OFF + if( pArea->cdPage != hb_cdp_page ) + { + hb_xfree( szPattern ); + } +#endif + + return fFound; +} + +static BOOL hb_cdxRegexMatch( CDXAREAP pArea, PHB_REGEX pRegEx, LPCDXKEY pKey ) +{ + char * szKey = ( char * ) pKey->val; +#ifndef HB_CDP_SUPPORT_OFF + char szBuff[ CDX_MAXKEY + 1 ]; + + if( pArea->cdPage != hb_cdp_page ) + { + hb_strncpy( szBuff, szKey, pKey->len ); + hb_cdpnTranslate( szBuff, pArea->cdPage, hb_cdp_page, pKey->len ); + szKey = szBuff; + } +#else + HB_SYMBOL_UNUSED( pArea ); +#endif + return hb_regexMatch( pRegEx, szKey, FALSE ); +} + +/* + * skip while regular expression on index key val doesn't return TRUE + */ +static BOOL hb_cdxDBOISkipRegEx( CDXAREAP pArea, LPCDXTAG pTag, BOOL fForward, + PHB_ITEM pRegExItm ) +{ + BOOL fFound = FALSE, fFirst = TRUE; + PHB_REGEX pRegEx; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxDBOISkipRegEx(%p, %p, %i, %p)", pArea, pTag, fForward, pRegExItm)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FALSE; + + if( !pTag || pTag->uiType != 'C' || ( pRegEx = hb_regexGet( pRegExItm, 0 ) ) == NULL ) + { + if ( SELF_SKIP( ( AREAP ) pArea, fForward ? 1 : -1 ) == FAILURE ) + return FALSE; + return fForward ? pArea->fPositioned : !pArea->fBof; + } + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( !pTag->TagEOF && pTag->CurKey->rec != 0 && + ( fForward ? pTag->UsrAscend : !pTag->UsrAscend ) && + hb_cdxTopScope( pTag ) && hb_cdxBottomScope( pTag ) ) + fFirst = FALSE; + } + if ( fForward ) + { + if ( fFirst ) + hb_cdxTagSkipNext( pTag ); + while ( !pTag->TagEOF ) + { + if( hb_cdxRegexMatch( pArea, pRegEx, pTag->CurKey ) ) + { + ULONG ulRecNo = pArea->ulRecNo; + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + if ( pArea->ulRecNo == ulRecNo || + hb_cdxRegexMatch( pArea, pRegEx, pTag->CurKey ) ) + { + fFound = TRUE; + break; + } + } + hb_cdxTagSkipNext( pTag ); + } + SELF_GOTO( ( AREAP ) pArea, fFound ? pTag->CurKey->rec : 0 ); + } + else + { + if ( fFirst ) + hb_cdxTagSkipPrev( pTag ); + while ( !pTag->TagBOF ) + { + if( hb_cdxRegexMatch( pArea, pRegEx, pTag->CurKey ) ) + { + ULONG ulRecNo = pArea->ulRecNo; + SELF_SKIPFILTER( ( AREAP ) pArea, -1 ); + if ( pArea->ulRecNo == ulRecNo || + hb_cdxRegexMatch( pArea, pRegEx, pTag->CurKey ) ) + { + fFound = TRUE; + break; + } + } + hb_cdxTagSkipPrev( pTag ); + } + if ( fFound ) + SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + else + { + SELF_GOTOP( ( AREAP ) pArea ); + pArea->fBof = TRUE; + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + + /* Update Bof and Eof flags */ + if( fForward ) + pArea->fBof = FALSE; + else + pArea->fEof = FALSE; + + hb_regexFree( pRegEx ); + + return fFound; +} + +/* + * evaluate given C function in given scope + */ +static ULONG hb_cdxDBOIScopeEval( LPCDXTAG pTag, HB_EVALSCOPE_FUNC pFunc, void *pParam, PHB_ITEM pItemLo, PHB_ITEM pItemHi ) +{ + ULONG ulCount = 0, ulLen = ( ULONG ) pTag->uiLen; + LPCDXKEY pCurKey = hb_cdxKeyCopy( NULL, pTag->CurKey ), + pTopScopeKey = pTag->topScopeKey, + pBtmScopeKey = pTag->bottomScopeKey; + + /* TODO: RT error when item type differ then Tag type */ + if ( !pItemLo || HB_IS_NIL( pItemLo ) ) + pTag->topScopeKey = NULL; + else + pTag->topScopeKey = hb_cdxKeyPutItem( NULL, pItemLo, CDX_IGNORE_REC_NUM, pTag, TRUE, FALSE ); + + if ( !pItemHi || HB_IS_NIL( pItemHi ) ) + pTag->bottomScopeKey = NULL; + else + pTag->bottomScopeKey = hb_cdxKeyPutItem( NULL, pItemHi, CDX_MAX_REC_NUM, pTag, TRUE, FALSE ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagGoTop( pTag ); + while( !pTag->TagEOF ) + { + pFunc( pTag->CurKey->rec, pTag->CurKey->val, ulLen, pParam ); + ulCount++; + hb_cdxTagSkipNext( pTag ); + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + + if ( pTag->topScopeKey ) + hb_cdxKeyFree( pTag->topScopeKey ); + pTag->topScopeKey = pTopScopeKey; + if ( pTag->bottomScopeKey ) + hb_cdxKeyFree( pTag->bottomScopeKey ); + pTag->bottomScopeKey = pBtmScopeKey; + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS ); + + pTag->fRePos = TRUE; + hb_cdxKeyCopy( pTag->CurKey, pCurKey ); + hb_cdxKeyFree( pCurKey ); + + return ulCount; +} + +/* + * return number of keys in order + */ +static LONG hb_cdxDBOIKeyCount( CDXAREAP pArea, LPCDXTAG pTag, BOOL fFilters ) +{ + ULONG ulKeyCount = 0; + BOOL fLogOpt = pArea->dbfi.itmCobExpr || !pArea->dbfi.fFilter; + + if ( pTag ) + { + BOOL fCheckFilter = ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ); + ULONG ulRecNo = pArea->ulRecNo; + LPCDXKEY pCurKey; + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + if ( pTag && ( fFilters ? fLogOpt && CURKEY_LOGCNT( pTag ) : CURKEY_RAWCNT( pTag ) ) ) + { + ulKeyCount = fFilters ? pTag->logKeyCount : pTag->rawKeyCount; + } + else + { + if ( pTag->topScopeKey || pTag->bottomScopeKey || pTag->UsrUnique || pArea->dbfi.fFilter ) + { + pCurKey = hb_cdxKeyCopy( NULL, pTag->CurKey ); + hb_cdxTagGoTop( pTag ); + while ( !pTag->TagEOF ) + { + if ( !fCheckFilter || hb_cdxCheckRecordFilter( pArea, pTag->CurKey->rec ) ) + ulKeyCount++; + hb_cdxTagSkipNext( pTag ); + } + pTag->fRePos = TRUE; + hb_cdxKeyCopy( pTag->CurKey, pCurKey ); + hb_cdxKeyFree( pCurKey ); + if ( fCheckFilter ) + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + } + else + { + LPCDXPAGE pPage; + pCurKey = hb_cdxKeyCopy( NULL, pTag->CurKey ); + if ( pTag->UsrAscend ) + hb_cdxTagGoTop( pTag ); + else + hb_cdxTagGoBottom( pTag ); + pPage = pTag->RootPage; + while ( pPage->Child ) + pPage = pPage->Child; + ulKeyCount = pPage->iKeys; + if ( pPage->Right != CDX_DUMMYNODE ) + { + ULONG ulPage = pPage->Right; + pPage = hb_cdxPageNew( pTag, NULL, CDX_DUMMYNODE ); + pPage->Page = ulPage; + while ( pPage->Page != CDX_DUMMYNODE ) + { + hb_cdxPageLoad( pPage ); + ulKeyCount += pPage->iKeys; + pPage->Page = pPage->Right; + } + hb_cdxPageFree( pPage, TRUE ); + } + pTag->fRePos = TRUE; + hb_cdxKeyCopy( pTag->CurKey, pCurKey ); + hb_cdxKeyFree( pCurKey ); + } + if ( !fFilters ) + { + pTag->rawKeyCount = ulKeyCount; + pTag->curKeyState |= CDX_CURKEY_RAWCNT; + } + else if ( fLogOpt ) + CURKEY_SETLOGCNT( pTag, ulKeyCount ) + + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + else /* no filter, no order */ + { + if ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ) + { + ULONG ulRecNo = pArea->ulRecNo; + + if ( SELF_GOTOP( ( AREAP ) pArea ) == SUCCESS ) + { + while ( !pArea->fEof ) + { + ulKeyCount++; + if ( SELF_SKIP( ( AREAP ) pArea, 1 ) != SUCCESS ) + break; + } + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + } + } + else + { + SELF_RECCOUNT( ( AREAP ) pArea, &ulKeyCount ); + } + } + return ulKeyCount; +} + +/* + * return logical key position in order + */ +static LONG hb_cdxDBOIKeyNo( CDXAREAP pArea, LPCDXTAG pTag, BOOL fFilters ) +{ + ULONG ulKeyNo = 0; + BOOL fLogOpt = pArea->dbfi.itmCobExpr || !pArea->dbfi.fFilter; + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + if ( !pArea->fPositioned ) + ulKeyNo = 0; + else if ( pTag ) + { + BOOL fCheckFilter = ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ); + ULONG ulRecNo = pArea->ulRecNo; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + if ( fFilters ? ( fLogOpt && CURKEY_LOGPOS( pTag ) ) : + ( CURKEY_RAWPOS( pTag ) && + pTag->rawKeyRec == pArea->ulRecNo ) ) + { + ulKeyNo = fFilters ? pTag->logKeyPos : pTag->rawKeyPos; + } + else + { + hb_cdxTagOpen( pTag ); + if ( hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( pTag->topScopeKey || pTag->bottomScopeKey || pTag->UsrUnique || pArea->dbfi.fFilter ) + { + if ( hb_cdxBottomScope( pTag ) && hb_cdxTopScope( pTag ) && + ( !fCheckFilter || hb_cdxCheckRecordFilter( pArea, ulRecNo ) ) ) + { + LPCDXKEY pCurKey = hb_cdxKeyCopy( NULL, pTag->CurKey ); + if ( !hb_cdxCheckRecordScope( pArea, pTag->CurKey->rec ) ) + hb_cdxTagSkipPrev( pTag ); + while ( !pTag->TagBOF ) + { + if ( !fCheckFilter || hb_cdxCheckRecordFilter( pArea, pTag->CurKey->rec ) ) + ulKeyNo++; + hb_cdxTagSkipPrev( pTag ); + } + pTag->fRePos = TRUE; + hb_cdxKeyCopy( pTag->CurKey, pCurKey ); + hb_cdxKeyFree( pCurKey ); + if ( fCheckFilter ) + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + } + } + else + { + LPCDXPAGE pPage = pTag->RootPage; + while ( pPage->Child ) + pPage = pPage->Child; + if ( pTag->UsrAscend ) + { + ulKeyNo = pPage->iCurKey + 1; + if ( pPage->Left != CDX_DUMMYNODE ) + { + ULONG ulPage = pPage->Left; + pPage = hb_cdxPageNew( pTag, NULL, CDX_DUMMYNODE ); + pPage->Page = ulPage; + while ( pPage->Page != CDX_DUMMYNODE ) + { + hb_cdxPageLoad( pPage ); + ulKeyNo += pPage->iKeys; + pPage->Page = pPage->Left; + } + hb_cdxPageFree( pPage, TRUE ); + } + } + else + { + ulKeyNo = pPage->iKeys - pPage->iCurKey; + if ( pPage->Right != CDX_DUMMYNODE ) + { + ULONG ulPage = pPage->Right; + pPage = hb_cdxPageNew( pTag, NULL, CDX_DUMMYNODE ); + pPage->Page = ulPage; + while ( pPage->Page != CDX_DUMMYNODE ) + { + hb_cdxPageLoad( pPage ); + ulKeyNo += pPage->iKeys; + pPage->Page = pPage->Right; + } + hb_cdxPageFree( pPage, TRUE ); + } + } + } + if ( ulKeyNo != 0 ) + { + if ( !fFilters ) + { + pTag->rawKeyPos = ulKeyNo; + CURKEY_SETRAWPOS( pTag ); + } + else if ( fLogOpt ) + { + pTag->logKeyPos = ulKeyNo; + CURKEY_SETLOGPOS( pTag ); + } + } + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + else + { + ULONG ulRecNo = pArea->ulRecNo; + + if ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ) + { + if ( hb_cdxCheckRecordFilter( pArea, ulRecNo ) ) + { + do + { + ulKeyNo++; + if ( SELF_SKIP( ( AREAP ) pArea, -1 ) != SUCCESS ) + break; + } while ( !( ( AREAP ) pArea )->fBof ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + } + } + else + { + ulKeyNo = ulRecNo; + } + } + return ulKeyNo; +} + +/* + * DBOI_KEYGOTO goto specific logical record in the index file + */ +static ERRCODE hb_cdxDBOIKeyGoto( CDXAREAP pArea, LPCDXTAG pTag, ULONG ulKeyNo, BOOL fFilters ) +{ + ERRCODE retval; + ULONG ulKeyCnt = ulKeyNo; + BOOL fLogOpt = pArea->dbfi.itmCobExpr || !pArea->dbfi.fFilter; + + if ( ulKeyNo == 0 ) + retval = SELF_GOTO( ( AREAP ) pArea, 0 ); + else if ( pTag ) + { + BOOL fCheckFilter = ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ); + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! pArea->lpdbPendingRel && ( fFilters ? + fLogOpt && CURKEY_LOGPOS( pTag ) && pTag->logKeyPos == ulKeyNo : + ( CURKEY_RAWPOS( pTag ) && pTag->rawKeyPos == ulKeyNo ) ) ) + { + retval = SELF_GOTO( ( AREAP ) pArea, fFilters ? pTag->logKeyRec : pTag->rawKeyRec ); + } + else + { + if ( pTag->topScopeKey || pTag->bottomScopeKey || pTag->UsrUnique || pArea->dbfi.fFilter ) + { + hb_cdxTagGoTop( pTag ); + if ( fCheckFilter ) + while ( !pTag->TagEOF ) + { + if ( hb_cdxCheckRecordFilter( pArea, pTag->CurKey->rec ) ) + { + if ( ! --ulKeyCnt ) + break; + } + hb_cdxTagSkipNext( pTag ); + } + else + while( !pTag->TagEOF && --ulKeyCnt ) + hb_cdxTagSkipNext( pTag ); + } + else + { + LPCDXPAGE pPage, pOwnerPage = NULL; + ULONG ulNextPg; + hb_cdxTagGoTop( pTag ); + pPage = pTag->RootPage; + while ( pPage->Child ) + { + pOwnerPage = pPage; + pPage = pPage->Child; + } + while ( (ULONG) pPage->iKeys < ulKeyCnt && pOwnerPage && + ( ulNextPg = pTag->UsrAscend ? + pPage->Right : pPage->Left ) != CDX_DUMMYNODE ) + { + ulKeyCnt -= pPage->iKeys; + pOwnerPage->Child = hb_cdxPageNew( pPage->TagParent, pPage->Owner, ulNextPg ); + hb_cdxPageFree( pPage, FALSE ); + pPage = pOwnerPage->Child; + } + if ( (ULONG) pPage->iKeys >= ulKeyCnt ) + { + pPage->iCurKey = pTag->UsrAscend ? ( SHORT ) ulKeyCnt - 1 : pPage->iKeys - ( SHORT ) ulKeyCnt; + hb_cdxSetCurKey( pPage ); + } + else + { + pTag->CurKey->rec = 0; + } + } + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + if ( pArea->fPositioned ) + { + if ( !fFilters ) + { + pTag->rawKeyPos = ulKeyNo; + CURKEY_SETRAWPOS( pTag ); + } + else if ( fLogOpt ) + { + pTag->logKeyPos = ulKeyNo; + CURKEY_SETLOGPOS( pTag ); + } + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + else + { + if ( fLogOpt && fFilters && pArea->dbfi.itmCobExpr ) + { + retval = SELF_GOTOP( ( AREAP ) pArea ); + if( retval == SUCCESS && --ulKeyCnt ) + retval = SELF_SKIP( ( AREAP ) pArea, ulKeyCnt ); + } + else + { + retval = SELF_GOTO( ( AREAP ) pArea, ulKeyNo ); + } + } + + return retval; +} + +static double hb_cdxCountRelKeyPos( LPCDXPAGE pPage ) +{ + return ( ( pPage->Child ? hb_cdxCountRelKeyPos( pPage->Child ) : 0.5 ) + + pPage->iCurKey ) / pPage->iKeys; +} + +static BOOL hb_cdxGoToRelKeyPos( LPCDXPAGE pPage, double dPos ) +{ + do + { + if( pPage->iKeys == 0 ) + return FALSE; + + pPage->iCurKey = ( SHORT ) ( dPos * pPage->iKeys ); + if( pPage->iCurKey >= pPage->iKeys ) + pPage->iCurKey = pPage->iKeys - 1; + + if( ( pPage->PageType & CDX_NODE_LEAF ) != 0 ) + break; + + dPos = dPos * pPage->iKeys - pPage->iCurKey; + if( dPos < 0.0 ) + dPos = 0.0; + else if( dPos >= 1.0 ) + dPos = 1.0; + + hb_cdxPageGetChild( pPage ); + pPage = pPage->Child; + } + while( pPage ); + + return TRUE; +} + +static double hb_cdxDBOIGetRelKeyPos( CDXAREAP pArea, LPCDXTAG pTag ) +{ + ULONG ulRecNo = 0, ulRecCount = 0; + double dPos = 0.0; + + /* resolve any pending relations */ + SELF_RECNO( ( AREAP ) pArea, &ulRecNo ); + + if( !pArea->fPositioned ) + { + if( ulRecNo > 1 ) + dPos = 1.0; + } + else if( !pTag ) + { + SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ); + if( ulRecCount != 0 ) + dPos = ( 0.5 + ulRecNo ) / ulRecCount; + } + else + { + LPCDXKEY pKey; + double dStart, dStop, dFact = 0.0000000000001; + BOOL fOK = TRUE; + + if( pTag->UsrAscend ) + { + dStart = 0.0; + dStop = 1.0; + } + else + { + dStart = 1.0; + dStop = 0.0; + } + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + pKey = pTag->UsrAscend ? pTag->topScopeKey : pTag->bottomScopeKey; + if( pKey ) + { + hb_cdxTagKeyFind( pTag, pKey ); + if( pTag->CurKey->rec == 0 || pTag->TagEOF || ! hb_cdxBottomScope( pTag ) ) + fOK = FALSE; + else + dStart = hb_cdxCountRelKeyPos( pTag->RootPage ); + } + pKey = pTag->UsrAscend ? pTag->bottomScopeKey : pTag->topScopeKey; + if( pKey && fOK ) + { + hb_cdxTagKeyFind( pTag, pKey ); + if( pTag->CurKey->rec == 0 || pTag->TagBOF || ! hb_cdxTopScope( pTag ) ) + fOK = FALSE; + else + dStop = hb_cdxCountRelKeyPos( pTag->RootPage ); + } + if( fOK ) + { + if( !pTag->UsrAscend ) + { + double dTmp = dStart; + dStart = dStop; + dStop = dTmp; + } + pTag->fRePos = TRUE; + if( hb_cdxCurKeyRefresh( pArea, pTag ) && + hb_cdxTopScope( pTag ) && hb_cdxBottomScope( pTag ) ) + { + if( dStart >= dStop - dFact ) + dPos = 0.5; + else + { + dPos = hb_cdxCountRelKeyPos( pTag->RootPage ); + dPos = ( dPos - dStart ) / ( dStop - dStart ); + if( !pTag->UsrAscend ) + dPos = 1.0 - dPos; + /* fix possible differences in FL representation */ + if( dPos <= 0.0 ) + dPos = 0.0; + else if( dPos >= 1.0 ) + dPos = 1.0; + } + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + + return dPos; +} + +static void hb_cdxDBOISetRelKeyPos( CDXAREAP pArea, LPCDXTAG pTag, double dPos ) +{ + if( !pTag ) + { + if( dPos >= 1.0 ) + { + SELF_GOBOTTOM( ( AREAP ) pArea ); + } + else if( dPos <= 0.0 ) + { + SELF_GOTOP( ( AREAP ) pArea ); + } + else + { + ULONG ulRecCount, ulRecNo; + SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ); + ulRecNo = ( ULONG ) dPos * ulRecCount + 1; + if( ulRecNo >= ulRecCount ) + ulRecNo = ulRecCount; + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + if( pArea->fEof ) + SELF_GOTOP( ( AREAP ) pArea ); + } + } + else + { + BOOL fForward = TRUE, fOK = TRUE, fTop = FALSE; + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + if( dPos >= 1.0 ) + { + fForward = FALSE; + } + else if( dPos <= 0.0 ) + { + fTop = TRUE; + } + else + { + LPCDXKEY pKey; + double dStart, dStop, dFact = 0.0000000000001; + BOOL fOK = TRUE; + + if( pTag->UsrAscend ) + { + dStart = 0.0; + dStop = 1.0; + } + else + { + dStart = 1.0; + dStop = 0.0; + } + + pKey = pTag->UsrAscend ? pTag->topScopeKey : pTag->bottomScopeKey; + if( pKey ) + { + hb_cdxTagKeyFind( pTag, pKey ); + if( pTag->CurKey->rec == 0 || pTag->TagEOF || ! hb_cdxBottomScope( pTag ) ) + fOK = FALSE; + else + dStart = hb_cdxCountRelKeyPos( pTag->RootPage ); + } + pKey = pTag->UsrAscend ? pTag->bottomScopeKey : pTag->topScopeKey; + if( pKey && fOK ) + { + hb_cdxTagKeyFind( pTag, pKey ); + if( pTag->CurKey->rec == 0 || pTag->TagBOF || ! hb_cdxTopScope( pTag ) ) + fOK = FALSE; + else + dStop = hb_cdxCountRelKeyPos( pTag->RootPage ); + } + if( fOK ) + { + if( !pTag->UsrAscend ) + { + double dTmp = dStart; + dStart = dStop; + dStop = dTmp; + dPos = 1.0 - dPos; + } + if( dStart >= dStop - dFact ) + { + fTop = TRUE; + } + else + { + dPos = dPos * ( dStop - dStart ) + dStart; + pTag->fRePos = FALSE; + hb_cdxTagOpen( pTag ); + pTag->TagBOF = pTag->TagEOF = FALSE; + if( !hb_cdxGoToRelKeyPos( pTag->RootPage, dPos ) ) + { + fTop = TRUE; + } + else + { + hb_cdxSetCurKey( pTag->RootPage ); + if( !hb_cdxTopScope( pTag ) ) + fTop = TRUE; + else if( !hb_cdxBottomScope( pTag ) ) + fForward = FALSE; + } + } + } + } + if( !fOK ) + { + SELF_GOTO( ( AREAP ) pArea, 0 ); + } + else + { + if( fForward ) + { + if( fTop ) + hb_cdxTagGoTop( pTag ); + while( !pTag->TagEOF ) + { + if ( hb_cdxCheckRecordFilter( pArea, pTag->CurKey->rec ) ) + break; + hb_cdxTagSkipNext( pTag ); + } + if( pTag->TagEOF && !fTop ) + fForward = FALSE; + } + if( !fForward ) + { + hb_cdxTagGoBottom( pTag ); + while( !pTag->TagBOF ) + { + if ( hb_cdxCheckRecordFilter( pArea, pTag->CurKey->rec ) ) + break; + hb_cdxTagSkipPrev( pTag ); + } + if( pTag->TagBOF ) + { + pTag->CurKey->rec = 0; + } + } + SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } +} + +/* + * DBOI_FINDREC find a specific record in the tag - it's useful for + * custom indexes when the same record can be stored more then once + * or when the used index key is unknown + */ +static BOOL hb_cdxDBOIFindRec( CDXAREAP pArea, LPCDXTAG pTag, ULONG ulRecNo, BOOL fCont ) +{ + BOOL fFound = FALSE; + + if ( pTag && ulRecNo ) + { + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( fCont ) + { + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + ulRecNo = 0; + else + hb_cdxTagSkipNext( pTag ); + } + else + { + hb_cdxTagGoTop( pTag ); + } + if ( ulRecNo ) + { + while ( !pTag->TagBOF && !pTag->TagEOF && hb_cdxBottomScope( pTag ) ) + { + if ( pTag->CurKey->rec == ulRecNo ) + { + fFound = TRUE; + break; + } + hb_cdxTagSkipNext( pTag ); + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + SELF_GOTO( ( AREAP ) pArea, fFound ? ulRecNo : 0 ); + return fFound; +} + +static void hb_cdxClearLogPosInfo( CDXAREAP pArea ) +{ + LPCDXINDEX pIndex = pArea->lpIndexes; + LPCDXTAG pTag; + + while ( pIndex ) + { + pTag = pIndex->TagList; + while ( pTag ) + { + pTag->curKeyState &= ~( CDX_CURKEY_LOGPOS | CDX_CURKEY_LOGCNT ); + pTag = pTag->pNext; + } + pIndex = pIndex->pNext; + } +} + +/* + * -- BMDBFCDX METHODS -- + */ + +/* ( DBENTRYP_BP ) hb_cdxBof : NULL */ +/* ( DBENTRYP_BP ) hb_cdxEof : NULL */ +/* ( DBENTRYP_BP ) hb_cdxFound : NULL */ + +/* ( DBENTRYP_V ) hb_cdxGoBottom */ +static ERRCODE hb_cdxGoBottom( CDXAREAP pArea ) +{ + LPCDXTAG pTag; + ERRCODE retval; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxGoBottom(%p)", pArea)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + pTag = hb_cdxGetActiveTag( pArea ); + if ( ! pTag ) + return SUPER_GOBOTTOM( ( AREAP ) pArea ); + + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + hb_cdxTagGoBottom( pTag ); + + pArea->fTop = FALSE; + pArea->fBottom = TRUE; + + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + + if ( retval != FAILURE && pArea->fPositioned ) + { + retval = SELF_SKIPFILTER( ( AREAP ) pArea, -1 ); + + if ( pArea->fPositioned && CURKEY_LOGCNT( pTag ) ) + { + pTag->logKeyPos = pTag->logKeyCount; + CURKEY_SETLOGPOS( pTag ); + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + + return retval; +} + +/* ( DBENTRYP_UL ) hb_cdxGoTo : NULL */ +/* ( DBENTRYP_I ) hb_cdxGoToId : NULL */ + +/* ( DBENTRYP_V ) hb_cdxGoTop */ +static ERRCODE hb_cdxGoTop( CDXAREAP pArea ) +{ + LPCDXTAG pTag; + ERRCODE retval; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxGoTop(%p)", pArea)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + pTag = hb_cdxGetActiveTag( pArea ); + if ( ! pTag ) + return SUPER_GOTOP( ( AREAP ) pArea ); + + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + + hb_cdxTagGoTop( pTag ); + + pArea->fTop = TRUE; + pArea->fBottom = FALSE; + + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + + if ( retval != FAILURE && pArea->fPositioned ) + retval = SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + + if ( retval != FAILURE && pArea->fPositioned ) + { + pTag->logKeyPos = 1; + CURKEY_SETLOGPOS( pTag ); + } + + hb_cdxIndexUnLockRead( pTag->pIndex ); + return retval; +} + +/* ( DBENTRYP_BIB ) hb_cdxSeek */ +static ERRCODE hb_cdxSeek( CDXAREAP pArea, BOOL fSoftSeek, PHB_ITEM pKeyItm, BOOL fFindLast ) +{ + LPCDXTAG pTag; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxSeek(%p, %d, %p, %d)", pArea, fSoftSeek, pKeyItm, fFindLast)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + pTag = hb_cdxGetActiveTag( pArea ); + + if ( ! pTag ) + { + hb_cdxErrorRT( pArea, EG_NOORDER, 1201, NULL, 0, EF_CANDEFAULT ); + return FAILURE; + } + else + { + LPCDXKEY pKey; + ERRCODE retval = SUCCESS; + BOOL fEOF = FALSE, fLast; + ULONG ulRec; + + if( pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + pArea->fEof = FALSE; + + if ( pTag->UsrUnique ) + fLast = !pTag->UsrAscend; + else + fLast = pTag->UsrAscend ? fFindLast : !fFindLast; + + /* TODO: runtime error if valtype(pKeyItm) != pTag->Type */ + pKey = hb_cdxKeyPutItem( NULL, pKeyItm, fLast ? CDX_MAX_REC_NUM : CDX_IGNORE_REC_NUM, pTag, TRUE, FALSE ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + ulRec = hb_cdxTagKeyFind( pTag, pKey ); + if ( ( ulRec == 0 && ! fSoftSeek ) || pTag->TagEOF ) + fEOF = TRUE; + else /* if ( fSoftSeek ) */ + { + if ( ! hb_cdxBottomScope( pTag ) ) + fEOF = TRUE; + else if ( ! hb_cdxTopScope( pTag ) ) + { + hb_cdxTagGoTop( pTag ); + if ( pTag->CurKey->rec == 0 ) + fEOF = TRUE; + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + if ( !fEOF ) + { + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + if ( retval != FAILURE && pArea->fPositioned ) + { + retval = SELF_SKIPFILTER( ( AREAP ) pArea, fFindLast ? -1 : 1 ); + if ( retval != FAILURE && ulRec && pArea->fPositioned ) + { + pArea->fFound = ( ulRec == pArea->ulRecNo || + hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->CurKey->val, pTag->CurKey->len, FALSE ) == 0 ); + if ( ! pArea->fFound && ! fSoftSeek ) + fEOF = TRUE; + } + } + } + if ( retval != FAILURE && + ( fEOF || ! hb_cdxTopScope( pTag ) || + ! hb_cdxBottomScope( pTag ) ) ) + { + retval = SELF_GOTO( ( AREAP ) pArea, 0 ); + } + pArea->fBof = FALSE; + hb_cdxKeyFree( pKey ); + return retval; + } +} + +#if ! defined( HB_SIXCDX ) + +static BYTE * hb_cdxPageGetKeyValActual( LPCDXPAGE pPage ) +{ + while ( pPage->Child ) + { + pPage = pPage->Child; + } + return hb_cdxPageGetKeyVal( pPage, pPage->iCurKey ); +} + + +/* + * compare two values using Tag conditions (len & type) + */ +static int hb_cdxValCompareWild( BYTE * val1, BYTE * val2, BOOL fExact ) +{ + return ( ( fExact ) ? hb_strMatchWildExact( (const char *) val2, (const char *) val1 ) : hb_strMatchWild( (const char *) val2, (const char *) val1 ) ) ? 0: 1; +} + +/* + * seek given Key in the Page or in its children + */ +static int hb_cdxPageSeekKeyWild( LPCDXPAGE pPage, LPCDXKEY pKey, ULONG ulKeyRec, BOOL fExact, BOOL fNext ) +{ + int k; + + k = ( ulKeyRec == CDX_MAX_REC_NUM ) ? -1 : 1; + + if ( fNext ? hb_cdxPageReadNextKey( pPage ) : hb_cdxPageReadTopKey( pPage ) ) + { + do + { + k = hb_cdxValCompareWild( pKey->val, hb_cdxPageGetKeyValActual( pPage ), fExact ); + } + while ( k && hb_cdxPageReadNextKey( pPage ) ); + + if ( k == 0 ) + { + while ( pPage->Child ) + { + pPage = pPage->Child; + } + if ( ulKeyRec == CDX_MAX_REC_NUM ) + k = 1; + else if ( ulKeyRec != CDX_IGNORE_REC_NUM ) + { + ULONG ulRec = hb_cdxPageGetKeyRec( pPage, pPage->iCurKey ); + if ( ulKeyRec > ulRec ) + k = 1; + else if ( ulKeyRec < ulRec ) + k = -1; + } + } + } + return k; +} + +/* + * find pKey in pTag return 0 or TagNO + */ +static ULONG hb_cdxTagKeyFindWild( LPCDXTAG pTag, LPCDXKEY pKey, BOOL fNext ) +{ + int K; + ULONG ulKeyRec = pKey->rec; + + pTag->fRePos = FALSE; + hb_cdxTagOpen( pTag ); + + pTag->TagBOF = pTag->TagEOF = FALSE; + K = hb_cdxPageSeekKeyWild( pTag->RootPage, pKey, ulKeyRec, FALSE, fNext ); + if ( ulKeyRec == CDX_MAX_REC_NUM ) + K = - K; + + if ( K > 0 ) + { + pTag->CurKey->rec = 0; + pTag->TagEOF = TRUE; + } + else + { + hb_cdxSetCurKey( pTag->RootPage ); + if ( K == 0 ) + return pTag->CurKey->rec; + } + return 0; +} + + +/* hb_cdxSeekWild */ +static ERRCODE hb_cdxSeekWild( CDXAREAP pArea, BOOL fSoftSeek, PHB_ITEM pKeyItm, BOOL fFindLast, BOOL fNext, BOOL bAll ) +{ + LPCDXTAG pTag; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxSeekWild(%p, %d, %p, %d, %d)", pArea, fSoftSeek, pKeyItm, fFindLast, fNext)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + pTag = hb_cdxGetActiveTag( pArea ); + + if ( ! pTag ) + { + hb_cdxErrorRT( pArea, EG_NOORDER, 1201, NULL, 0, EF_CANDEFAULT ); + return FAILURE; + } + else + { + LPCDXKEY pKey; + ERRCODE retval = SUCCESS; + BOOL fEOF = FALSE, fLast; + ULONG ulRec; + + if( ! bAll && pArea->lpdbPendingRel && pArea->lpdbPendingRel->isScoped ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pArea->fTop = pArea->fBottom = FALSE; + pArea->fEof = FALSE; + + if ( pTag->UsrUnique ) + fLast = !pTag->UsrAscend; + else + fLast = pTag->UsrAscend ? fFindLast : !fFindLast; + + /* TODO: runtime error if valtype(pKeyItm) != pTag->Type */ + pKey = hb_cdxKeyPutItem( NULL, pKeyItm, fLast ? CDX_MAX_REC_NUM : CDX_IGNORE_REC_NUM, pTag, TRUE, FALSE ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + ulRec = hb_cdxTagKeyFindWild( pTag, pKey, fNext ); + if ( ( ulRec == 0 && ! fSoftSeek ) || pTag->TagEOF ) + fEOF = TRUE; + else /* if ( fSoftSeek ) */ + { + if ( ! hb_cdxBottomScope( pTag ) ) + fEOF = TRUE; + else if ( ! hb_cdxTopScope( pTag ) ) + { + hb_cdxTagGoTop( pTag ); + if ( pTag->CurKey->rec == 0 ) + fEOF = TRUE; + } + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + if ( !fEOF ) + { + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + if ( retval != FAILURE && pArea->fPositioned ) + { + retval = SELF_SKIPFILTER( ( AREAP ) pArea, fFindLast ? -1 : 1 ); + if ( retval != FAILURE && ulRec && pArea->fPositioned ) + { + pArea->fFound = ( ulRec == pArea->ulRecNo || + hb_cdxValCompareWild( pKey->val, pTag->CurKey->val, FALSE ) == 0 ); + if ( ! pArea->fFound && ! fSoftSeek ) + fEOF = TRUE; + } + } + } + if ( retval != FAILURE && + ( fEOF || ! hb_cdxTopScope( pTag ) || + ! hb_cdxBottomScope( pTag ) ) ) + { + retval = SELF_GOTO( ( AREAP ) pArea, 0 ); + } + pArea->fBof = FALSE; + hb_cdxKeyFree( pKey ); + return retval; + } +} + +HB_FUNC( BM_TURBO ) +{ + bTurbo = ISLOG( 1 ) ? hb_parl( 1 ) : FALSE; + hb_retl( bTurbo ); +} + +HB_FUNC( BM_DBGETFILTERARRAY ) +{ + CDXAREAP pArea = (CDXAREAP) hb_rddGetCurrentWorkAreaPointer(); + PHB_ITEM pList = hb_itemArrayNew( 0 ); + ULONG ulRec,ulRecOld; + + if ( pArea->dbfi.fOptimized ) + { + ULONG ulSize = ( ( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size+1) >> 5 ) + 1; + ULONG ulLong, ulByte, ulBytes, ulRecno; + + ulRecOld = pArea->ulRecNo; + + for ( ulLong = 0; ulLong < ulSize; ulLong++ ) + if ( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap[ulLong] ) + for ( ulByte = (ulLong<<2), ulBytes = 0; ulBytes < 4; ulByte++, ulBytes++ ) + if ( ((char*)( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap)[ulByte] ) + for ( ulRec=(ulByte<<3)+1, ulRecno=0; ulRecno < 8; ulRec++, ulRecno++ ) + if ( hb_cdxCheckRecordFilter( pArea, ulRec ) ) + hb_arrayAdd( pList, hb_itemPutNL( NULL, ulRec ) ); + + SELF_GOTO( (AREAP) pArea, ulRecOld ); + } + hb_itemRelease( hb_itemReturnForward( pList ) ); +} + +HB_FUNC( BM_DBSETFILTERARRAY ) +{ + AREAP pArea = (AREAP) hb_rddGetCurrentWorkAreaPointer(); + PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ); + ULONG ulPos,ulRecCount; + + if( pArea ) + { + if( pArray ) + { + LPCDXTAG pTag; + /* Limpiamos el filtro activo */ + if( SELF_CLEARFILTER( pArea ) != SUCCESS ) + return; + + pArea->dbfi.itmCobExpr = hb_itemPutC( NULL, "" ); + pArea->dbfi.abFilterText = hb_itemPutC( NULL, "" ); + pArea->dbfi.fOptimized = TRUE; + pArea->dbfi.fFilter = TRUE; + + SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ); + pArea->dbfi.lpvCargo = hb_xgrab( sizeof( BM_FILTER ) ); + memset( pArea->dbfi.lpvCargo, 0, sizeof( BM_FILTER ) ); + + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size = ulRecCount; + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap = (ULONG *) hb_xgrab( sizeof(ULONG) * (((ulRecCount+1) >> 5) + 1) ); + for ( ulPos = 1; ulPos <= ulRecCount; ulPos++ ) + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ulRecCount, ulPos ); + for ( ulPos = 1; ulPos <= hb_arrayLen( pArray ); ulPos++ ) + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ulRecCount, (ULONG) hb_arrayGetNL( pArray, ulPos ) ); + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag ) // Con índice activo + CURKEY_SETLOGCNT( pTag, (hb_arrayLen( pArray )) ) + } + else + hb_errRT_DBCMD( EG_ARG, EDBCMD_BADPARAMETER, NULL, "BM_DBSETFILTERARRAY" ); + } + else + hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "BM_DBSETFILTERARRAY" ); +} + +HB_FUNC( BM_DBSETFILTERARRAYADD ) +{ + AREAP pArea = (AREAP) hb_rddGetCurrentWorkAreaPointer(); + PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ); + ULONG ulPos,ulAdd = 0; + + if( pArea && pArea->dbfi.fOptimized ) + { + if( pArray ) + { + LPCDXTAG pTag; + + for ( ulPos = 1; ulPos <= hb_arrayLen( pArray ); ulPos++ ) + if ( ! BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, (ULONG) hb_arrayGetNL( pArray, ulPos ) ) ) + { + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, (ULONG) hb_arrayGetNL( pArray, ulPos ) ); + ulAdd++; + } + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag ) // Con índice activo + CURKEY_SETLOGCNT( pTag, (pTag->logKeyCount + ulAdd) ) + } + else + hb_errRT_DBCMD( EG_ARG, EDBCMD_BADPARAMETER, NULL, "BM_DBSETFILTERARRAYADD" ); + } + else + hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "BM_DBSETFILTERARRAYADD" ); +} + +HB_FUNC( BM_DBSETFILTERARRAYDEL ) +{ + AREAP pArea = (AREAP) hb_rddGetCurrentWorkAreaPointer(); + PHB_ITEM pArray = hb_param( 1, HB_IT_ARRAY ); + ULONG ulPos,ulDel = 0; + + if( pArea && pArea->dbfi.fOptimized ) + { + if( pArray ) + { + LPCDXTAG pTag; + + for ( ulPos = 1; ulPos <= hb_arrayLen( pArray ); ulPos++ ) + if ( BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, (ULONG) hb_arrayGetNL( pArray, ulPos ) ) ) + { + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, (ULONG) hb_arrayGetNL( pArray, ulPos ) ); + ulDel++; + } + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag ) // Con índice activo + CURKEY_SETLOGCNT( pTag, pTag->logKeyCount - ulDel ) + } + else + hb_errRT_DBCMD( EG_ARG, EDBCMD_BADPARAMETER, NULL, "BM_DBSETFILTERARRAYDEL" ); + } + else + hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "BM_DBSETFILTERARRAYDEL" ); +} + + +HB_FUNC( BM_DBSEEKWILD ) +{ + PHB_ITEM pKey; + BOOL bAll, bNext, bSoftSeek, bFindLast, fFound; + AREAP pArea = (AREAP) hb_rddGetCurrentWorkAreaPointer(); + + if( pArea ) + { + if( !ISNIL( 1 ) ) + { + pKey = hb_param( 1, HB_IT_ANY ); + bSoftSeek = ISLOG( 2 ) ? hb_parl( 2 ) : hb_set.HB_SET_SOFTSEEK; + bFindLast = ISLOG( 3 ) ? hb_parl( 3 ) : FALSE; + bNext = ISLOG( 4 ) ? hb_parl( 4 ) : FALSE; + bAll = ISLOG( 5 ) ? hb_parl( 5 ) : FALSE; + if ( bAll) + { + PHB_ITEM pList; + pList = hb_itemNew( NULL ); + hb_arrayNew( pList, 0 ); + SELF_GOTOP( ( AREAP ) pArea ); + if ( hb_cdxSeekWild( (CDXAREAP) pArea, bSoftSeek, pKey, bFindLast, FALSE, bAll ) == SUCCESS && + pArea->fEof == FALSE && + SELF_FOUND( pArea, &fFound ) == SUCCESS ) + { + hb_arrayAdd( pList, hb_itemPutNL( NULL, ((CDXAREAP) pArea)->ulRecNo ) ); + while ( hb_cdxSeekWild( (CDXAREAP) pArea, bSoftSeek, pKey, bFindLast, TRUE, bAll ) == SUCCESS && + pArea->fEof == FALSE && + SELF_FOUND( pArea, &fFound ) == SUCCESS ) + { + hb_arrayAdd( pList, hb_itemPutNL( NULL, ((CDXAREAP) pArea)->ulRecNo ) ); + } + } + hb_itemReturn( pList ); + hb_itemRelease( pList ); + return; + } + else + if( hb_cdxSeekWild( (CDXAREAP) pArea, bSoftSeek, pKey, bFindLast, bNext, bAll ) == SUCCESS ) + { + if( SELF_FOUND( pArea, &fFound ) == SUCCESS ) + { + hb_retl( fFound ); + return; + } + } + } + else + hb_errRT_DBCMD( EG_ARG, EDBCMD_SEEK_BADPARAMETER, NULL, "BM_DBSEEKWILD" ); + } + else + hb_errRT_DBCMD( EG_NOTABLE, EDBCMD_NOTABLE, NULL, "BM_DBSEEKWILD" ); + + hb_retl( FALSE ); + +} + +#endif + +/* ( DBENTRYP_L ) hb_cdxSkip : NULL */ +static ERRCODE hb_cdxSkip( CDXAREAP pArea, LONG lToSkip ) +{ + LPCDXTAG pTag; + ULONG ulPos, ulRec; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxSkip(%p, %ld)", pArea, lToSkip)); + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + pTag = lToSkip == 0 ? NULL : hb_cdxGetActiveTag( pArea ); + if( pTag && pArea->fPositioned && CURKEY_LOGPOS( pTag ) ) + { + ulPos = pTag->logKeyPos; + ulRec = pTag->logKeyRec; + } + else + { + ulPos = ulRec = 0; + } + + if ( SUPER_SKIP( ( AREAP ) pArea, lToSkip ) == FAILURE ) + return FAILURE; + + if ( pTag ) + { + if ( ulPos && ( pTag->logKeyPos != ulPos || pTag->logKeyRec != ulRec || + ( pTag->curKeyState & CDX_CURKEY_LOGPOS ) == 0 ) ) + { + ulPos = 0; + } + + if ( lToSkip > 0 ) + { + if ( pArea->fEof ) + { + if ( lToSkip == 1 && ulPos && !CURKEY_LOGCNT( pTag ) ) + CURKEY_SETLOGCNT( pTag, ulPos ) + } + else if ( ulPos ) + { + pTag->logKeyPos += lToSkip - ( ulPos ? 0 : 1 ); + pTag->logKeyRec = pArea->ulRecNo; + } + } + else if ( pArea->fBof ) + { + if ( pArea->fPositioned ) + { + pTag->logKeyPos = 1; + CURKEY_SETLOGPOS( pTag ); + } + } + else if ( ulPos ) + { + pTag->logKeyPos += lToSkip + ( ulPos ? 0 : 1 ); + pTag->logKeyRec = pArea->ulRecNo; + } + } + return SUCCESS; +} + +/* ( DBENTRYP_L ) hb_cdxSkipFilter */ +/* + * Reposition cursor respecting any filter setting. + */ +ERRCODE hb_cdxSkipFilter( CDXAREAP pArea, LONG lUpDown ) +{ + BOOL fBottom, fDeleted; + ERRCODE uiError; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxSkipFilter(%p, %ld)", pArea, lUpDown)); + + if( !hb_set.HB_SET_DELETED && pArea->dbfi.itmCobExpr == NULL ) + return SUCCESS; + + /* Since lToSkip is passed to SkipRaw, it should never request more than + a single skip. + The implied purpose of hb_cdxSkipFilter is to get off of a "bad" record + after a skip was performed, NOT to skip lToSkip filtered records. + */ + lUpDown = ( lUpDown < 0 ? -1 : 1 ); + + /* remember if we are here after SELF_GOTOP() */ + fBottom = pArea->fBottom; + + while( !pArea->fBof && !pArea->fEof ) + { + /* SET DELETED */ + if( hb_set.HB_SET_DELETED ) + { + if( SELF_DELETED( (AREAP) pArea, &fDeleted ) != SUCCESS ) + return FAILURE; + if( fDeleted ) + { + if( SELF_SKIPRAW( (AREAP) pArea, lUpDown ) != SUCCESS ) + return FAILURE; + continue; + } + } + + /* SET FILTER TO */ + if( pArea->dbfi.fFilter ) + { + if( ! hb_cdxCheckRecordFilter( pArea, pArea->ulRecNo ) ) + { + if( SELF_SKIPRAW( (AREAP) pArea, lUpDown ) != SUCCESS ) + return FAILURE; + continue; + } + } + + break; + } + + /* + * The only one situation when we should repos is backward skipping + * if we are at BOTTOM position (it's SKIPFILTER called from GOBOTTOM) + * then GOEOF() if not then GOTOP() + */ + if( pArea->fBof && lUpDown < 0 ) + { + if( fBottom ) + { + /* GOTO EOF (phantom) record - + this is the only one place where GOTO is used by xHarbour + directly and RDD which does not operate on numbers should + serve this method only as SELF_GOEOF() synonym. If it's a + problem then we can remove this if and always use SELF_GOTOP() + but it also means second table scan if all records filtered + are out of filter so I do not want to do that. I will prefer + explicit add SELF_GOEOF() method + */ + uiError = SELF_GOTO( (AREAP) pArea, 0 ); + } + else + { + uiError = SELF_GOTOP( (AREAP) pArea ); + pArea->fBof = TRUE; + } + } + else + { + uiError = SUCCESS; + } + + return uiError; +} + +/* ( DBENTRYP_L ) hb_cdxSkipRaw */ +static ERRCODE hb_cdxSkipRaw( CDXAREAP pArea, LONG lToSkip ) +{ + LPCDXTAG pTag; + ERRCODE retval; + BOOL fOut = FALSE, fForward; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxSkipRaw(%p, %ld)", pArea, lToSkip)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + pTag = hb_cdxGetActiveTag( pArea ); + + if ( ! pTag || lToSkip == 0 ) + return SUPER_SKIPRAW( ( AREAP ) pArea, lToSkip ); + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + fForward = ( lToSkip > 0 ); + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + if ( ! hb_cdxCurKeyRefresh( pArea, pTag ) ) + { + if ( fForward ) + { + if ( pTag->TagEOF || !hb_cdxBottomScope( pTag ) ) + fOut = TRUE; + else if ( pTag->UsrAscend && hb_cdxTopScope( pTag ) ) + lToSkip--; + } + else if ( pArea->fPositioned ) + { + if ( pTag->TagEOF || !hb_cdxTopScope( pTag ) ) + fOut = TRUE; + else if ( !pTag->UsrAscend && hb_cdxBottomScope( pTag ) ) + lToSkip++; + } + } + if ( fForward ) + { + if ( !fOut ) + { + while ( lToSkip-- > 0 ) + { + hb_cdxTagSkipNext( pTag ); + if ( pTag->TagEOF ) + { + fOut = TRUE; + break; + } + } + } + retval = SELF_GOTO( ( AREAP ) pArea, ( pTag->TagEOF || fOut ) + ? 0 : pTag->CurKey->rec ); + } + else /* if ( lToSkip < 0 ) */ + { + if ( fOut ) + hb_cdxTagGoTop( pTag ); + else + { + while ( lToSkip++ < 0 ) + { + hb_cdxTagSkipPrev( pTag ); + if ( pTag->TagBOF ) + { + fOut = TRUE; + break; + } + } + } + retval = SELF_GOTO( ( AREAP ) pArea, pTag->CurKey->rec ); + pArea->fBof = fOut; + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + /* Update Bof and Eof flags */ + /* + if( fForward ) + pArea->fBof = FALSE; + else + pArea->fEof = FALSE; + */ + return retval; +} + +/* ( DBENTRYP_VF ) hb_cdxAddField : NULL */ +/* ( DBENTRYP_B ) hb_cdxAppend */ +static ERRCODE hb_cdxAppend( CDXAREAP pArea, BOOL bUnLockAll ) +{ + if ( SUPER_APPEND( (AREAP) pArea, bUnLockAll ) == SUCCESS ) + { + if ( pArea->dbfi.fFilter && pArea->dbfi.fOptimized ) + { + ULONG ulRecCount, bytes; + + SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ); + bytes = ( (ulRecCount + 1) >> 5 ) + 1; + + if ( ( (ulRecCount) >> 5 ) + 1 < bytes ) + { + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap = (ULONG *) hb_xrealloc( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, bytes << 2 ); + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size = ulRecCount; + } + pArea->dbfi.fFilter = FALSE; + if ( hb_cdxCheckRecordFilter( pArea, ulRecCount ) ) + { + LPCDXTAG pTag; + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, ulRecCount ); + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag && CURKEY_LOGCNT(pTag) ) // Con índice activo + CURKEY_SETLOGCNT( pTag, (pTag)->logKeyCount + 1 ) + } + else + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, ulRecCount ); + pArea->dbfi.fFilter = TRUE; + } + return SUCCESS; + } + else + return FAILURE; +} +/* ( DBENTRYP_I ) hb_cdxCreateFields : NULL */ +/* ( DBENTRYP_V ) hb_cdxDeleteRec */ +static ERRCODE hb_cdxDeleteRec( CDXAREAP pArea ) +{ + if ( SUPER_DELETE( (AREAP) pArea ) == SUCCESS ) + { + if ( pArea->dbfi.fFilter && pArea->dbfi.fOptimized ) + { + pArea->dbfi.fFilter = FALSE; + if ( hb_cdxCheckRecordFilter( pArea, pArea->ulRecNo ) ) + { + if ( ! BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ) ) + { + LPCDXTAG pTag; + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag && CURKEY_LOGCNT(pTag) ) + CURKEY_SETLOGCNT( pTag, (pTag)->logKeyCount + 1 ) + } + } + else + { + if ( BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ) ) + { + LPCDXTAG pTag; + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + pTag = hb_cdxGetActiveTag( (CDXAREAP) pArea ); + if ( pTag && CURKEY_LOGCNT(pTag) ) + CURKEY_SETLOGCNT( pTag, (pTag)->logKeyCount - 1 ) + } + } + pArea->dbfi.fFilter = TRUE; + } + return SUCCESS; + } + else + return FAILURE; +} + +/* ( DBENTRYP_BP ) hb_cdxDeleted : NULL */ +/* ( DBENTRYP_SP ) hb_cdxFieldCount : NULL */ +/* ( DBENTRYP_VF ) hb_cdxFieldDisplay : NULL */ +/* ( DBENTRYP_SSI ) hb_cdxFieldInfo : NULL */ +/* ( DBENTRYP_SVP ) hb_cdxFieldName : NULL */ + +/* ( DBENTRYP_V ) hb_cdxFlush : NULL */ +/* + * Flush _system_ buffers to disk + */ +static ERRCODE hb_cdxFlush( CDXAREAP pArea ) +{ + LPCDXINDEX pIndex; + ERRCODE uiError; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxFlush(%p)", pArea)); + + if( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + uiError = SUPER_FLUSH( ( AREAP ) pArea ); + + if ( hb_set.HB_SET_HARDCOMMIT ) + { + pIndex = pArea->lpIndexes; + while ( pIndex ) + { + if ( pIndex->hFile != FS_ERROR && pIndex->fFlush ) + { + hb_fsCommit( pIndex->hFile ); + pIndex->fFlush = FALSE; + } + pIndex = pIndex->pNext; + } + } + + return uiError; +} + +/* ( DBENTRYP_PP ) hb_cdxGetRec : NULL */ +/* ( DBENTRYP_SI ) hb_cdxGetValue : NULL */ +/* ( DBENTRYP_SVL ) hb_cdxGetVarLen : NULL */ + +/* ( DBENTRYP_V ) hb_cdxGoCold */ +/* + * Perform a write of WorkArea memory to the data store. + */ +static ERRCODE hb_cdxGoCold( CDXAREAP pArea ) +{ + BOOL fRecordChanged = pArea->fRecordChanged; + BOOL fAppend = pArea->fAppend; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxGoCold(%p)", pArea)); + + if ( SUPER_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( ( fRecordChanged || pArea->fCdxAppend ) && pArea->lpIndexes ) + { + LPCDXTAG pTag = pArea->lpIndexes->TagList; + LPCDXKEY pKey = NULL; + BOOL fAdd, fDel, fLck = FALSE; + LPDBRELINFO lpdbPendingRel; + + if ( pArea->fShared ) + { + if ( fAppend ) + { + if ( pArea->fCdxAppend ) + hb_cdxErrInternal( "hb_cdxGoCold: multiple appending without GOCOLD." ); + pArea->fCdxAppend = TRUE; + return SUCCESS; + } + else + { + fAppend = pArea->fCdxAppend; + pArea->fCdxAppend = FALSE; + } + } + + /* The pending relation may move the record pointer so we should + disable them for KEY/FOR evaluation */ + lpdbPendingRel = pArea->lpdbPendingRel; + pArea->lpdbPendingRel = NULL; + + /* TODO: + * There is possible race condition here but not very dangerous. + * To avoid it we should Lock all index file before SUPER_GOCOLD + * but it makes other problem if two stations open the database index + * files in a differ order then they can block each other. + * Without changes in locking scheme we can do only one thing which + * is enough if there is only one index file: lock first index only + * before SUPER_GOCOLD + * Druzus, 05 Oct 2003 10:27:52 CEST + */ + + while ( pTag ) + { + if ( !pTag->Custom ) + { + pKey = hb_cdxKeyEval( pKey, pTag ); + + if ( pTag->pForItem != NULL ) + fAdd = hb_cdxEvalCond( pArea, pTag->pForItem, TRUE ); + else + fAdd = TRUE; + + if ( fAppend ) + fDel = FALSE; + else + { + if ( hb_cdxValCompare( pTag, pKey->val, pKey->len, + pTag->HotKey->val, pTag->HotKey->len, TRUE ) == 0 ) + { + fDel = !fAdd && pTag->HotFor; + fAdd = fAdd && !pTag->HotFor; + } + else + { + fDel = pTag->HotFor; + } + } + if ( fDel || fAdd ) + { + if ( !fLck ) + { + hb_cdxIndexLockWrite( pTag->pIndex ); + fLck = TRUE; + } + if ( fDel ) + hb_cdxTagKeyDel( pTag, pTag->HotKey ); + if ( fAdd ) + hb_cdxTagKeyAdd( pTag, pKey ); + } +#if 0 + if ( pTag->HotKey ) + { + hb_cdxKeyFree( pTag->HotKey ); + pTag->HotKey = NULL; + } +#endif + } + if ( pTag->pNext ) + pTag = pTag->pNext; + else + { + if ( fLck ) + { + hb_cdxIndexUnLockWrite( pTag->pIndex ); + fLck = FALSE; + } + if ( pTag->pIndex->pNext ) + pTag = pTag->pIndex->pNext->TagList; + else + pTag = NULL; + } + } + + if ( pKey ) + hb_cdxKeyFree( pKey ); + + /* Restore disabled pending relation */ + pArea->lpdbPendingRel = lpdbPendingRel; + } + + return SUCCESS; +} + +/* ( DBENTRYP_V ) hb_cdxGoHot */ +/* + * Mark the WorkArea data buffer as hot. + */ +static ERRCODE hb_cdxGoHot( CDXAREAP pArea ) +{ + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxGoHot(%p)", pArea)); + + if ( pArea->fRecordChanged ) + hb_cdxErrInternal( "hb_cdxGoHot: multiple marking buffer as hot." ); + + if ( SUPER_GOHOT( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( pArea->lpIndexes && !pArea->fCdxAppend ) + { + LPCDXTAG pTag = pArea->lpIndexes->TagList; + while ( pTag ) + { + if ( !pTag->Custom ) + { + pTag->HotKey = hb_cdxKeyEval( pTag->HotKey, pTag ); + pTag->HotFor = pTag->pForItem == NULL || hb_cdxEvalCond( pArea, pTag->pForItem, TRUE ); + } + + if ( pTag->pNext ) + pTag = pTag->pNext; + else + { + if ( pTag->pIndex->pNext ) + pTag = pTag->pIndex->pNext->TagList; + else + pTag = NULL; + } + } + } + return SUCCESS; +} + +/* ( DBENTRYP_P ) hb_cdxPutRec : NULL */ +static ERRCODE hb_cdxPutRec( CDXAREAP pArea, BYTE * pBuffer ) +{ + if ( SUPER_PUTREC( (AREAP) pArea, pBuffer ) == SUCCESS ) + { + if ( pArea->dbfi.fFilter && pArea->dbfi.fOptimized ) + { + pArea->dbfi.fFilter = FALSE; + + if ( hb_cdxCheckRecordFilter( pArea, pArea->ulRecNo ) ) + { + if ( ! BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ) ) + { + LPCDXTAG pTag; + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + pTag = hb_cdxGetActiveTag( pArea ); + if ( pTag ) + CURKEY_SETLOGCNT( pTag, pTag->logKeyCount + 1 ) + } + } + else + { + if ( BM_GetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ) ) + { + LPCDXTAG pTag; + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + pTag = hb_cdxGetActiveTag( pArea ); + if ( pTag ) + CURKEY_SETLOGCNT( pTag, pTag->logKeyCount - 1 ) + } + } + pArea->dbfi.fFilter = TRUE; + } + return SUCCESS; + } + else + return FAILURE; +} +/* ( DBENTRYP_SI ) hb_cdxPutValue : NULL */ +/* ( DBENTRYP_V ) hb_cdxRecall */ +static ERRCODE hb_cdxRecall( CDXAREAP pArea ) +{ + if ( SUPER_RECALL( (AREAP) pArea ) == SUCCESS ) + { + if ( pArea->dbfi.fFilter && pArea->dbfi.fOptimized ) + { + pArea->dbfi.fFilter = FALSE; + if ( hb_cdxCheckRecordFilter( pArea, pArea->ulRecNo ) ) + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + else + BM_ClrBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size, pArea->ulRecNo ); + pArea->dbfi.fFilter = TRUE; + } + return SUCCESS; + } + else + return FAILURE; +} + +/* ( DBENTRYP_ULP ) hb_cdxRecCount : NULL */ +/* ( DBENTRYP_ISI ) hb_cdxRecInfo : NULL */ +/* ( DBENTRYP_ULP ) hb_cdxRecNo : NULL */ +/* ( DBENTRYP_I ) hb_cdxRecId : NULL */ +/* ( DBENTRYP_S ) hb_cdxSetFieldExtent : NULL */ +/* ( DBENTRYP_P ) hb_cdxAlias : NULL */ + +/* ( DBENTRYP_V ) hb_cdxClose */ +/* + * Close the table in the WorkArea. + */ +static ERRCODE hb_cdxClose( CDXAREAP pArea ) +{ + ERRCODE errCode; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxClose(%p)", pArea)); + + if ( SELF_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + errCode = SUPER_CLOSE( ( AREAP ) pArea ); + + if( errCode == SUCCESS ) + { + if ( pArea->pSort ) + { + hb_cdxSortFree( pArea->pSort ); + pArea->pSort = NULL; + } + + hb_cdxOrdListClear( pArea, TRUE, NULL ); + if ( pArea->bCdxSortTab ) + { + hb_xfree( pArea->bCdxSortTab ); + pArea->bCdxSortTab = NULL; + } +#ifdef HB_CDX_DBGTIME + printf( "\r\ncdxTimeIntBld=%f, cdxTimeExtBld=%f, cdxTimeBld=%f\r\n" + "cdxTimeGetKey=%f, cdxTimeFreeKey=%f\r\n" + "cdxTimeExtBlc=%f, cdxTimeIntBlc=%f\r\n" + "cdxTimeIdxBld=%f\r\n" + "cdxTimeTotal=%f\r\n", + (double) cdxTimeIntBld / 1000000, (double) cdxTimeExtBld / 1000000, + (double) ( cdxTimeIntBld + cdxTimeExtBld ) / 1000000, + (double) cdxTimeGetKey / 1000000, (double) cdxTimeFreeKey / 1000000, + (double) cdxTimeIntBlc / 1000000, (double) cdxTimeExtBlc / 1000000, + (double) cdxTimeIdxBld / 1000000, + (double) ( cdxTimeIntBld + cdxTimeExtBld + cdxTimeIdxBld + + cdxTimeGetKey + cdxTimeFreeKey + + cdxTimeExtBlc + cdxTimeIntBlc ) / 1000000 ); + fflush(stdout); + cdxTimeIntBld = cdxTimeExtBld = 0; +#endif +#ifdef HB_CDX_DBGUPDT + printf( "\r\n#reads=%ld, #writes=%ld, stacksize=%d\r\n", cdxReadNO, cdxWriteNO, cdxStackSize ); + fflush(stdout); + cdxReadNO = cdxWriteNO = 0; +#endif + } + + return errCode; +} + +/* ( DBENTRYP_VP ) hb_cdxCreate : NULL */ +/* ( DBENTRYP_SI ) hb_cdxInfo : NULL */ +/* ( DBENTRYP_V ) hb_cdxNewArea : NULL */ + +/* ( DBENTRYP_VP ) hb_cdxOpen */ +/* + * Open a data store in the WorkArea. + */ +static ERRCODE hb_cdxOpen( CDXAREAP pArea, LPDBOPENINFO pOpenInfo ) +{ + ERRCODE errCode = SUCCESS; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOpen(%p, %p)", pArea, pOpenInfo)); + + if ( !pArea->bLockType ) + { + PHB_ITEM pItem = hb_itemNew( NULL ); + if( SELF_INFO( ( AREAP ) pArea, DBI_LOCKSCHEME, pItem ) != SUCCESS ) + { + hb_itemRelease( pItem ); + return FAILURE; + } + pArea->bLockType = hb_itemGetNI( pItem ); + hb_itemRelease( pItem ); + if( pArea->bLockType == 0 ) + { + pArea->bLockType = DB_DBFLOCK_VFP; + } + } + if ( SUPER_OPEN( ( AREAP ) pArea, pOpenInfo ) == FAILURE ) + { + return FAILURE; + } + + /* If SET_AUTOPEN open index */ +#ifdef HB_CDX_CLIP_AUTOPEN + if ( hb_set.HB_SET_AUTOPEN ) +#else + if ( pArea->fHasTags && hb_set.HB_SET_AUTOPEN ) +#endif + { + char szFileName[ _POSIX_PATH_MAX + 1 ]; + + pArea->fHasTags = FALSE; + hb_cdxCreateFName( pArea, NULL, NULL, szFileName, NULL ); + if ( hb_spFile( ( BYTE * ) szFileName, NULL ) ) + { + DBORDERINFO pOrderInfo; + + pOrderInfo.itmResult = hb_itemPutNI( NULL, 0 ); + pOrderInfo.atomBagName = hb_itemPutC( NULL, szFileName ); + pOrderInfo.itmNewVal = NULL; + pOrderInfo.itmOrder = NULL; + errCode = SELF_ORDLSTADD( ( AREAP ) pArea, &pOrderInfo ); + if( errCode == SUCCESS ) + { + pOrderInfo.itmOrder = hb_itemPutNI( NULL, hb_set.HB_SET_AUTORDER ); + errCode = SELF_ORDLSTFOCUS( ( AREAP ) pArea, &pOrderInfo ); + hb_itemRelease( pOrderInfo.itmOrder ); + if( errCode == SUCCESS ) + errCode = SELF_GOTOP( ( AREAP ) pArea ); + } + hb_itemRelease( pOrderInfo.atomBagName ); + hb_itemRelease( pOrderInfo.itmResult ); + } + } + else + { + pArea->fHasTags = FALSE; + } + + return errCode; +} + +/* ( DBENTRYP_V ) hb_cdxRelease : NULL */ + +/* ( DBENTRYP_SP ) hb_cdxStructSize */ +/* + * Retrieve the size of the WorkArea structure. + */ +static ERRCODE hb_cdxStructSize( CDXAREAP pArea, USHORT * uiSize ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_cdxStrucSize(%p, %p)", pArea, uiSize)); + HB_SYMBOL_UNUSED( pArea ); + + * uiSize = sizeof( CDXAREA ); + return SUCCESS; +} + +/* ( DBENTRYP_P ) hb_cdxSysName : NULL */ + +/* ( DBENTRYP_VEI ) hb_cdxEval : NULL */ + +/* ( DBENTRYP_V ) hb_cdxPack */ +static ERRCODE hb_cdxPack( CDXAREAP pArea ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_cdxPack(%p)", pArea )); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( SUPER_PACK( ( AREAP ) pArea ) == SUCCESS ) + { + return SELF_ORDLSTREBUILD( ( AREAP ) pArea ); + } + else + return FAILURE; +} + +/* ( DBENTRYP_LSP ) hb_cdxPackRec : NULL */ +/* ( DBENTRYP_VS ) hb_cdxSort : NULL */ +/* ( DBENTRYP_VT ) hb_cdxTrans : NULL */ +/* ( DBENTRYP_VT ) hb_cdxTransRec : NULL */ + +/* ( DBENTRYP_V ) hb_cdxZap */ +static ERRCODE hb_cdxZap ( CDXAREAP pArea ) +{ + HB_TRACE(HB_TR_DEBUG, ("nb_cdxZap(%p)", pArea )); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( SUPER_ZAP( ( AREAP ) pArea ) == SUCCESS ) + { + return SELF_ORDLSTREBUILD( ( AREAP ) pArea ); + } + else + return FAILURE; +} + +/* ( DBENTRYP_VR ) hb_cdxChildEnd : NULL */ +/* ( DBENTRYP_VR ) hb_cdxChildStart : NULL */ +/* ( DBENTRYP_VR ) hb_cdxChildSync : NULL */ +/* ( DBENTRYP_V ) hb_cdxSyncChildren : NULL */ +/* ( DBENTRYP_V ) hb_cdxClearRel : NULL */ +/* ( DBENTRYP_V ) hb_cdxForceRel : NULL */ +/* ( DBENTRYP_SVP ) hb_cdxRelArea : NULL */ +/* ( DBENTRYP_VR ) hb_cdxRelEval : NULL */ +/* ( DBENTRYP_SVP ) hb_cdxRelText : NULL */ +/* ( DBENTRYP_VR ) hb_cdxSetRel : NULL */ + +/* ( DBENTRYP_OI ) hb_cdxOrderListAdd */ +static ERRCODE hb_cdxOrderListAdd( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ) +{ + USHORT uiFlags; + FHANDLE hFile; + char szBaseName[ CDX_MAXTAGNAMELEN + 1 ]; + char szFileName[ _POSIX_PATH_MAX + 1 ]; + LPCDXINDEX pIndex, * pIndexPtr; + BOOL fProd, bRetry; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderListAdd(%p, %p)", pArea, pOrderInfo)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( hb_itemGetCLen( pOrderInfo->atomBagName ) == 0 ) + return FAILURE; + + hb_cdxCreateFName( pArea, hb_itemGetCPtr( pOrderInfo->atomBagName ), + &fProd, szFileName, szBaseName ); + +/* + if ( ! szBaseName[0] ) + return FAILURE; +*/ + pIndex = hb_cdxFindBag( pArea, szFileName ); + + if ( pIndex ) + { + /* index already open, do nothing */ + if ( ! pArea->uiTag ) + { + pArea->uiTag = hb_cdxGetTagNumber( pArea, pIndex->TagList ); + SELF_GOTOP( ( AREAP ) pArea ); + } + return SUCCESS; + } + + uiFlags = ( pArea->fReadonly ? FO_READ : FO_READWRITE ) | + ( pArea->fShared ? FO_DENYNONE : FO_EXCLUSIVE ); + do + { + hFile = hb_fsExtOpen( ( BYTE * ) szFileName, NULL, uiFlags | + FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME, + NULL, NULL ); + if( hFile == FS_ERROR ) + bRetry = ( hb_cdxErrorRT( pArea, EG_OPEN, EDBF_OPEN_INDEX, szFileName, + hb_fsError(), EF_CANRETRY | EF_CANDEFAULT ) == E_RETRY ); + else + { + if( hb_fsSeekLarge( hFile, 0, FS_END ) <= ( HB_FOFFSET ) sizeof( CDXTAGHEADER ) ) + { + hb_fsClose( hFile ); + hFile = FS_ERROR; + hb_cdxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, + szFileName, hb_fsError(), EF_CANDEFAULT ); + } + bRetry = FALSE; + } + + } while ( bRetry ); + + if ( hFile == FS_ERROR ) + { + return FAILURE; + } + + pIndex = hb_cdxIndexNew( pArea ); + pIndex->hFile = hFile; + pIndex->fShared = pArea->fShared; + pIndex->fReadonly = pArea->fReadonly; + pIndex->szFileName = hb_strdup( szFileName ); + + pIndexPtr = &pArea->lpIndexes; + while ( *pIndexPtr != NULL ) + pIndexPtr = &( *pIndexPtr )->pNext; + *pIndexPtr = pIndex; + + if ( ! hb_cdxIndexLoad( pIndex, szBaseName ) ) + { + /* index file is corrupted */ + *pIndexPtr = NULL; + hb_cdxIndexFree( pIndex ); + hb_cdxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, + szFileName, hb_fsError(), EF_CANDEFAULT ); + return FAILURE; + } + + if( fProd ) + pArea->fHasTags = TRUE; + + /* bmdbfcdx specific: If there was no controlling order, set this one. + * This is the behaviour of Clipper's bmdbfcdx, although + * Clipper doc says a different rule + */ + if ( ! pArea->uiTag ) + { + pArea->uiTag = hb_cdxGetTagNumber( pArea, pIndex->TagList ); + SELF_GOTOP( ( AREAP ) pArea ); + } + return SUCCESS; +} + +/* ( DBENTRYP_V ) hb_cdxOrderListClear */ +/* + * Clear the current order list. + */ +static ERRCODE hb_cdxOrderListClear( CDXAREAP pArea ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderListClear(%p)", pArea)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + +#ifdef HB_CDX_CLIP_AUTOPEN + hb_cdxOrdListClear( pArea, !hb_set.HB_SET_AUTOPEN, NULL ); +#else + hb_cdxOrdListClear( pArea, !pArea->fHasTags || !hb_set.HB_SET_AUTOPEN, NULL ); +#endif + pArea->uiTag = 0; + + return SUCCESS; +} + +/* TODO: in the future, now there is no API call to SELF_ORDLSTDELETE */ +/* ( DBENTRYP_OI ) hb_cdxOrderListDelete : NULL */ + +/* ( DBENTRYP_OI ) hb_cdxOrderListFocus */ +static ERRCODE hb_cdxOrderListFocus( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ) +{ + LPCDXTAG pTag; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderListFocus(%p, %p)", pArea, pOrderInfo)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( ! pArea->lpIndexes ) + return SUCCESS; + + pTag = hb_cdxGetActiveTag( pArea ); + if ( pTag ) + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag->szName ); + + if ( pOrderInfo->itmOrder ) + { + hb_cdxFindTag( pArea, pOrderInfo->itmOrder, pOrderInfo->atomBagName, &(pArea->uiTag) ); + /* TODO: RTerror if not found? */ + } + + return SUCCESS; +} + +/* ( DBENTRYP_V ) hb_cdxOrderListRebuild */ +static ERRCODE hb_cdxOrderListRebuild( CDXAREAP pArea ) +{ + LPCDXINDEX pIndex, * pIndexPtr; + USHORT uiPrevTag; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxPack(%p)", pArea )); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( pArea->fShared ) + { + hb_cdxErrorRT( pArea, EG_SHARED, EDBF_SHARED, pArea->szDataFileName, 0, 0 ); + return FAILURE; + } + if ( pArea->fReadonly ) + { + hb_cdxErrorRT( pArea, EG_READONLY, EDBF_READONLY, pArea->szDataFileName, 0, 0 ); + return FAILURE; + } + + if ( ! pArea->lpIndexes ) + return SUCCESS; + + uiPrevTag = pArea->uiTag; + pArea->uiTag = 0; + + pIndex = pArea->lpIndexes; + pArea->lpIndexes = NULL; + pIndexPtr = &pArea->lpIndexes; + while ( pIndex ) + { + (*pIndexPtr) = pIndex; + pIndex = pIndex->pNext; + (*pIndexPtr)->pNext = NULL; + hb_cdxIndexReindex( *pIndexPtr ); + pIndexPtr = &(*pIndexPtr)->pNext; + } + + pArea->uiTag = uiPrevTag; + /* Clear pArea->lpdbOrdCondInfo */ + SELF_ORDSETCOND( ( AREAP ) pArea, NULL ); + + return SELF_GOTOP( ( AREAP ) pArea ); +} + +/* ( DBENTRYP_VOI ) hb_cdxOrderCondition : NULL */ + +/* ( DBENTRYP_VOC ) hb_cdxOrderCreate */ +/* + * create new order + */ +static ERRCODE hb_cdxOrderCreate( CDXAREAP pArea, LPDBORDERCREATEINFO pOrderInfo ) +{ + ULONG ulRecNo; + BOOL fNewFile, fOpenedIndex, fProd, fAscend = TRUE, fCustom = FALSE, + fTemporary = FALSE, fExclusive = FALSE; + PHB_ITEM pKeyExp, pForExp = NULL, pResult; + char szCpndTagName[ CDX_MAXTAGNAMELEN + 1 ], szTagName[ CDX_MAXTAGNAMELEN + 1 ]; + char szFileName[ _POSIX_PATH_MAX + 1 ], szTempFile[ _POSIX_PATH_MAX + 1 ]; + char *szFor = NULL; + LPCDXINDEX pIndex; + LPCDXTAG pTag; + USHORT uiLen; + BYTE bType; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderCreate(%p, %p)", pArea, pOrderInfo)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + /* If we have a codeblock for the expression, use it */ + if ( pOrderInfo->itmCobExpr ) + pKeyExp = hb_itemNew( pOrderInfo->itmCobExpr ); + else /* Otherwise, try compiling the key expression string */ + { + if( SELF_COMPILE( (AREAP) pArea, ( BYTE * ) hb_itemGetCPtr( pOrderInfo->abExpr ) ) == FAILURE ) + return FAILURE; + pKeyExp = pArea->valResult; + pArea->valResult = NULL; + } + + /* Get a blank record before testing expression */ + ulRecNo = pArea->ulRecNo; + SELF_GOTO( ( AREAP ) pArea, 0 ); + if ( SELF_EVALBLOCK( ( AREAP ) pArea, pKeyExp ) == FAILURE ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + return FAILURE; + } + pResult = pArea->valResult; + pArea->valResult = NULL; + + bType = hb_cdxItemType( pResult ); + switch ( bType ) + { + case 'N': + case 'D': + uiLen = 8; + break; + case 'L': + uiLen = 1; + break; + case 'C': + uiLen = HB_CDXMAXKEY( hb_itemGetCLen( pResult ) ); + break; + default: + bType = 'U'; + uiLen = 0; + } + hb_itemRelease( pResult ); + + if ( bType == 'U' || uiLen == 0 ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + hb_cdxErrorRT( pArea, bType == 'U' ? EG_DATATYPE : EG_DATAWIDTH, + 1026, NULL, 0, 0 ); + return FAILURE; + } + if ( pArea->lpdbOrdCondInfo ) + { + fAscend = !pArea->lpdbOrdCondInfo->fDescending; + fCustom = pArea->lpdbOrdCondInfo->fCustom; + fTemporary = pArea->lpdbOrdCondInfo->fTemporary; + fExclusive = pArea->lpdbOrdCondInfo->fExclusive; + + /* Check conditional expression */ + szFor = ( char * ) pArea->lpdbOrdCondInfo->abFor; + if ( pArea->lpdbOrdCondInfo->itmCobFor ) + /* If we have a codeblock for the conditional expression, use it */ + pForExp = hb_itemNew( pArea->lpdbOrdCondInfo->itmCobFor ); + else if ( pArea->lpdbOrdCondInfo->abFor ) + { + /* Otherwise, try compiling the conditional expression string */ + if ( SELF_COMPILE( (AREAP) pArea, pArea->lpdbOrdCondInfo->abFor ) == FAILURE ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + return FAILURE; + } + pForExp = pArea->valResult; + pArea->valResult = NULL; + } + } + /* Test conditional expression */ + if ( pForExp ) + { + BOOL fOK; + + if ( SELF_EVALBLOCK( ( AREAP ) pArea, pForExp ) == FAILURE ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + hb_vmDestroyBlockOrMacro( pForExp ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + return FAILURE; + } + fOK = hb_itemType( pArea->valResult ) == HB_IT_LOGICAL; + hb_itemRelease( pArea->valResult ); + pArea->valResult = NULL; + if ( ! fOK ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + hb_vmDestroyBlockOrMacro( pForExp ); + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + hb_cdxErrorRT( pArea, EG_DATATYPE, EDBF_INVALIDFOR, NULL, 0, 0 ); + return FAILURE; + } + } + + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + + /* + * abBagName -> cBag, atomBagName -> cTag + * The following scheme implemented: + * 1. abBagName == NULL -> add the Tag to the structural index + * 2. atomBagName == NULL -> overwrite any index file of abBagName + * 3. ads the Tag to index file + */ + + hb_cdxCreateFName( pArea, ( char * ) pOrderInfo->abBagName, + &fProd, szFileName, szCpndTagName ); + + if ( pOrderInfo->atomBagName && pOrderInfo->atomBagName[0] ) + { + hb_strncpyUpperTrim( szTagName, ( char * ) pOrderInfo->atomBagName, CDX_MAXTAGNAMELEN ); + fNewFile = FALSE; + } + else + { + hb_strncpy( szTagName, szCpndTagName, CDX_MAXTAGNAMELEN ); + fNewFile = TRUE; + } + + if ( !pArea->lpdbOrdCondInfo || + ( pArea->lpdbOrdCondInfo->fAll && !pArea->lpdbOrdCondInfo->fAdditive ) ) +#ifdef HB_CDX_CLIP_AUTOPEN + hb_cdxOrdListClear( pArea, !hb_set.HB_SET_AUTOPEN, NULL ); +#else + hb_cdxOrdListClear( pArea, !pArea->fHasTags || !hb_set.HB_SET_AUTOPEN, NULL ); +#endif + + pIndex = hb_cdxFindBag( pArea, szFileName ); + + if ( fNewFile && pIndex != NULL ) + { + LPCDXINDEX * pIndexPtr = &pArea->lpIndexes; + while ( *pIndexPtr ) + { + if ( pIndex == *pIndexPtr ) + { + *pIndexPtr = pIndex->pNext; + break; + } + pIndexPtr = &(*pIndexPtr)->pNext; + } + hb_cdxIndexFree( pIndex ); + pIndex = NULL; + } + fOpenedIndex = ( pIndex != NULL ); + + if ( !fOpenedIndex ) + { + FHANDLE hFile; + BOOL bRetry, fShared = pArea->fShared && !fTemporary && !fExclusive; + + do + { + if( fTemporary ) + { + hFile = hb_fsCreateTemp( NULL, NULL, FC_NORMAL, ( BYTE * ) szTempFile ); + fNewFile = TRUE; + } + else + { + hFile = hb_fsExtOpen( ( BYTE * ) szFileName, NULL, FO_READWRITE | + ( fShared ? FO_DENYNONE : FO_EXCLUSIVE ) | + ( fNewFile ? FXO_TRUNCATE : FXO_APPEND ) | + FXO_DEFAULTS | FXO_SHARELOCK | FXO_COPYNAME, + NULL, NULL ); + } + if( hFile == FS_ERROR ) + bRetry = ( hb_cdxErrorRT( pArea, EG_CREATE, EDBF_CREATE, szFileName, + hb_fsError(), EF_CANRETRY | EF_CANDEFAULT ) == E_RETRY ); + else + { + bRetry = FALSE; + if ( !fNewFile ) + fNewFile = ( hb_fsSeekLarge( hFile, 0, FS_END ) == 0 ); + } + + } while ( bRetry ); + + if ( hFile != FS_ERROR ) + { + pIndex = hb_cdxIndexNew( pArea ); + pIndex->hFile = hFile; + pIndex->fShared = fShared; + pIndex->fReadonly = FALSE; + pIndex->szFileName = hb_strdup( szFileName ); + pIndex->fDelete = fTemporary; + if( fTemporary ) + pIndex->szRealName = hb_strdup( szTempFile ); + + if ( !fNewFile ) + { + /* index file is corrupted? */ + if ( ! hb_cdxIndexLoad( pIndex, szCpndTagName ) ) + { + /* TODO: What should be default? */ + /* + hb_cdxIndexFree( pIndex ); + hb_fsClose( hFile ); + hFile = FS_ERROR; + hb_cdxErrorRT( pArea, EG_CORRUPTION, EDBF_CORRUPT, + szFileName, hb_fsError(), EF_CANDEFAULT ); + */ + hb_cdxIndexFreeTags( pIndex ); + fNewFile = TRUE; + } + } + } + + if ( hFile == FS_ERROR ) + { + hb_vmDestroyBlockOrMacro( pKeyExp ); + if ( pForExp != NULL ) + hb_vmDestroyBlockOrMacro( pForExp ); + return FAILURE; + } + } + + hb_cdxIndexLockWrite( pIndex ); + if ( !fNewFile ) + { + /* Delete new tag if exist */ + hb_cdxIndexDelTag( pIndex, szTagName ); + fNewFile = ( pIndex->TagList == NULL ); + } + + if ( fNewFile ) + { + hb_fsSeek( pIndex->hFile, 0, FS_SET ); + hb_fsWrite( pIndex->hFile, NULL, 0 ); + pIndex->fChanged = TRUE; + hb_cdxIndexDropAvailPage( pIndex ); + if ( pIndex->pCompound != NULL ) + hb_cdxTagFree( pIndex->pCompound ); + pIndex->nextAvail = pIndex->freePage = 0; + hb_cdxIndexCreateStruct( pIndex, szCpndTagName ); + } + + pTag = hb_cdxIndexAddTag( pIndex, szTagName, hb_itemGetCPtr( pOrderInfo->abExpr ), + pKeyExp, bType, uiLen, szFor, pForExp, + fAscend , pOrderInfo->fUnique, fCustom, FALSE ); + + if ( pArea->lpdbOrdCondInfo && ( !pArea->lpdbOrdCondInfo->fAll && + !pArea->lpdbOrdCondInfo->fAdditive ) ) + { +#ifdef HB_CDX_CLIP_AUTOPEN + hb_cdxOrdListClear( pArea, !hb_set.HB_SET_AUTOPEN, pIndex ); +#else + hb_cdxOrdListClear( pArea, !pArea->fHasTags || !hb_set.HB_SET_AUTOPEN, pIndex ); +#endif + } + hb_cdxIndexUnLockWrite( pIndex ); + /* Update DBF header */ + if( !pArea->fHasTags && !fOpenedIndex && !pIndex->fDelete && fProd ) + { + pArea->fHasTags = TRUE; + if ( !pArea->fReadonly && ( pArea->dbfHeader.bHasTags & 0x01 ) == 0 ) + { +#ifdef HB_CDX_CLIP_AUTOPEN + if ( hb_set.HB_SET_AUTOPEN ) +#endif + SELF_WRITEDBHEADER( ( AREAP ) pArea ); + } + } + else + { + fProd = FALSE; + } + + if( !fOpenedIndex ) + { + if ( fProd || pArea->lpIndexes == NULL ) + { + pIndex->pNext = pArea->lpIndexes; + pArea->lpIndexes = pIndex; + } + else + { + LPCDXINDEX pIndexTmp = pArea->lpIndexes; + while ( pIndexTmp->pNext ) + pIndexTmp = pIndexTmp->pNext; + pIndexTmp->pNext = pIndex; + } + } + + pArea->uiTag = hb_cdxGetTagNumber( pArea, pTag ); + + /* Clear pArea->lpdbOrdCondInfo */ + SELF_ORDSETCOND( ( AREAP ) pArea, NULL ); + + return SELF_GOTOP( ( AREAP ) pArea ); +} + +/* ( DBENTRYP_OI ) hb_cdxOrderDestroy */ +static ERRCODE hb_cdxOrderDestroy( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ) +{ + LPCDXINDEX pIndex, pIndexTmp; + LPCDXTAG pTag; + USHORT uiTag; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderDestroy(%p, %p)", pArea, pOrderInfo)); + + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if ( ! pArea->lpIndexes ) + return SUCCESS; + + if ( pOrderInfo->itmOrder ) + { + pTag = hb_cdxFindTag( pArea, pOrderInfo->itmOrder, pOrderInfo->atomBagName, &uiTag ); + if ( pTag ) + { + pIndex = pTag->pIndex; + if ( /* !pIndex->fShared && */ !pIndex->fReadonly ) + { + hb_cdxIndexLockWrite( pIndex ); + hb_cdxIndexDelTag( pIndex, pTag->szName ); + hb_cdxIndexUnLockWrite( pIndex ); + if ( !pIndex->TagList ) + { + if ( pArea->lpIndexes == pIndex ) + { + pArea->lpIndexes = pIndex->pNext; + if ( pArea->fHasTags ) + { + pArea->fHasTags = FALSE; + if ( !pArea->fReadonly && ( pArea->dbfHeader.bHasTags & 0x01 ) != 0 ) + { +#ifdef HB_CDX_CLIP_AUTOPEN + if ( hb_set.HB_SET_AUTOPEN ) +#endif + SELF_WRITEDBHEADER( ( AREAP ) pArea ); + } + } + } + else + { + pIndexTmp = pArea->lpIndexes; + while ( pIndexTmp->pNext && ( pIndexTmp->pNext != pIndex ) ) + { + pIndexTmp = pIndexTmp->pNext; + } + if ( pIndexTmp->pNext == pIndex ) + { + pIndexTmp->pNext = pIndex->pNext; + } + } + pIndex->fDelete = TRUE; + hb_cdxIndexFree( pIndex ); + } + if( uiTag < pArea->uiTag ) + pArea->uiTag--; + else if( uiTag == pArea->uiTag ) + pArea->uiTag = 0; + } + else + { + /* TODO: allow this operation for shared mode? */ + hb_errInternal( 1023, "hb_cdxOrderDestroy: exclusive required.", "", "" ); + } + } + } + return SUCCESS; +} + +/* ( DBENTRYP_OII ) hb_cdxOrderInfo */ +/* + * Provides information about order management. + */ +static ERRCODE hb_cdxOrderInfo( CDXAREAP pArea, USHORT uiIndex, LPDBORDERINFO pOrderInfo ) +{ + LPCDXTAG pTag; + USHORT uiTag = 0; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxOrderInfo(%p, %hu, %p)", pArea, uiIndex, pOrderInfo)); + + switch( uiIndex ) + { + case DBOI_STRICTREAD: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_STRICTREAD, 0, pOrderInfo->itmResult ); + + case DBOI_OPTIMIZE: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_OPTIMIZE, 0, pOrderInfo->itmResult ); + + case DBOI_AUTOOPEN: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_AUTOOPEN, 0, pOrderInfo->itmResult ); + + case DBOI_AUTOORDER: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_AUTOORDER, 0, pOrderInfo->itmResult ); + + case DBOI_AUTOSHARE: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_AUTOSHARE, 0, pOrderInfo->itmResult ); + + case DBOI_BAGEXT: + if( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + else + pOrderInfo->itmResult = hb_itemNew( NULL ); + return SELF_RDDINFO( SELF_RDDNODE( pArea ), RDDI_ORDBAGEXT, 0, pOrderInfo->itmResult ); + + case DBOI_EVALSTEP: + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + pArea->lpdbOrdCondInfo ? pArea->lpdbOrdCondInfo->lStep : 0 ); + return SUCCESS; + + case DBOI_KEYSINCLUDED: + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + pArea->pSort ? pArea->pSort->ulTotKeys : 0 ); + return SUCCESS; + + case DBOI_I_TAGNAME: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, + pArea->pSort ? pArea->pSort->pTag->szName : NULL ); + return SUCCESS; + + case DBOI_I_BAGNAME: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, + pArea->pSort ? pArea->pSort->pTag->pIndex->szFileName : NULL ); + return SUCCESS; + + case DBOI_ISREINDEX: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + pArea->pSort ? pArea->pSort->fReindex : FALSE ); + return SUCCESS; + + case DBOI_LOCKOFFSET: + case DBOI_HPLOCKING: + { + HB_FOFFSET ulPos, ulPool; + + hb_dbfLockIdxGetData( pArea->bLockType, &ulPos, &ulPool ); + if ( uiIndex == DBOI_LOCKOFFSET ) + pOrderInfo->itmResult = hb_itemPutNInt( pOrderInfo->itmResult, ulPos ); + else + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, ulPool > 0 ); + return SUCCESS; + } + + case DBOI_ORDERCOUNT: + { + LPCDXINDEX pIndex; + char *pszBag = hb_itemGetCLen( pOrderInfo->atomBagName ) > 0 ? + hb_itemGetCPtr( pOrderInfo->atomBagName ) : NULL; + pIndex = pszBag ? hb_cdxFindBag( pArea, pszBag ) : pArea->lpIndexes; + while ( pIndex ) + { + pTag = pIndex->TagList; + while ( pTag ) + { + ++uiTag; + pTag = pTag->pNext; + } + pIndex = pszBag ? NULL : pIndex->pNext; + } + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, uiTag ); + return SUCCESS; + } + + case DBOI_BAGCOUNT: + { + LPCDXINDEX pIndex = pArea->lpIndexes; + while ( pIndex ) + { + ++uiTag; + pIndex = pIndex->pNext; + } + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, uiTag ); + return SUCCESS; + } + + case DBOI_BAGNUMBER: + { + LPCDXINDEX pIndex = pArea->lpIndexes, pIndexSeek; + + if( hb_itemGetCLen( pOrderInfo->atomBagName ) > 0 ) + pIndexSeek = hb_cdxFindBag( pArea, + hb_itemGetCPtr( pOrderInfo->atomBagName ) ); + else + pIndexSeek = pIndex; + + if( pIndexSeek ) + { + ++uiTag; + do + { + if( pIndex == pIndexSeek ) + break; + ++uiTag; + pIndex = pIndex->pNext; + } + while ( pIndex ); + } + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, + pIndex ? uiTag : 0 ); + return SUCCESS; + } + + case DBOI_BAGORDER: + { + LPCDXINDEX pIndex = pArea->lpIndexes, pIndexSeek; + + if( hb_itemGetCLen( pOrderInfo->atomBagName ) > 0 ) + pIndexSeek = hb_cdxFindBag( pArea, + hb_itemGetCPtr( pOrderInfo->atomBagName ) ); + else + pIndexSeek = pIndex; + + if( pIndexSeek ) + { + ++uiTag; + do + { + if( pIndex == pIndexSeek ) + break; + pTag = pIndex->TagList; + while ( pTag ) + { + ++uiTag; + pTag = pTag->pNext; + } + pIndex = pIndex->pNext; + } + while ( pIndex ); + } + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, + pIndex ? uiTag : 0 ); + return SUCCESS; + } + } + + if( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + if( pOrderInfo->itmOrder ) + { + pTag = hb_cdxFindTag( pArea, pOrderInfo->itmOrder, pOrderInfo->atomBagName, &uiTag ); + } + else + { + uiTag = pArea->uiTag; + pTag = hb_cdxGetTagByNumber( pArea, uiTag ); + } + + switch( uiIndex ) + { + case DBOI_CONDITION: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, ( pTag ? pTag->ForExpr : "" ) ); + if ( pTag && pOrderInfo->itmNewVal && HB_IS_STRING( pOrderInfo->itmNewVal ) ) + { + if ( pTag->ForExpr != NULL ) + { + hb_xfree( pTag->ForExpr ); + pTag->ForExpr = NULL; + } + if ( pTag->pForItem != NULL ) + { + hb_vmDestroyBlockOrMacro( pTag->pForItem ); + pTag->pForItem = NULL; + } + if ( hb_itemGetCLen( pOrderInfo->itmNewVal ) > 0 ) + { + char * pForExpr = hb_itemGetCPtr( pOrderInfo->itmNewVal ); + + if ( SELF_COMPILE( ( AREAP ) pArea, ( BYTE *) pForExpr ) == SUCCESS ) + { + PHB_ITEM pForItem = pArea->valResult; + pArea->valResult = NULL; + if ( SELF_EVALBLOCK( ( AREAP ) pArea, pForItem ) == SUCCESS ) + { + if ( hb_itemType( pArea->valResult ) == HB_IT_LOGICAL ) + { + pTag->pForItem = pForItem; + pForItem = NULL; + } + hb_itemRelease( pArea->valResult ); + pArea->valResult = NULL; + } + if ( pForItem ) + hb_vmDestroyBlockOrMacro( pForItem ); + } + } + } + break; + + case DBOI_EXPRESSION: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag ? pTag->KeyExpr : "" ); + break; + + case DBOI_POSITION: + if ( pOrderInfo->itmNewVal && HB_IS_NUMERIC( pOrderInfo->itmNewVal ) ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOIKeyGoto( pArea, pTag, + hb_itemGetNL( pOrderInfo->itmNewVal ), TRUE ) == SUCCESS ); + } + else + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + hb_cdxDBOIKeyNo( pArea, pTag, TRUE ) ); + break; + + /* TODO: is this ok? DBOI_RECNO == DBOI_KEYNORAW ? No, it isn't. */ + /* case DBOI_RECNO: */ + case DBOI_KEYNORAW: + if ( pOrderInfo->itmNewVal && HB_IS_NUMERIC( pOrderInfo->itmNewVal ) ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOIKeyGoto( pArea, pTag, + hb_itemGetNL( pOrderInfo->itmNewVal ), FALSE ) == SUCCESS ); + } + else + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + hb_cdxDBOIKeyNo( pArea, pTag, FALSE ) ); + break; + + case DBOI_KEYCOUNT: + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + hb_cdxDBOIKeyCount( pArea, pTag, TRUE ) ); + break; + + case DBOI_KEYCOUNTRAW: + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + hb_cdxDBOIKeyCount( pArea, pTag, FALSE ) ); + break; + + case DBOI_RELKEYPOS: + if ( pOrderInfo->itmNewVal && HB_IS_NUMERIC( pOrderInfo->itmNewVal ) ) + hb_cdxDBOISetRelKeyPos( pArea, pTag, + hb_itemGetND( pOrderInfo->itmNewVal ) ); + else + pOrderInfo->itmResult = hb_itemPutND( pOrderInfo->itmResult, + hb_cdxDBOIGetRelKeyPos( pArea, pTag ) ); + break; + + case DBOI_FINDREC: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOIFindRec( pArea, pTag, + hb_itemGetNL( pOrderInfo->itmNewVal ), FALSE ) ); + break; + + case DBOI_FINDRECCONT: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOIFindRec( pArea, pTag, + hb_itemGetNL( pOrderInfo->itmNewVal ), TRUE ) ); + break; + + case DBOI_SKIPUNIQUE: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipUnique( pArea, pTag, + hb_itemGetNI( pOrderInfo->itmNewVal ) >= 0 ) == SUCCESS ); + break; + + case DBOI_SKIPEVAL: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipEval( pArea, pTag, TRUE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SKIPEVALBACK: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipEval( pArea, pTag, FALSE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SKIPWILD: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipWild( pArea, pTag, TRUE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SKIPWILDBACK: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipWild( pArea, pTag, FALSE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SKIPREGEX: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipRegEx( pArea, pTag, TRUE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SKIPREGEXBACK: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxDBOISkipRegEx( pArea, pTag, FALSE, pOrderInfo->itmNewVal ) ); + break; + + case DBOI_SCOPEEVAL: + if ( pTag && pOrderInfo->itmNewVal && + hb_arrayLen( pOrderInfo->itmNewVal ) == DBRMI_SIZE && + hb_arrayGetPtr( pOrderInfo->itmNewVal, DBRMI_FUNCTION ) != NULL ) + { + pOrderInfo->itmResult = hb_itemPutNL( pOrderInfo->itmResult, + hb_cdxDBOIScopeEval( pTag, ( HB_EVALSCOPE_FUNC ) + hb_arrayGetPtr( pOrderInfo->itmNewVal, DBRMI_FUNCTION ), + hb_arrayGetPtr( pOrderInfo->itmNewVal, DBRMI_PARAM ), + hb_arrayGetItemPtr( pOrderInfo->itmNewVal, DBRMI_LOVAL ), + hb_arrayGetItemPtr( pOrderInfo->itmNewVal, DBRMI_HIVAL ) ) ); + } + else + { + /* TODO: RT error */ + ; + } + break; + + case DBOI_NAME: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag ? pTag->szName : "" ); + break; + + case DBOI_NUMBER: + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, uiTag ); + break; + + case DBOI_BAGNAME: + if ( pTag ) + { + PHB_FNAME pFileName = hb_fsFNameSplit( pTag->pIndex->szFileName ); + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pFileName->szName ); + hb_xfree( pFileName ); + } + else + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, "" ); + break; + + case DBOI_FULLPATH: + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, pTag ? pTag->pIndex->szFileName : "" ); + break; + + case DBOI_FILEHANDLE: + pOrderInfo->itmResult = hb_itemPutNInt( pOrderInfo->itmResult, pTag ? pTag->pIndex->hFile : FS_ERROR ); + break; + + case DBOI_ISCOND: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, pTag && pTag->ForExpr != NULL ); + break; + + case DBOI_ISDESC: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, pTag && !pTag->UsrAscend ); + if ( pTag && pOrderInfo->itmNewVal && HB_IS_LOGICAL( pOrderInfo->itmNewVal ) ) + { + pTag->UsrAscend = ! hb_itemGetL( pOrderInfo->itmNewVal ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS ); + } + break; + + case DBOI_UNIQUE: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, ( pTag ? pTag->UniqueKey || pTag->UsrUnique : FALSE ) ); + if ( pTag && pOrderInfo->itmNewVal && HB_IS_LOGICAL( pOrderInfo->itmNewVal ) && !pTag->UniqueKey ) + { + pTag->UsrUnique = hb_itemGetL( pOrderInfo->itmNewVal ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_LOGPOS | + CDX_CURKEY_RAWCNT | CDX_CURKEY_LOGCNT ); + } + break; + + case DBOI_KEYTYPE: + if ( pTag ) + { + char szType[2]; + szType[0] = (char) pTag->uiType; + szType[1] = 0; + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, szType ); + } + else + pOrderInfo->itmResult = hb_itemPutC( pOrderInfo->itmResult, "" ); + break; + + case DBOI_KEYSIZE: + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, pTag ? pTag->uiLen : 0 ); + break; + + case DBOI_KEYDEC: + /* there is no fixed number of decimal places for numeric keys + in CDX format */ + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, 0 ); + break; + + case DBOI_KEYVAL: + hb_itemClear( pOrderInfo->itmResult ); + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + if ( pTag && pArea->fPositioned ) + { + if ( pTag->CurKey->rec != pArea->ulRecNo ) + { + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxCurKeyRefresh( pArea, pTag ); + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + if ( pTag->CurKey->rec == pArea->ulRecNo ) + pOrderInfo->itmResult = hb_cdxKeyGetItem( pTag->CurKey, + pOrderInfo->itmResult, pTag, TRUE ); + } + break; + + case DBOI_SCOPETOP: + if ( pTag ) + { + if ( pOrderInfo->itmResult ) + hb_cdxTagGetScope( pTag, 0, pOrderInfo->itmResult ); + if ( pOrderInfo->itmNewVal ) + hb_cdxTagSetScope( pTag, 0, pOrderInfo->itmNewVal ); + } + else if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_SCOPEBOTTOM: + if ( pTag ) + { + if ( pOrderInfo->itmResult ) + hb_cdxTagGetScope( pTag, 1, pOrderInfo->itmResult ); + if ( pOrderInfo->itmNewVal ) + hb_cdxTagSetScope( pTag, 1, pOrderInfo->itmNewVal ); + } + else if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_SCOPESET: + if ( pTag ) + { + if ( pOrderInfo->itmNewVal ) + { + hb_cdxTagSetScope( pTag, 0, pOrderInfo->itmNewVal ); + hb_cdxTagSetScope( pTag, 1, pOrderInfo->itmNewVal ); + } + } + if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_SCOPETOPCLEAR: + if ( pTag ) + { + if ( pOrderInfo->itmResult ) + hb_cdxTagGetScope( pTag, 0, pOrderInfo->itmResult ); + hb_cdxTagClearScope( pTag, 0 ); + } + else if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_SCOPEBOTTOMCLEAR: + if ( pTag ) + { + if ( pOrderInfo->itmResult ) + hb_cdxTagGetScope( pTag, 1, pOrderInfo->itmResult ); + hb_cdxTagClearScope( pTag, 1 ); + } + else if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_SCOPECLEAR: + if ( pTag ) + { + hb_cdxTagClearScope( pTag, 0 ); + hb_cdxTagClearScope( pTag, 1 ); + } + if ( pOrderInfo->itmResult ) + hb_itemClear( pOrderInfo->itmResult ); + break; + + case DBOI_CUSTOM: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, ( pTag ? pTag->Custom : FALSE ) ); + if ( pOrderInfo->itmNewVal && HB_IS_LOGICAL( pOrderInfo->itmNewVal ) + && hb_itemGetL( pOrderInfo->itmNewVal ) ) + { + pTag->Custom = TRUE; + } + break; + + /* TODO: */ + /* + case DBOI_TEMPLATE: + case DBOI_MULTIKEY: + case DBOI_PARTIAL: + case DBOI_CHGONLY: + */ + + case DBOI_KEYADD: + if ( !pTag ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + } + else + { + if ( pTag->Custom ) + { + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + if( !pArea->fPositioned || + ( pTag->pForItem && + !hb_cdxEvalCond( pArea, pTag->pForItem, TRUE ) ) ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + } + else + { + LPCDXKEY pKey; + hb_cdxIndexLockWrite( pTag->pIndex ); + if ( pOrderInfo->itmNewVal && !HB_IS_NIL( pOrderInfo->itmNewVal ) ) + pKey = hb_cdxKeyPutItem( NULL, pOrderInfo->itmNewVal, pArea->ulRecNo, pTag, TRUE, TRUE ); + else + pKey = hb_cdxKeyEval( NULL, pTag ); + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxTagKeyAdd( pTag, pKey ) ); + hb_cdxIndexUnLockWrite( pTag->pIndex ); + hb_cdxKeyFree( pKey ); + } + } + else + { + hb_cdxErrorRT( pArea, 0, 1052, NULL, 0, 0 ); + } + } + break; + + case DBOI_KEYDELETE: + if ( !pTag ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + } + else + { + if ( pTag->Custom ) + { + if( pArea->lpdbPendingRel ) + SELF_FORCEREL( ( AREAP ) pArea ); + + if( !pArea->fPositioned || + ( pTag->pForItem && + !hb_cdxEvalCond( pArea, pTag->pForItem, TRUE ) ) ) + { + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + } + else + { + LPCDXKEY pKey; + hb_cdxIndexLockWrite( pTag->pIndex ); + if ( pOrderInfo->itmNewVal && !HB_IS_NIL( pOrderInfo->itmNewVal ) ) + pKey = hb_cdxKeyPutItem( NULL, pOrderInfo->itmNewVal, pArea->ulRecNo, pTag, TRUE, TRUE ); + else + pKey = hb_cdxKeyEval( NULL, pTag ); + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + hb_cdxTagKeyDel( pTag, pKey ) ); + hb_cdxIndexUnLockWrite( pTag->pIndex ); + hb_cdxKeyFree( pKey ); + } + } + else + { + hb_cdxErrorRT( pArea, 0, 1052, NULL, 0, 0 ); + } + } + break; + + case DBOI_READLOCK: + if( pTag ) + { + if( hb_itemType( pOrderInfo->itmNewVal ) == HB_IT_LOGICAL ) + { + if( hb_itemGetL( pOrderInfo->itmNewVal ) ) + hb_cdxIndexLockRead( pTag->pIndex ); + else + hb_cdxIndexUnLockRead( pTag->pIndex ); + } + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + pTag->pIndex->lockRead > 0 ); + } + else + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + break; + + case DBOI_WRITELOCK: + if( pTag ) + { + if( hb_itemType( pOrderInfo->itmNewVal ) == HB_IT_LOGICAL ) + { + if( hb_itemGetL( pOrderInfo->itmNewVal ) ) + hb_cdxIndexLockWrite( pTag->pIndex ); + else + hb_cdxIndexUnLockWrite( pTag->pIndex ); + } + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + pTag->pIndex->lockWrite > 0 ); + } + else + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + break; + + case DBOI_UPDATECOUNTER: + if( pTag ) + { + /* refresh update counter */ + if( hb_cdxIndexLockRead( pTag->pIndex ) ) + hb_cdxIndexUnLockRead( pTag->pIndex ); + pOrderInfo->itmResult = hb_itemPutNInt( pOrderInfo->itmResult, + pTag->pIndex->ulVersion ); + } + else + pOrderInfo->itmResult = hb_itemPutNI( pOrderInfo->itmResult, 0 ); + break; + + case DBOI_SHARED: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + pTag && pTag->pIndex->fShared ); + break; + + case DBOI_ISREADONLY: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, + pTag && pTag->pIndex->fReadonly ); + break; + + case DBOI_ISMULTITAG: + case DBOI_ISSORTRECNO: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, pTag != NULL ); + break; + + case DBOI_LARGEFILE: + pOrderInfo->itmResult = hb_itemPutL( pOrderInfo->itmResult, FALSE ); + break; + + default: + return SUPER_ORDINFO( ( AREAP ) pArea, uiIndex, pOrderInfo ); + + } + return SUCCESS; +} + +/* ( DBENTRYP_V ) hb_cdxClearFilter */ +static ERRCODE hb_cdxClearFilter( CDXAREAP pArea ) +{ + hb_cdxClearLogPosInfo( pArea ); + // Limpiamos filtro tipo array + if ( pArea->dbfi.lpvCargo ) + { + hb_xfree( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap ); + hb_xfree( pArea->dbfi.lpvCargo ); + pArea->dbfi.lpvCargo = NULL; + } + return SUPER_CLEARFILTER( ( AREAP ) pArea ); +} + +/* ( DBENTRYP_V ) hb_cdxClearLocate : NULL */ +/* ( DBENTRYP_V ) hb_cdxClearScope : NULL */ + +/* ( DBENTRYP_VPLP ) hb_cdxCountScope */ +static ERRCODE hb_cdxCountScope( CDXAREAP pArea, void * pPtr, LONG * plRec ) +{ + HB_TRACE(HB_TR_DEBUG, ("hb_cdxCountScope(%p, %p, %p)", pArea, pPtr, plRec)); + + if ( pPtr == NULL ) + { + return SUCCESS; + } + return SUPER_COUNTSCOPE( ( AREAP ) pArea, pPtr, plRec ); +} + +/* ( DBENTRYP_I ) hb_cdxFilterText : NULL */ +/* ( DBENTRYP_SI ) hb_cdxScopeInfo : NULL */ + +/* ( DBENTRYP_VFI ) hb_cdxSetFilter */ +static ERRCODE hb_cdxSetFilter( CDXAREAP pArea, LPDBFILTERINFO pFilterInfo ) +{ + ULONG ulRecCount = 0, ulLogKeyCount = 0; + LPCDXTAG pTag; + PHB_ITEM pResult; + + HB_SYMBOL_UNUSED( ulLogKeyCount ); + + hb_cdxClearLogPosInfo( pArea ); + + if ( SUPER_SETFILTER( ( AREAP ) pArea, pFilterInfo ) != SUCCESS ) + return FAILURE; + + pArea->dbfi.fOptimized = hb_set.HB_SET_OPTIMIZE; + + if ( pArea->dbfi.fOptimized ) + { + pArea->dbfi.lpvCargo = hb_xgrab( sizeof( BM_FILTER ) ); + memset( pArea->dbfi.lpvCargo, 0, sizeof( BM_FILTER ) ); + + pArea->dbfi.fFilter = FALSE; + + pTag = hb_cdxGetActiveTag( pArea ); + SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ); + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->Size = ulRecCount; + ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap = (ULONG *) hb_xgrab( sizeof(ULONG) * (((ulRecCount+1) >> 5) + 1 ) ); + memset( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, 0, sizeof(ULONG) * (((ulRecCount+1) >> 5) + 1 ) ); + + if ( pTag ) // with active index + { + if ( FAST_GOCOLD( ( AREAP ) pArea ) == FAILURE ) + return FAILURE; + + hb_cdxIndexLockRead( pTag->pIndex ); + hb_cdxTagRefreshScope( pTag ); + hb_cdxTagGoTop( pTag ); + ulLogKeyCount = 0; + while ( !pTag->TagEOF ) + { + if( pArea->ulRecNo != pTag->CurKey->rec || pArea->lpdbPendingRel ) + SELF_GOTO( (AREAP) pArea, pTag->CurKey->rec ); + pResult = hb_vmEvalBlock( pArea->dbfi.itmCobExpr ); + if ( HB_IS_LOGICAL( pResult ) && hb_itemGetL( pResult ) ) + { + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ulRecCount, pTag->CurKey->rec ); + ulLogKeyCount++; + } + hb_cdxTagSkipNext( pTag ); + } + hb_cdxIndexUnLockRead( pTag->pIndex ); + pTag->curKeyState &= ~( CDX_CURKEY_RAWPOS | CDX_CURKEY_RAWCNT ); + CURKEY_SETLOGCNT( pTag, ulLogKeyCount ); + } + else + { + SELF_GOTOP( ( AREAP ) pArea ); + while ( ! pArea->fEof ) + { + pResult = hb_vmEvalBlock( pArea->dbfi.itmCobExpr ); + if ( HB_IS_LOGICAL( pResult ) && hb_itemGetL( pResult ) ) + BM_SetBit( ( ( LPBM_FILTER ) pArea->dbfi.lpvCargo)->rmap, ulRecCount, (pArea)->ulRecNo ); + SELF_SKIP( ( AREAP ) pArea, 1 ); + } + } + pArea->dbfi.fFilter = TRUE; + } + + return SUCCESS; +} + +/* ( DBENTRYP_VLO ) hb_cdxSetLocate : NULL */ +/* ( DBENTRYP_VOS ) hb_cdxSetScope : NULL */ +/* ( DBENTRYP_VPL ) hb_cdxSkipScope : NULL */ +/* ( DBENTRYP_B ) hb_cdxLocate : NULL */ + +/* ( DBENTRYP_P ) hb_cdxCompile : NULL */ +/* ( DBENTRYP_I ) hb_cdxError : NULL */ +/* ( DBENTRYP_I ) hb_cdxEvalBlock : NULL */ + +/* ( DBENTRYP_VSP ) hb_cdxRawLock : NULL */ +/* ( DBENTRYP_VL ) hb_cdxLock : NULL */ +/* ( DBENTRYP_UL ) hb_cdxUnLock : NULL */ + +/* ( DBENTRYP_V ) hb_cdxCloseMemFile : NULL */ +/* ( DBENTRYP_VP ) hb_cdxCreateMemFile : NULL */ +/* ( DBENTRYP_SVPB ) hb_cdxGetValueFile : NULL */ +/* ( DBENTRYP_VP ) hb_cdxOpenMemFile : NULL */ +/* ( DBENTRYP_SVPB ) hb_cdxPutValueFile : NULL */ + +/* ( DBENTRYP_V ) hb_cdxReadDBHeader : NULL */ +/* ( DBENTRYP_V ) hb_cdxWriteDBHeader : NULL */ +/* ( DBENTRYP_SVP ) hb_cdxWhoCares : NULL */ + +/* + * Retrieve (set) information about RDD + * ( DBENTRYP_RSLV ) hb_fptFieldInfo + */ +static ERRCODE hb_cdxRddInfo( LPRDDNODE pRDD, USHORT uiIndex, ULONG ulConnect, PHB_ITEM pItem ) +{ + LPDBFDATA pData; + + HB_TRACE(HB_TR_DEBUG, ("hb_cdxRddInfo(%p, %hu, %lu, %p)", pRDD, uiIndex, ulConnect, pItem)); + + pData = ( LPDBFDATA ) pRDD->lpvCargo; + + switch( uiIndex ) + { + case RDDI_ORDBAGEXT: + case RDDI_ORDEREXT: + case RDDI_ORDSTRUCTEXT: + { + char * szNew = hb_itemGetCPtr( pItem ); + + if( szNew[0] == '.' && szNew[1] ) + szNew = hb_strdup( szNew ); + else + szNew = NULL; + + hb_itemPutC( pItem, pData->szIndexExt[ 0 ] ? pData->szIndexExt : CDX_INDEXEXT ); + if( szNew ) + { + hb_strncpy( pData->szIndexExt, szNew, HB_MAX_FILE_EXT ); + hb_xfree( szNew ); + } + break; + } + + case RDDI_MULTIKEY: + case RDDI_MULTITAG: + case RDDI_SORTRECNO: + case RDDI_STRUCTORD: + case RDDI_STRICTSTRUCT: + hb_itemPutL( pItem, TRUE ); + break; + + default: + return SUPER_RDDINFO( pRDD, uiIndex, ulConnect, pItem ); + + } + + return SUCCESS; +} + + + +/* ######################################################################### */ + +static int hb_cdxQuickSortCompare( LPCDXSORTINFO pSort, BYTE * pKey1, BYTE * pKey2 ) +{ + int i, iLen = pSort->keyLen; + + i = hb_cdxValCompare( pSort->pTag, pKey1, iLen, pKey2, iLen, TRUE ); + + if ( i == 0 ) + { + i = ( HB_GET_LE_UINT32( pKey1 + iLen ) < HB_GET_LE_UINT32( pKey2 + iLen ) ) ? -1 : 1; + } + + return i; +} + +static BOOL hb_cdxQSort( LPCDXSORTINFO pSort, BYTE * pSrc, BYTE * pBuf, LONG lKeys ) +{ + if ( lKeys > 1 ) + { + int iLen = pSort->keyLen + 4; + LONG l1, l2; + BYTE * pPtr1, * pPtr2, *pDst; + BOOL f1, f2; + + l1 = lKeys >> 1; + l2 = lKeys - l1; + pPtr1 = &pSrc[ 0 ]; + pPtr2 = &pSrc[ l1 * iLen ]; + + f1 = hb_cdxQSort( pSort, pPtr1, &pBuf[ 0 ], l1 ); + f2 = hb_cdxQSort( pSort, pPtr2, &pBuf[ l1 * iLen ], l2 ); + if ( f1 ) + { + pDst = pBuf; + } + else + { + pDst = pSrc; + pPtr1 = &pBuf[ 0 ]; + } + if ( !f2 ) + { + pPtr2 = &pBuf[ l1 * iLen ]; + } + while ( l1 > 0 && l2 > 0 ) + { + if ( hb_cdxQuickSortCompare( pSort, pPtr1, pPtr2 ) <= 0 ) + { + memcpy( pDst, pPtr1, iLen ); + pPtr1 += iLen; + l1--; + } + else + { + memcpy( pDst, pPtr2, iLen ); + pPtr2 += iLen; + l2--; + } + pDst += iLen; + } + if ( l1 > 0 ) + { + memcpy( pDst, pPtr1, iLen * l1 ); + } + else if ( l2 > 0 && f1 == f2 ) + { + memcpy( pDst, pPtr2, iLen * l2 ); + } + return !f1; + } + return TRUE; +} + +static void hb_cdxSortSortPage( LPCDXSORTINFO pSort ) +{ + ULONG ulSize = pSort->ulKeys * ( pSort->keyLen + 4 ); +#ifdef HB_CDX_DBGTIME + cdxTimeIdxBld -= hb_cdxGetTime(); +#endif + if ( !hb_cdxQSort( pSort, pSort->pKeyPool, &pSort->pKeyPool[ ulSize ], pSort->ulKeys ) ) + { + memcpy( pSort->pKeyPool, &pSort->pKeyPool[ ulSize ], ulSize ); + } +#ifdef HB_CDX_DBGTIME + cdxTimeIdxBld += hb_cdxGetTime(); +#endif +} + +static void hb_cdxSortAddNodeKey( LPCDXSORTINFO pSort, int iLevel, BYTE *pKeyVal, ULONG ulRec, ULONG ulPage ) +{ + LPCDXPAGE pPage; + BOOL fNew; + int iLen = pSort->keyLen, iDup = 0, iTrl = 0, iTmp, iPos; + BYTE *pTmp; + + pPage = pSort->NodeList[ iLevel ]; + if ( iLevel == 0 ) + { + while ( iTrl < iLen && pKeyVal[ iLen - iTrl - 1 ] == pSort->bTrl ) + { + iTrl++; + } + if ( pPage != NULL && pPage->iKeys > 0 ) + { +#ifdef HB_CDX_PACKTRAIL + int iMax = iLen - iTrl; +#else + int iMax = iLen - HB_MAX( iTrl, pSort->iLastTrl ); +#endif + while ( pKeyVal[ iDup ] == pSort->pLastKey[ iDup ] && iDup < iMax ) + { + iDup++; + } + } +#ifndef HB_CDX_PACKTRAIL + pSort->iLastTrl = iTrl; +#endif + } + if( pPage == NULL ) + { + fNew = TRUE; + } + else + { + if ( iLevel == 0 ) + { + fNew = ( pPage->iFree - ( iLen - iDup - iTrl ) - pPage->ReqByte ) < 0; + } + else + { + fNew = ( pSort->NodeList[ iLevel ]->iKeys >= pSort->pTag->MaxKeys ); + } + } + + if( fNew ) + { + pPage = hb_cdxPageNew( pSort->pTag, NULL, 0 ); + pPage->PageType = ( iLevel == 0 ) ? CDX_NODE_LEAF : CDX_NODE_BRANCH; + if ( iLevel == 0 ) + { + hb_cdxPageLeafInitSpace( pPage ); + iDup = 0; + while ( pSort->ulMaxRec > pPage->RNMask ) + { + pPage->ReqByte++; + pPage->RNBits += 8; + pPage->RNMask = ( pPage->RNMask << 8 ) | 0xFF; + } + } + if ( pSort->NodeList[ iLevel ] != NULL ) + { + pSort->NodeList[ iLevel ]->Right = pPage->Page; + pPage->Left = pSort->NodeList[ iLevel ]->Page; + if ( iLevel == 0 ) + { +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckDupTrlRaw( pSort->NodeList[ iLevel ] ); +#endif + hb_cdxSortAddNodeKey( pSort, iLevel + 1, pSort->pLastKey, pSort->ulLastRec, pSort->NodeList[ iLevel ]->Page ); + } + else + { + iPos = ( pSort->NodeList[ iLevel ]->iKeys - 1 ) * ( iLen + 8 ); + pTmp = &pSort->NodeList[ iLevel ]->node.intNode.keyPool[ iPos ]; + hb_cdxSortAddNodeKey( pSort, iLevel + 1, pTmp, HB_GET_BE_UINT32( &pTmp[ iLen ] ), pSort->NodeList[ iLevel ]->Page ); + } + hb_cdxPageFree( pSort->NodeList[ iLevel ], TRUE ); + } + pSort->NodeList[ iLevel ] = pPage; + } + if ( iLevel == 0 ) + { + iPos = pPage->iKeys * pPage->ReqByte; + hb_cdxSetLeafRecord( &pPage->node.extNode.keyPool[ iPos ], + ulRec, iDup, iTrl, + pPage->ReqByte, pPage->DCBits, pPage->TCBits ); + iTmp = iLen - iDup - iTrl; + if ( iTmp > 0 ) + { + memcpy( &pPage->node.extNode.keyPool[ pPage->iFree + iPos - iTmp ], + &pKeyVal[ iDup ], iTmp ); + } + pPage->iFree -= iTmp + pPage->ReqByte; + pPage->iKeys++; +#ifdef HB_CDX_DBGCODE_EXT + hb_cdxPageCheckDupTrlRaw( pSort->NodeList[ iLevel ] ); +#endif + } + else + { + pPage = pSort->NodeList[ iLevel ]; + iPos = pPage->iKeys * ( iLen + 8 ); + pTmp = &pPage->node.intNode.keyPool[ iPos ]; + memcpy( pTmp, pKeyVal, iLen ); + HB_PUT_BE_UINT32( &pTmp[ iLen ], ulRec ); + HB_PUT_BE_UINT32( &pTmp[ iLen + 4 ], ulPage ); + pPage->iKeys++; + } +} + +static void hb_cdxSortWritePage( LPCDXSORTINFO pSort ) +{ + ULONG ulSize = pSort->ulKeys * ( pSort->keyLen + 4 ); + + hb_cdxSortSortPage( pSort ); + + if ( pSort->hTempFile == FS_ERROR ) + { + BYTE szName[ _POSIX_PATH_MAX + 1 ]; + pSort->hTempFile = hb_fsCreateTemp( NULL, NULL, FC_NORMAL, szName ); + if ( pSort->hTempFile == FS_ERROR ) + { + hb_errInternal( 9999, "hb_cdxSortWritePage: Can't create temporary file.", "", "" ); + } + pSort->szTempFileName = hb_strdup( ( char * ) szName ); + } + pSort->pSwapPage[ pSort->ulCurPage ].ulKeys = pSort->ulKeys; + pSort->pSwapPage[ pSort->ulCurPage ].nOffset = hb_fsSeekLarge( pSort->hTempFile, 0, FS_END ); + if ( hb_fsWriteLarge( pSort->hTempFile, pSort->pKeyPool, ulSize ) != ulSize ) + { + hb_errInternal( 9999, "hb_cdxSortWritePage: Write error in temporary file.", "", "" ); + } + pSort->ulKeys = 0; + pSort->ulCurPage++; +} + +static void hb_cdxSortGetPageKey( LPCDXSORTINFO pSort, ULONG ulPage, + BYTE ** pKeyVal, ULONG *pulRec ) +{ + int iLen = pSort->keyLen; + + if ( pSort->pSwapPage[ ulPage ].ulKeyBuf == 0 ) + { + ULONG ulKeys = HB_MIN( pSort->ulPgKeys, pSort->pSwapPage[ ulPage ].ulKeys ); + ULONG ulSize = ulKeys * ( iLen + 4 ); + + if ( hb_fsSeekLarge( pSort->hTempFile, pSort->pSwapPage[ ulPage ].nOffset, SEEK_SET ) != pSort->pSwapPage[ ulPage ].nOffset || + hb_fsReadLarge( pSort->hTempFile, pSort->pSwapPage[ ulPage ].pKeyPool, ulSize ) != ulSize ) + { + hb_errInternal( 9999, "hb_cdxSortGetPageKey: Read error from temporary file.", "", "" ); + } + pSort->pSwapPage[ ulPage ].nOffset += ulSize; + pSort->pSwapPage[ ulPage ].ulKeyBuf = ulKeys; + pSort->pSwapPage[ ulPage ].ulCurKey = 0; + } + *pKeyVal = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ]; + *pulRec = HB_GET_LE_UINT32( *pKeyVal + iLen ); +} + +#ifdef HB_CDX_NEW_SORT +static void hb_cdxSortOrderPages( LPCDXSORTINFO pSort ) +{ + int iLen = pSort->keyLen, i; + LONG l, r, m; + ULONG n, ulPage, ulRec; + BYTE *pKey = NULL, *pTmp; + + pSort->ulFirst = 0; + pSort->pSortedPages = ( ULONG * ) hb_xgrab( pSort->ulPages * sizeof( ULONG ) ); + pSort->pSortedPages[ 0 ] = 0; + + if ( pSort->ulTotKeys > 0 ) + { + for ( n = 0; n < pSort->ulPages; n++ ) + { + hb_cdxSortGetPageKey( pSort, n, &pKey, &ulRec ); + l = 0; + r = n - 1; + while ( l <= r ) + { + m = ( l + r ) >> 1; + ulPage = pSort->pSortedPages[ m ]; + pTmp = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ]; + i = hb_cdxValCompare( pSort->pTag, pKey, iLen, pTmp, iLen, TRUE ); + if ( i == 0 ) + i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1; + if ( i > 0 ) + l = m + 1; + else + r = m - 1; + } + for ( r = n; r > l; r-- ) + pSort->pSortedPages[ r ] = pSort->pSortedPages[ r - 1 ]; + pSort->pSortedPages[ l ] = n; + } + } +} + +static BOOL hb_cdxSortKeyGet( LPCDXSORTINFO pSort, BYTE ** pKeyVal, ULONG *pulRec ) +{ + int iLen = pSort->keyLen, i; + LONG l, r, m; + ULONG ulPage; + + ulPage = pSort->pSortedPages[ pSort->ulFirst ]; + + /* check if first page has some keys yet */ + if ( pSort->pSwapPage[ ulPage ].ulKeys > 0 ) + { + BYTE *pKey, *pTmp; + ULONG ulRec; + + /* + * last key was taken from this page - we have to resort it. + * This is done intentionally here to be sure that the key + * value return by this function will not be overwritten by + * next keys in page read from temporary file in function + * hb_cdxSortGetPageKey() - please do not move this part down + * even it seems to be correct + */ + hb_cdxSortGetPageKey( pSort, ulPage, &pKey, &ulRec ); + + l = pSort->ulFirst + 1; + r = pSort->ulPages - 1; + while ( l <= r ) + { + m = ( l + r ) >> 1; + ulPage = pSort->pSortedPages[ m ]; + pTmp = &pSort->pSwapPage[ ulPage ].pKeyPool[ pSort->pSwapPage[ ulPage ].ulCurKey * ( iLen + 4 ) ]; + i = hb_cdxValCompare( pSort->pTag, pKey, iLen, pTmp, iLen, TRUE ); + if ( i == 0 ) + i = ( ulRec < HB_GET_LE_UINT32( &pTmp[ iLen ] ) ) ? -1 : 1; + + if ( i > 0 ) + l = m + 1; + else + r = m - 1; + } + if ( l > ( LONG ) pSort->ulFirst + 1 ) + { + ulPage = pSort->pSortedPages[ pSort->ulFirst ]; + for ( r = pSort->ulFirst + 1; r < l; r++ ) + pSort->pSortedPages[ r - 1 ] = pSort->pSortedPages[ r ]; + pSort->pSortedPages[ l - 1 ] = ulPage; + } + } + else + { + pSort->ulFirst++; + } + if ( pSort->ulFirst < pSort->ulPages ) + { + ulPage = pSort->pSortedPages[ pSort->ulFirst ]; + hb_cdxSortGetPageKey( pSort, ulPage, pKeyVal, pulRec ); + pSort->pSwapPage[ ulPage ].ulCurKey++; + pSort->pSwapPage[ ulPage ].ulKeys--; + pSort->pSwapPage[ ulPage ].ulKeyBuf--; + return TRUE; + } + return FALSE; +} + +#else + +static BOOL hb_cdxSortKeyGet( LPCDXSORTINFO pSort, BYTE ** pKeyVal, ULONG *pulRec ) +{ + int i, iLen = pSort->keyLen; + ULONG ulPage, ulKeyPage = 0, ulRec = 0, ulRecTmp; + BYTE *pKey = NULL, *pTmp; + + for ( ulPage = 0; ulPage < pSort->ulPages; ulPage++ ) + { + if ( pSort->pSwapPage[ ulPage ].ulKeys > 0 ) + { + hb_cdxSortGetPageKey( pSort, ulPage, &pTmp, &ulRecTmp ); + if ( ! pKey ) + { + i = 1; + } + else + { + i = hb_cdxValCompare( pSort->pTag, pKey, iLen, pTmp, iLen, TRUE ); + if ( i == 0 ) + { + i = ( ulRec < ulRecTmp ) ? -1 : 1; + } + } + if ( i > 0 ) + { + pKey = pTmp; + ulRec = ulRecTmp; + ulKeyPage = ulPage; + } + } + } + if ( pKey ) + { + pSort->pSwapPage[ ulKeyPage ].ulCurKey++; + pSort->pSwapPage[ ulKeyPage ].ulKeys--; + pSort->pSwapPage[ ulKeyPage ].ulKeyBuf--; + *pulRec = ulRec; + *pKeyVal = pKey; + return TRUE; + } + return FALSE; +} + +#endif + +static void hb_cdxSortKeyAdd( LPCDXSORTINFO pSort, ULONG ulRec, BYTE * pKeyVal, int iKeyLen ) +{ + int iLen = pSort->keyLen; + BYTE *pDst; + + if ( pSort->ulKeys >= pSort->ulPgKeys ) + { + hb_cdxSortWritePage( pSort ); + } + pDst = &pSort->pKeyPool[ pSort->ulKeys * ( iLen + 4 ) ]; + + if ( iLen > iKeyLen ) + { + memcpy( pDst, pKeyVal, iKeyLen ); + memset( &pDst[ iKeyLen ], pSort->bTrl, iLen - iKeyLen ); + } + else + { + memcpy( pDst, pKeyVal, iLen ); + } + HB_PUT_LE_UINT32( &pDst[ iLen ], ulRec ); + pSort->ulKeys++; + pSort->ulTotKeys++; +} + +static LPCDXSORTINFO hb_cdxSortNew( LPCDXTAG pTag, ULONG ulRecCount ) +{ + LPCDXSORTINFO pSort; + BYTE * pBuf; + int iLen = pTag->uiLen; + ULONG ulSize, ulMax, ulMin; + + if ( ulRecCount == 0 ) + ulRecCount = 1; + + pSort = ( LPCDXSORTINFO ) hb_xgrab( sizeof( CDXSORTINFO ) ); + memset( pSort, 0, sizeof( CDXSORTINFO ) ); + ulMax = ulMin = ( ULONG ) ceil( sqrt( ( double ) ulRecCount ) ); + ulSize = ( 1L << 20 ) / ( iLen + 4 ); + while ( ulMax < ulSize ) + ulMax <<= 1; + if ( ulMax > ulRecCount ) + ulMax = ulRecCount; + + do + { + ulSize = ulMax * ( iLen + 4 ); + pBuf = ( BYTE * ) hb_xalloc( ulSize << 2 ); + if ( pBuf ) + { + hb_xfree( pBuf ); + pBuf = ( BYTE * ) hb_xalloc( ulSize << 1 ); + } + else + { + ulMax >>= 1; + } + } + while ( ! pBuf && ulMax >= ulMin ); + + if ( ! pBuf ) + { + /* call hb_xgrab() to force out of memory error, + * though in multi process environment this call may return + * with success when other process free some memory + * (also the size of buf is reduced to absolute minimum). + * Sorry but I'm to lazy to implement indexing with smaller + * memory though it's possible - just simply I can even create + * index on-line by key adding like in normal update process. + * The memory necessary to index file is now ~ + * ~ (keySize+4+sizeof(CDXSWAPPAGE)) * sqrt(ulRecCount) * 2 + * so the maximum is for DBF with 2^32 records and keySize 240 ~ + * ~ 2^17 * 268 ~=~ 35 Mb + * this is not a problem for current computers and I do not see + * any way to use DBFs with four billions records and indexes with + * such long (240 bytes) keys on the old ones - they will be simply + * to slow. IMHO it's also better to signal out of memory here and + * force some system upgrades then run process which will have to + * take many hours, Druzus. + */ + ulMax = ulMin; + pBuf = ( BYTE * ) hb_xgrab( ( ulMax << 1 ) * ( iLen + 4 ) ); + } + + pSort->pTag = pTag; + pSort->hTempFile = FS_ERROR; + pSort->keyLen = iLen; + pSort->bTrl = pTag->bTrail; + pSort->fUnique = pTag->UniqueKey; + pSort->ulMaxKey = ulMax << 1; + pSort->ulPgKeys = ulMax; + pSort->ulMaxRec = ulRecCount; + pSort->pKeyPool = pBuf; + pSort->ulPages = ( ulRecCount + pSort->ulPgKeys - 1 ) / pSort->ulPgKeys; + pSort->pSwapPage = ( LPCDXSWAPPAGE ) hb_xgrab( sizeof( CDXSWAPPAGE ) * pSort->ulPages ); + memset( pSort->pSwapPage, 0, sizeof( CDXSWAPPAGE ) * pSort->ulPages ); + + return pSort; +} + +static void hb_cdxSortFree( LPCDXSORTINFO pSort ) +{ + if ( pSort->hTempFile != FS_ERROR ) + { + hb_fsClose( pSort->hTempFile ); + } + if ( pSort->szTempFileName ) + { + hb_fsDelete( (BYTE *) ( pSort->szTempFileName ) ); + hb_xfree( pSort->szTempFileName ); + } + if ( pSort->pKeyPool ) + { + hb_xfree( pSort->pKeyPool ); + } + if ( pSort->pSwapPage ) + { + hb_xfree( pSort->pSwapPage ); + } + if ( pSort->pRecBuff ) + { + hb_xfree( pSort->pRecBuff ); + } + if ( pSort->pSortedPages ) + { + hb_xfree( pSort->pSortedPages ); + } + hb_xfree( pSort ); +} + +static void hb_cdxSortOut( LPCDXSORTINFO pSort ) +{ + BOOL fUnique = pSort->fUnique, fNext; + ULONG ulPage, ulRec, ulKey; + BYTE * pKeyVal; + int iLen = pSort->keyLen, iLevel; + + pSort->ulPages = pSort->ulCurPage + 1; + pSort->ulPgKeys = pSort->ulMaxKey / pSort->ulPages; + /* + printf( "\r\npSort->ulMaxKey=%ld, pSort->ulPages=%ld, pSort->ulPgKeys=%ld, size=%ld\r\n", + pSort->ulMaxKey, pSort->ulPages, pSort->ulPgKeys, + pSort->ulMaxKey * ( pSort->keyLen + 4 ) ); fflush(stdout); + */ + if ( pSort->ulPages > 1 ) + { + BYTE * pBuf = pSort->pKeyPool; + hb_cdxSortWritePage( pSort ); + for ( ulPage = 0; ulPage < pSort->ulPages; ulPage++ ) + { + pSort->pSwapPage[ ulPage ].ulKeyBuf = 0; + pSort->pSwapPage[ ulPage ].ulCurKey = 0; + pSort->pSwapPage[ ulPage ].pKeyPool = pBuf; + pBuf += pSort->ulPgKeys * ( pSort->keyLen + 4 ); + } + } + else + { + hb_cdxSortSortPage( pSort ); + pSort->pSwapPage[ 0 ].ulKeys = pSort->ulKeys; + pSort->pSwapPage[ 0 ].ulKeyBuf = pSort->ulKeys; + pSort->pSwapPage[ 0 ].ulCurKey = 0; + pSort->pSwapPage[ 0 ].pKeyPool = pSort->pKeyPool; + } + +#ifdef HB_CDX_NEW_SORT + hb_cdxSortOrderPages( pSort ); +#endif + + for ( ulKey = 0; ulKey < pSort->ulTotKeys; ulKey++ ) + { + if ( ! hb_cdxSortKeyGet( pSort, &pKeyVal, &ulRec ) ) + { + hb_errInternal( 9999, "hb_cdxSortOut: memory structure corrupted.", "", "" ); + } + if ( fUnique ) + { + if ( ulKey != 0 && hb_cdxValCompare( pSort->pTag, pSort->pLastKey, iLen, pKeyVal, iLen, TRUE ) == 0 ) + { + continue; + } + } +#ifdef HB_CDX_DBGCODE_EXT + if ( ulKey != 0 ) + { + int i = hb_cdxValCompare( pSort->pTag, pSort->pLastKey, iLen, pKeyVal, iLen, TRUE ); + if ( i == 0 ) + { + i = ( pSort->ulLastRec < ulRec ) ? -1 : 1; + } + if ( i > 0 ) + { + printf("\r\nulKey=%ld, pKeyVal=[%s][%ld], pKeyLast=[%s][%ld]\r\n", + ulKey, pKeyVal, ulRec, pSort->pLastKey, pSort->ulLastRec); fflush(stdout); + hb_errInternal( 9999, "hb_cdxSortOut: sorting fails.", "", "" ); + } + } +#endif + hb_cdxSortAddNodeKey( pSort, 0, pKeyVal, ulRec, 0 ); + memcpy( pSort->pLastKey, pKeyVal, iLen ); + pSort->ulLastRec = ulRec; + } + +#ifdef HB_CDX_DBGCODE + if ( hb_cdxSortKeyGet( pSort, &pKeyVal, &ulRec ) ) + { + hb_errInternal( 9999, "hb_cdxSortOut: memory structure corrupted(2).", "", "" ); + } +#endif + + if ( pSort->NodeList[ 0 ] == NULL ) + { + pSort->NodeList[ 0 ] = hb_cdxPageNew( pSort->pTag, NULL, 0 ); + pSort->NodeList[ 0 ]->PageType = CDX_NODE_LEAF; + hb_cdxPageLeafInitSpace( pSort->NodeList[ 0 ] ); + } + + iLevel = 0; + fNext = TRUE; + do + { + if ( iLevel + 1 == CDX_STACKSIZE || pSort->NodeList[ iLevel + 1 ] == NULL ) + { + pSort->NodeList[ iLevel ]->PageType |= CDX_NODE_ROOT; + pSort->pTag->RootBlock = pSort->NodeList[ iLevel ]->Page; + fNext = FALSE; + } + else + { + hb_cdxSortAddNodeKey( pSort, iLevel + 1, pSort->pLastKey, pSort->ulLastRec, pSort->NodeList[ iLevel ]->Page ); + } + hb_cdxPageFree( pSort->NodeList[ iLevel ], TRUE ); + iLevel++; + } + while ( fNext ); +} + +static void hb_cdxTagEmptyIndex( LPCDXTAG pTag ) +{ + pTag->RootPage = hb_cdxPageNew( pTag, NULL, 0 ); + pTag->RootBlock = pTag->RootPage->Page; + pTag->RootPage->PageType = CDX_NODE_ROOT | CDX_NODE_LEAF; + hb_cdxPageLeafInitSpace( pTag->RootPage ); +} + +static void hb_cdxTagDoIndex( LPCDXTAG pTag, BOOL fReindex ) +{ + LPCDXAREA pArea = pTag->pIndex->pArea; + LPCDXSORTINFO pSort; + PHB_ITEM pForItem, pWhileItem = NULL, pEvalItem = NULL, pItem = NULL; + ULONG ulRecCount, ulRecNo = pArea->ulRecNo; + LONG lStep = 0; +#ifndef HB_CDP_SUPPORT_OFF + /* TODO: this hack is not thread safe, hb_cdp_page has to be thread specific */ + PHB_CODEPAGE cdpTmp = hb_cdp_page; + hb_cdp_page = pArea->cdPage; +#endif + + if ( pArea->lpdbOrdCondInfo ) + { + pEvalItem = pArea->lpdbOrdCondInfo->itmCobEval; + pWhileItem = pArea->lpdbOrdCondInfo->itmCobWhile; + lStep = pArea->lpdbOrdCondInfo->lStep; + } + + if( pTag->Custom || ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) ) + { + ulRecCount = 0; + } + else if( SELF_RECCOUNT( ( AREAP ) pArea, &ulRecCount ) != SUCCESS ) + { + return; + } + + pArea->pSort = pSort = hb_cdxSortNew( pTag, ulRecCount ); + pSort->fReindex = fReindex; + +#if defined( HB_SIXCDX ) + if ( ( pTag->OptFlags & CDX_TYPE_STRUCTURE ) == 0 && pEvalItem ) + { + SELF_GOTO( ( AREAP ) pArea, 0 ); + if ( !hb_cdxEvalCond( pArea, pEvalItem, FALSE ) ) + { + hb_cdxSortFree( pSort ); + pArea->pSort = NULL; + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + return; + } + } +#endif + if( ulRecCount == 0 ) + { + hb_cdxTagEmptyIndex( pTag ); + } + else + { + USHORT uiSaveTag = pArea->uiTag; + ULONG ulStartRec = 0, ulNextCount = 0; + BOOL fDirectRead, fUseFilter = FALSE; + BYTE * pSaveRecBuff = pArea->pRecord, cTemp[8]; + int iRecBuff = 0, iRecBufSize = USHRT_MAX / pArea->uiRecordLen, iRec; + + pForItem = pTag->pForItem; + if ( pTag->nField ) + pItem = hb_itemNew( NULL ); + + if ( !pArea->lpdbOrdCondInfo || pArea->lpdbOrdCondInfo->fAll ) + { + pArea->uiTag = 0; + } + else + { + if( pArea->lpdbOrdCondInfo->itmRecID ) + ulStartRec = hb_itemGetNL( pArea->lpdbOrdCondInfo->itmRecID ); + if ( ulStartRec ) + { + ulNextCount = 1; + } + else if ( pArea->lpdbOrdCondInfo->fRest || pArea->lpdbOrdCondInfo->lNextCount > 0 ) + { + if( pArea->lpdbOrdCondInfo->itmStartRecID ) + ulStartRec = hb_itemGetNL( pArea->lpdbOrdCondInfo->itmStartRecID ); + if( !ulStartRec ) + ulStartRec = ulRecNo; + if( pArea->lpdbOrdCondInfo->lNextCount > 0 ) + ulNextCount = pArea->lpdbOrdCondInfo->lNextCount; + } + else if( pArea->lpdbOrdCondInfo->fUseFilter ) + { + fUseFilter = TRUE; + } + else if ( !pArea->lpdbOrdCondInfo->fUseCurrent ) + { + pArea->uiTag = 0; + } + } + fDirectRead = !hb_set.HB_SET_STRICTREAD && /* !pArea->lpdbRelations && */ + ( !pArea->lpdbOrdCondInfo || pArea->lpdbOrdCondInfo->fAll || + ( pArea->uiTag == 0 && !fUseFilter ) ); + + if ( fDirectRead ) + pSort->pRecBuff = (BYTE *) hb_xgrab( pArea->uiRecordLen * iRecBufSize ); + + if ( ulStartRec == 0 && pArea->uiTag == 0 ) + ulStartRec = 1; + + if ( ulStartRec == 0 ) + { + SELF_GOTOP( ( AREAP ) pArea ); + } + else + { + SELF_GOTO( ( AREAP ) pArea, ulStartRec ); + if ( fUseFilter ) + SELF_SKIPFILTER( ( AREAP ) pArea, 1 ); + } + + ulRecNo = pArea->ulRecNo; + + while ( !pArea->fEof ) + { + if ( fDirectRead ) + { + if ( ulRecNo > ulRecCount ) + break; + if ( iRecBuff == 0 || iRecBuff >= iRecBufSize ) + { + if ( ulRecCount - ulRecNo >= ( ULONG ) iRecBufSize ) + iRec = iRecBufSize; + else + iRec = ulRecCount - ulRecNo + 1; + if( ulNextCount > 0 && ulNextCount < ( ULONG ) iRec ) + iRec = ( int ) ulNextCount; + hb_fsSeekLarge( pArea->hDataFile, + ( HB_FOFFSET ) pArea->uiHeaderLen + + ( HB_FOFFSET ) ( ulRecNo - 1 ) * + ( HB_FOFFSET ) pArea->uiRecordLen, FS_SET ); + hb_fsReadLarge( pArea->hDataFile, pSort->pRecBuff, pArea->uiRecordLen * iRec ); + iRecBuff = 0; + } + pArea->pRecord = pSort->pRecBuff + iRecBuff * pArea->uiRecordLen; + pArea->ulRecNo = ulRecNo; + if( SELF_GETREC( ( AREAP ) pArea, NULL ) == FAILURE ) + break; + pArea->fValidBuffer = pArea->fPositioned = TRUE; + pArea->fDeleted = pArea->pRecord[ 0 ] == '*'; + /* Force relational movement in child WorkAreas */ + if( pArea->lpdbRelations ) + if( SELF_SYNCCHILDREN( ( AREAP ) pArea ) == FAILURE ) + break; + iRecBuff++; + } + +#if !defined( HB_SIXCDX ) + if ( pEvalItem ) + { + if ( lStep >= pArea->lpdbOrdCondInfo->lStep ) + { + lStep = 0; + if ( !hb_cdxEvalCond( pArea, pEvalItem, FALSE ) ) + break; + } + ++lStep; + } +#endif + + if ( pWhileItem && !hb_cdxEvalCond( NULL, pWhileItem, FALSE ) ) + break; + + if ( ulRecNo <= ulRecCount && + ( pForItem == NULL || hb_cdxEvalCond( pArea, pForItem, FALSE ) ) ) + { + double d; + + if ( pTag->nField ) + SELF_GETVALUE( ( AREAP ) pArea, pTag->nField, pItem ); + else + pItem = hb_vmEvalBlockOrMacro( pTag->pKeyItem ); + + switch( hb_itemType( pItem ) ) + { + case HB_IT_STRING: + case HB_IT_STRING | HB_IT_MEMO: + hb_cdxSortKeyAdd( pSort, pArea->ulRecNo, + ( BYTE * ) hb_itemGetCPtr( pItem ), + hb_itemGetCLen( pItem ) ); + break; + + case HB_IT_INTEGER: + case HB_IT_LONG: + case HB_IT_DOUBLE: + d = hb_itemGetND( pItem ); + HB_DBL2ORD( &d, &cTemp[0] ); + hb_cdxSortKeyAdd( pSort, pArea->ulRecNo, cTemp, 8 ); + break; + + case HB_IT_DATE: + d = (double) hb_itemGetDL( pItem ); + HB_DBL2ORD( &d, &cTemp[0] ); + hb_cdxSortKeyAdd( pSort, pArea->ulRecNo, cTemp, 8 ); + break; + + case HB_IT_LOGICAL: + cTemp[0] = (BYTE) (hb_itemGetL( pItem ) ? 'T' : 'F'); + hb_cdxSortKeyAdd( pSort, pArea->ulRecNo, cTemp, 1 ); + break; + + default: + if ( hb_vmRequestQuery() ) + { + pEvalItem = NULL; + ulNextCount = 1; + } + else + { + printf( "hb_cdxTagDoIndex: hb_itemType( pItem ) = %i", hb_itemType( pItem ) ); + } + break; + } + } + + if( ulNextCount > 0 ) + { + if( --ulNextCount == 0 ) + break; + } + +#if defined( HB_SIXCDX ) + if ( pEvalItem ) + { + if ( lStep >= pArea->lpdbOrdCondInfo->lStep ) + { + lStep = 0; + if ( !hb_cdxEvalCond( pArea, pEvalItem, FALSE ) ) + break; + } + ++lStep; + } +#endif + + if( fDirectRead ) + ulRecNo++; + else + { + if( SELF_SKIPRAW( ( AREAP ) pArea, 1 ) == FAILURE ) + break; + if( fUseFilter && SELF_SKIPFILTER( ( AREAP ) pArea, 1 ) == FAILURE ) + break; + ulRecNo = pArea->ulRecNo; + } + } + + hb_cdxSortOut( pSort ); + if ( pTag->nField ) + hb_itemRelease( pItem ); + + if ( fDirectRead ) + { + pArea->pRecord = pSaveRecBuff; + SELF_GOTO( ( AREAP ) pArea, ulRecNo ); + } + pArea->uiTag = uiSaveTag; + +#if !defined( HB_SIXCDX ) + if ( pEvalItem && lStep ) + { + /* pArea->fEof = TRUE; */ + hb_cdxEvalCond( pArea, pEvalItem, FALSE ); + } +#endif + } + +#if defined( HB_SIXCDX ) + if ( pEvalItem ) + { + SELF_GOTO( ( AREAP ) pArea, 0 ); + pArea->fBof = FALSE; + hb_cdxEvalCond( pArea, pEvalItem, FALSE ); + } +#endif + + hb_cdxSortFree( pSort ); + pArea->pSort = NULL; + +#ifndef HB_CDP_SUPPORT_OFF + hb_cdp_page = cdpTmp; +#endif +} + +#define __PRG_SOURCE__ __FILE__ +#ifdef HB_PCODE_VER + #undef HB_PRG_PCODE_VER + #define HB_PRG_PCODE_VER HB_PCODE_VER +#endif + +HB_FUNC_EXTERN( _DBF ); + +#if defined( HB_SIXCDX ) + +HB_FUNC( SIXCDX ) {;} + +HB_FUNC( SIXCDX_GETFUNCTABLE ) +{ + RDDFUNCS * pTable; + USHORT * uiCount, uiRddId; + + uiCount = ( USHORT * ) hb_itemGetPtr( hb_param( 1, HB_IT_POINTER ) ); + pTable = ( RDDFUNCS * ) hb_itemGetPtr( hb_param( 2, HB_IT_POINTER ) ); + uiRddId = hb_parni( 4 ); + + HB_TRACE(HB_TR_DEBUG, ("SIXCDX_GETFUNCTABLE(%i, %p)", uiCount, pTable)); + + if ( pTable ) + { + ERRCODE errCode; + + if ( uiCount ) + * uiCount = RDDFUNCSCOUNT; + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBFFPT" ); + if ( errCode != SUCCESS ) + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBFDBT" ); + if ( errCode != SUCCESS ) + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBF" ); + hb_retni( errCode ); + if ( errCode == SUCCESS ) + { + /* + * we successfully register our RDD so now we can initialize it + * You may think that this place is RDD init statement, Druzus + */ + s_uiRddId = uiRddId; + } + } + else + hb_retni( FAILURE ); +} + +static void hb_bmdbfcdxRddInit( void * cargo ) +{ + HB_SYMBOL_UNUSED( cargo ); + + if( hb_rddRegister( "DBF", RDT_FULL ) <= 1 ) + { + hb_rddRegister( "DBFFPT", RDT_FULL ); + if( hb_rddRegister( "SIXCDX", RDT_FULL ) <= 1 ) + { + return; + } + } + + hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL ); + + /* not executed, only to force DBF RDD linking */ + HB_FUNC_EXEC( _DBF ); +} + +HB_INIT_SYMBOLS_BEGIN( bmdbfcdx1__InitSymbols ) +{ "SIXCDX", {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( SIXCDX )}, NULL }, +{ "SIXCDX_GETFUNCTABLE", {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( SIXCDX_GETFUNCTABLE )}, NULL } +HB_INIT_SYMBOLS_END( bmdbfcdx1__InitSymbols ) + +#else + +HB_FUNC( BMDBFCDX ) {;} + +HB_FUNC( BMDBFCDX_GETFUNCTABLE ) +{ + RDDFUNCS * pTable; + USHORT * uiCount, uiRddId; + + uiCount = ( USHORT * ) hb_itemGetPtr( hb_param( 1, HB_IT_POINTER ) ); + pTable = ( RDDFUNCS * ) hb_itemGetPtr( hb_param( 2, HB_IT_POINTER ) ); + uiRddId = hb_parni( 4 ); + + HB_TRACE(HB_TR_DEBUG, ("BMDBFCDX_GETFUNCTABLE(%i, %p)", uiCount, pTable)); + + if ( pTable ) + { + ERRCODE errCode; + + if ( uiCount ) + * uiCount = RDDFUNCSCOUNT; + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBFFPT" ); + if ( errCode != SUCCESS ) + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBFDBT" ); + if ( errCode != SUCCESS ) + errCode = hb_rddInherit( pTable, &cdxTable, &cdxSuper, ( BYTE * ) "DBF" ); + if ( errCode == SUCCESS ) + { + /* + * we successfully register our RDD so now we can initialize it + * You may think that this place is RDD init statement, Druzus + */ + s_uiRddId = uiRddId; + } + hb_retni( errCode ); + } + else + hb_retni( FAILURE ); +} + +static void hb_bmdbfcdxRddInit( void * cargo ) +{ + HB_SYMBOL_UNUSED( cargo ); + + if( hb_rddRegister( "DBF", RDT_FULL ) <= 1 ) + { + hb_rddRegister( "DBFFPT", RDT_FULL ); + if( hb_rddRegister( "BMDBFCDX", RDT_FULL ) <= 1 ) + { + return; + } + } + + hb_errInternal( HB_EI_RDDINVALID, NULL, NULL, NULL ); + + /* not executed, only to force DBF RDD linking */ + HB_FUNC_EXEC( _DBF ); +} + +HB_INIT_SYMBOLS_BEGIN( bmdbfcdx1__InitSymbols ) +{ "BMDBFCDX", {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( BMDBFCDX )}, NULL }, +{ "BMDBFCDX_GETFUNCTABLE", {HB_FS_PUBLIC|HB_FS_LOCAL}, {HB_FUNCNAME( BMDBFCDX_GETFUNCTABLE )}, NULL } +HB_INIT_SYMBOLS_END( bmdbfcdx1__InitSymbols ) + +#endif + +HB_CALL_ON_STARTUP_BEGIN( _hb_bmdbfcdx_rdd_init_ ) + hb_vmAtInit( hb_bmdbfcdxRddInit, NULL ); +HB_CALL_ON_STARTUP_END( _hb_bmdbfcdx_rdd_init_ ) + +#if defined(HB_PRAGMA_STARTUP) +# pragma startup bmdbfcdx1__InitSymbols +# pragma startup _hb_bmdbfcdx_rdd_init_ +#elif defined(HB_MSC_STARTUP) +# if _MSC_VER >= 1010 +# pragma data_seg( ".CRT$XIY" ) +# pragma comment( linker, "/Merge:.CRT=.data" ) +# else +# pragma data_seg( "XIY" ) +# endif + static HB_$INITSYM hb_vm_auto_bmdbfcdx1__InitSymbols = bmdbfcdx1__InitSymbols; + static HB_$INITSYM hb_vm_auto_bmdbfcdx_rdd_init = _hb_bmdbfcdx_rdd_init_; +# pragma data_seg() +#endif diff --git a/harbour/contrib/bmdbfcdx/bmsixcdx1.c b/harbour/contrib/bmdbfcdx/bmsixcdx1.c new file mode 100644 index 0000000000..10d8eb4c27 --- /dev/null +++ b/harbour/contrib/bmdbfcdx/bmsixcdx1.c @@ -0,0 +1,55 @@ +/* + * $Id$ + */ + +/* + * xHarbour Project source code: + * BMSIXCDX RDD + * + * Copyright 2007 Miguel Angel Marchuet Frutos + * www - http://www.xharbour.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#define HB_SIXCDX + +#include "bmdbfcdx1.c" diff --git a/harbour/contrib/bmdbfcdx/hbrddbmcdx.h b/harbour/contrib/bmdbfcdx/hbrddbmcdx.h new file mode 100644 index 0000000000..304e741e78 --- /dev/null +++ b/harbour/contrib/bmdbfcdx/hbrddbmcdx.h @@ -0,0 +1,669 @@ +/* + * $Id$ + */ + +/* + * BMDBFCDX RDD (ver.2) + * + * Copyright 1999 Bruno Cantero + * Copyright 2003 Przemyslaw Czerpak + * Copyright 2006 Miguel Angel Marchuet + * www - http://www.xharbour.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). + * + * As a special exception, the Harbour Project gives permission for + * additional uses of the text contained in its release of Harbour. + * + * The exception is that, if you link the Harbour libraries with other + * files to produce an executable, this does not by itself cause the + * resulting executable to be covered by the GNU General Public License. + * Your use of that executable is in no way restricted on account of + * linking the Harbour library code into it. + * + * This exception does not however invalidate any other reasons why + * the executable file might be covered by the GNU General Public License. + * + * This exception applies only to the code released by the Harbour + * Project under the name Harbour. If you copy code from other + * Harbour Project or Free Software Foundation releases into a copy of + * Harbour, as the General Public License permits, the exception does + * not apply to the code that you add in this way. To avoid misleading + * anyone as to the status of such modified files, you must delete + * this exception notice from them. + * + * If you write modifications of your own for Harbour, it is your choice + * whether to permit this exception to apply to your modifications. + * If you do not wish that, delete this exception notice. + * + */ + +#ifndef HB_RDDCDX_H_ +#define HB_RDDCDX_H_ + +#include "hbsetup.h" +#include "hbapirdd.h" +#include "hbdbferr.h" +#define HB_EXTERNAL_RDDDBF_USE +#include "hbrdddbf.h" + +HB_EXTERN_BEGIN + +/* CDX constants and defaults */ +#define CDX_INDEXEXT ".cdx" +#define CDX_MAXKEY 240 +#define CDX_MAXEXP 255 +#define CDX_MAXTAGNAMELEN 10 +#define CDX_PAGELEN 512 +#define CDX_HEADERLEN 1024 +#define CDX_HEADEREXPLEN (CDX_HEADERLEN - 512) +#define CDX_HEADERPAGES ((CDX_HEADERLEN+CDX_PAGELEN-1)/CDX_PAGELEN) +#define CDX_INT_FREESPACE (CDX_PAGELEN-12) /* 500 */ +#define CDX_EXT_FREESPACE (CDX_PAGELEN-24) /* 488 */ +#define CDX_DUMMYNODE 0xFFFFFFFFL + +/* #define CDX_LOCKOFFSET 0x7FFFFFFEL */ +/* #define CDX_LOCKSIZE 1L */ +#define CDX_STACKSIZE 64 +#define CDX_PAGECACHESIZE 8 +#define CDX_NODE_BRANCH 0 +#define CDX_NODE_ROOT 1 +#define CDX_NODE_LEAF 2 +#define CDX_NODE_UNUSED 0xFF +#define CDX_IGNORE_REC_NUM 0x0L +#define CDX_MAX_REC_NUM 0xFFFFFFFFL +#define CDX_BALANCE_LEAFPAGES 3 +#define CDX_BALANCE_INTPAGES 3 + +#define CDX_CURKEY_UNDEF (1<< 0) +#define CDX_CURKEY_REC (1<< 1) +#define CDX_CURKEY_VAL (1<< 2) +#define CDX_CURKEY_INPAGE (1<< 3) +#define CDX_CURKEY_INSTACK (1<< 4) +#define CDX_CURKEY_NOTEXIST (1<< 5) +#define CDX_CURKEY_RAWCNT (1<< 6) +#define CDX_CURKEY_RAWPOS (1<< 7) +#define CDX_CURKEY_LOGCNT (1<< 8) +#define CDX_CURKEY_LOGPOS (1<< 9) + +#define TOP_RECORD 1 +#define BTTM_RECORD 2 +#define PREV_RECORD 3 +#define NEXT_RECORD 4 +#define PRVU_RECORD 6 +#define NXTU_RECORD 5 + +#define NODE_NEWLASTKEY 1 +#define NODE_SPLIT 2 +#define NODE_JOIN 4 +#define NODE_BALANCE 8 +#define NODE_EAT 16 + +#define CURKEY_RAWCNT(pTag) (((pTag)->curKeyState & CDX_CURKEY_RAWCNT) != 0) +#define CURKEY_LOGCNT(pTag) (((pTag)->curKeyState & CDX_CURKEY_LOGCNT) != 0) +#define CURKEY_SETLOGCNT(pTag, lKeyCount) { (pTag)->curKeyState |= CDX_CURKEY_LOGCNT; \ + (pTag)->logKeyCount = (lKeyCount); } + +#define CURKEY_RAWPOS(pTag) ( ((pTag)->curKeyState & CDX_CURKEY_RAWPOS) != 0 && \ + (pTag)->rawKeyRec == (pTag)->CurKey->rec ) +#define CURKEY_SETRAWPOS(pTag) { (pTag)->curKeyState |= CDX_CURKEY_RAWPOS; \ + (pTag)->rawKeyRec = (pTag)->CurKey->rec; } + +#define CURKEY_LOGPOS(pTag) ( ((pTag)->curKeyState & CDX_CURKEY_LOGPOS) != 0 && \ + (pTag)->logKeyRec == (pTag)->pIndex->pArea->ulRecNo ) +#define CURKEY_SETLOGPOS(pTag) { (pTag)->curKeyState |= CDX_CURKEY_LOGPOS; \ + (pTag)->logKeyRec = (pTag)->pIndex->pArea->ulRecNo; } + +/* +#define CURKEY_UNDEF(pTag) (((pTag)->curKeyState & CDX_CURKEY_UNDEF) != 0) +#define CURKEY_NOTEXIST(pTag) (((pTag)->curKeyState & CDX_CURKEY_NOTEXIST) != 0) +#define CURKEY_ISSET(pTag) (((pTag)->curKeyState & (CDX_CURKEY_NOTEXIST | CDX_CURKEY_UNDEF)) == 0) +#define CURKEY_REC(pTag) ((((pTag)->curKeyState & CDX_CURKEY_REC) != 0) ? (pTag)->curKey->rec : 0) +#define CURKEY_VAL(pTag) ((((pTag)->curKeyState & CDX_CURKEY_VAL) != 0) ? (pTag)->curKey->val : NULL) +#define CURKEY_REFRESH(pTag) +*/ + +#define HB_CDXMAXKEY( x ) ((USHORT) ((x) > CDX_MAXKEY ? CDX_MAXKEY : (x))) +#define HB_CDXBITMASK( x ) ((LONG) ((1L<<(x))-1)) + +/* #define FAST_GOCOLD( A ) (((CDXAREAP) (A))->fRecordChanged || ((CDXAREAP) (A))->fCdxAppend ? (SELF_GOCOLD((A))) : SUCCESS) */ +#define FAST_GOCOLD( A ) SELF_GOCOLD(A) + + +#define CDX_TYPE_UNIQUE 0x01 /* unique index */ +#define CDX_TYPE_TEMPORARY 0x02 /* temporary index */ +#define CDX_TYPE_CUSTOM 0x04 /* custom index */ +#define CDX_TYPE_FORFILTER 0x08 /* for expression present */ +#define CDX_TYPE_BITVECTOR 0x10 /* SoftC? */ +#define CDX_TYPE_COMPACT 0x20 /* FoxPro */ +#define CDX_TYPE_COMPOUND 0x40 /* FoxPro */ +#define CDX_TYPE_STRUCTURE 0x80 /* FoxPro */ + +/* + TODO like in SIXCDX: + switch ( indexOpt & ( CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM ) ) + case CDX_TYPE_TEMPORARY: + PARTIAL_RYO + case CDX_TYPE_CUSTOM: + PARTIAL_RYO | CHGONLY_RYO + case CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM: + PARTIAL_RYO | NOUPDATE_RYO + if index key begin with: + 'SXCHAR(' or 'SXNUM(' or 'SXDATE(' or 'SXLOG(' + then + | TEMPLATE_RYO + + sx_chill() if ( ! NOUPDATE_RYO ) then set ( CHGONLY_RYO | PARTIAL_RYO ) + if ( indexOpt & ( CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM ) != + CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM ) + { + indexOpt &= ~CDX_TYPE_CUSTOM; + indexOpt |= CDX_TYPE_TEMPORARY + } + + sx_warm() if ( ! NOUPDATE_RYO ) then clear CHGONLY_RYO + if ( indexOpt & ( CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM ) != + CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM ) + { + indexOpt |= CDX_TYPE_CUSTOM; + indexOpt &= ~CDX_TYPE_TEMPORARY + } + + sx_freeze() set NOUPDATE_RYO + indexOpt |= CDX_TYPE_TEMPORARY | CDX_TYPE_CUSTOM; +*/ + +/* CDX index node strucutres */ +/* Compact Index Header Record */ +typedef struct _CDXTAGHEADER +{ + BYTE rootPtr [ 4 ]; /* offset of the root node */ + BYTE freePtr [ 4 ]; /* offset of list of free pages or -1 */ + BYTE reserved1[ 4 ]; /* Version number ??? */ + BYTE keySize [ 2 ]; /* key length */ + BYTE indexOpt; /* index options see CDX_TYPE_* */ + BYTE indexSig; /* index signature */ + BYTE reserved2[ 484 ]; + BYTE ignoreCase[ 2 ]; /* 1 = ignore case, key converted to upper */ + BYTE ascendFlg[ 2 ]; /* 0 = ascending 1 = descending */ + BYTE forExpPos[ 2 ]; /* offset of filter expression */ + BYTE forExpLen[ 2 ]; /* length of filter expression */ + BYTE keyExpPos[ 2 ]; /* offset of key expression */ + BYTE keyExpLen[ 2 ]; /* length of key expression */ + BYTE keyExpPool[ CDX_HEADEREXPLEN ]; +} CDXTAGHEADER; +typedef CDXTAGHEADER * LPCDXTAGHEADER; + +/* Compact Index Interior Node Record */ +typedef struct _CDXINTNODE +{ + BYTE attr [ 2 ]; /* node type see CDX_NODE_* */ + BYTE nKeys [ 2 ]; /* number of keys */ + BYTE leftPtr [ 4 ]; /* offset of left node or -1 */ + BYTE rightPtr[ 4 ]; /* offset of right node or -1 */ + BYTE keyPool [ CDX_INT_FREESPACE ]; +} CDXINTNODE; +typedef CDXINTNODE * LPCDXINTNODE; +typedef CDXINTNODE CDXNODE; +typedef CDXNODE * LPCDXNODE; + +/* Compact Index Exterior Node Record */ +typedef struct _CDXEXTNODE +{ + BYTE attr [ 2 ]; /* node type see CDX_NODE_* */ + BYTE nKeys [ 2 ]; /* number of keys */ + BYTE leftPtr [ 4 ]; /* offset of left node or -1 */ + BYTE rightPtr[ 4 ]; /* offset of right node or -1 */ + BYTE freeSpc [ 2 ]; /* free space available in a page */ + BYTE recMask [ 4 ]; /* record number mask */ + BYTE dupMask; /* duplicate bytes count mask */ + BYTE trlMask; /* trailing bytes count mask */ + BYTE recBits; /* number of bits for record number */ + BYTE dupBits; /* number of bits for duplicate count */ + BYTE trlBits; /* number of bits for trailing count */ + BYTE keyBytes; /* total number of bytes for recnn/dup/trail info */ + BYTE keyPool [ CDX_EXT_FREESPACE ]; /* rec/dup/trl */ +} CDXEXTNODE; +typedef CDXEXTNODE * LPCDXEXTNODE; + + + +/* CDX internal memory structures */ + +struct _CDXAREA; /* forward declaration */ +struct _CDXINDEX; /* forward declaration */ +struct _CDXTAG; /* forward declaration */ + +typedef struct _CDXKEY +{ + BYTE * val; + BYTE len; + ULONG rec; +} CDXKEY; +typedef CDXKEY * LPCDXKEY; + +typedef struct _CDXPAGE +{ + ULONG Page; + ULONG Left; + ULONG Right; + + BYTE PageType; + SHORT iKeys; + SHORT iCurKey; + + BOOL fChanged; + BYTE bUsed; + + ULONG RNMask; + BYTE ReqByte; + BYTE RNBits; + BYTE DCBits; + BYTE TCBits; + BYTE DCMask; + BYTE TCMask; + BOOL fBufChanged; + union + { + CDXEXTNODE extNode; + CDXINTNODE intNode; + } node; + BYTE bufKeyVal[ CDX_MAXKEY ]; /* buffer for leaf key val or added branch key */ + SHORT bufKeyNum; /* do not change these vars' order */ + SHORT bufKeyPos; /* they have to be just after the node */ + SHORT bufKeyLen; /* and maybe temporary overwriten when adding */ + SHORT iFree; /* new key to interior node record. */ + BYTE * pKeyBuf; /* pointer to uncompressed leaf page key pool */ + /* SHORT iKeyInBuf; */ + + struct _CDXPAGE * Owner; + struct _CDXPAGE * Child; + struct _CDXTAG * TagParent; + struct _CDXPAGE * pPoolPrev; + struct _CDXPAGE * pPoolNext; +} CDXPAGE; +typedef CDXPAGE * LPCDXPAGE; + +typedef struct _CDXSTACK +{ + LPCDXPAGE Page; + SHORT iKey; +} CDXSTACK; +typedef CDXSTACK * LPCDXSTACK; + +typedef struct _CDXLIST +{ + ULONG ulAddr; + BOOL fStat; + struct _CDXLIST * pNext; +} CDXLIST; +typedef CDXLIST * LPCDXLIST; + +typedef struct _CDXTAG +{ + char * szName; /* Name of tag */ + char * KeyExpr; /* a tag key expression as text */ + char * ForExpr; /* a tag for expression as text */ + PHB_ITEM pKeyItem; /* item with a macro pcode for a tag key expression */ + PHB_ITEM pForItem; /* item with a macro pcode for a tag for expression */ + USHORT uiType; /* a type of key expression value */ + USHORT uiLen; /* length of the key expression value */ + USHORT nField; /* Field number for simple (one field) key expersion */ + BYTE bTrail; /* trailing character for shorter key value */ + BYTE OptFlags; /* index options flag */ + BOOL AscendKey; /* ascending/descending order flag */ + BOOL UniqueKey; /* unique order flag */ + BOOL Temporary; /* temporary order flag */ + BOOL Custom; /* custom order flag */ + + BOOL UsrAscend; /* user settable ascending/descending order flag */ + BOOL UsrUnique; /* user settable unique order flag */ + + BOOL TagChanged; + BOOL TagBOF; + BOOL TagEOF; + + BOOL fRePos; + int curKeyState; /* see: CDX_CURKEY_* */ + ULONG rawKeyCount; + ULONG rawKeyPos; + ULONG rawKeyRec; + ULONG logKeyCount; + ULONG logKeyPos; + ULONG logKeyRec; + + ULONG TagBlock; /* a page offset where a tag header is stored */ + ULONG RootBlock; /* a page offset with the root of keys tree */ + USHORT MaxKeys; /* maximum number of keys in Interior node */ + + struct _CDXINDEX * pIndex; /* a parent index info */ + struct _CDXTAG * pNext; /* pointer to next tag in index */ + + /* CDXSTACK PageStack[ CDX_STACKSIZE ]; */ /* stack with page path to current key */ + LPCDXPAGE RootPage; /* pointer to root of keys tree in memory */ + LPCDXKEY CurKey; /* current value of key expression */ + LPCDXKEY HotKey; /* value of hot key expression */ + BOOL HotFor; /* index FOR condition for HotKey */ + + PHB_ITEM topScope; /* Top scope HB_ITEM */ + LPCDXKEY topScopeKey; /* Top scope index key */ + PHB_ITEM bottomScope; /* Bottom scope HB_ITEM */ + LPCDXKEY bottomScopeKey; /* Bottom index key */ + + LPCDXPAGE pagePool; /* page buffer in memory */ +} CDXTAG; +typedef CDXTAG * LPCDXTAG; + +typedef struct _CDXINDEX +{ + char * szFileName; /* Name of index file */ + char * szRealName; /* Real name of index file */ + FHANDLE hFile; /* Index file handle */ + struct _CDXAREA * pArea; /* Parent WorkArea */ + struct _CDXINDEX * pNext; /* The next index in the list */ + LPCDXTAG pCompound; /* Compound tag */ + LPCDXTAG TagList; /* List of tags in index file */ + BOOL fShared; /* Shared file */ + BOOL fReadonly; /* Read only file */ + BOOL fDelete; /* delete on close flag */ + ULONG nextAvail; /* offset to next free page in the end of index file */ + ULONG freePage; /* offset to next free page inside index file */ + LPCDXLIST freeLst; /* list of free pages in index file */ + int lockWrite; /* number of write lock set */ + int lockRead; /* number of read lock set */ + HB_FOFFSET ulLockPos; /* readlock position for CL53 lock scheme */ +#ifdef HB_CDX_DBGCODE + BOOL RdLck; + BOOL WrLck; +#endif + BOOL fChanged; /* changes written to index, need upadte ulVersion */ + ULONG ulVersion; /* network version/update flag */ + BOOL fFlush; /* changes written to index, need upadte ulVersion */ +} CDXINDEX; +typedef CDXINDEX * LPCDXINDEX; + +/* for index creation */ +typedef struct +{ + HB_FOFFSET nOffset; /* offset in temporary file */ + ULONG ulKeys; /* number of keys in page */ + ULONG ulKeyBuf; /* number of keys in memory buffer */ + ULONG ulCurKey; /* current key in memory buffer */ + BYTE * pKeyPool; /* memory buffer */ +} CDXSWAPPAGE; +typedef CDXSWAPPAGE * LPCDXSWAPPAGE; + +typedef struct +{ + LPCDXTAG pTag; /* current Tag */ + FHANDLE hTempFile; /* handle to temporary file */ + char * szTempFileName; /* temporary file name */ + int keyLen; /* key length */ + BYTE bTrl; /* filler char for shorter keys */ + BOOL fUnique; /* TRUE if index is unique */ + BOOL fReindex; /* TRUE if reindexing is in process */ + ULONG ulMaxRec; /* the highest record number */ + ULONG ulTotKeys; /* total number of keys indexed */ + ULONG ulKeys; /* keys in curently created page */ + ULONG ulPages; /* number of pages */ + ULONG ulCurPage; /* current page */ + ULONG ulPgKeys; /* maximum number of key in page memory buffer */ + ULONG ulMaxKey; /* maximum number of keys in single page */ + BYTE * pKeyPool; /* memory buffer for current page then for pages */ + LPCDXSWAPPAGE pSwapPage; /* list of pages */ + LPCDXPAGE NodeList[ CDX_STACKSIZE ]; /* Stack of pages */ + ULONG ulFirst; + ULONG * pSortedPages; + BYTE pLastKey[ CDX_MAXKEY ]; /* last key val */ + ULONG ulLastRec; + BYTE * pRecBuff; +#ifndef HB_CDX_PACKTRAIL + int iLastTrl; /* last key trailing spaces */ +#endif +} CDXSORTINFO; +typedef CDXSORTINFO * LPCDXSORTINFO; + + + +/* + * DBF WORKAREA + * ------------ + * The Workarea Structure of DBFCDX RDD + * + */ + +typedef struct _CDXAREA +{ + struct _RDDFUNCS * lprfsHost; /* Virtual method table for this workarea */ + USHORT uiArea; /* The number assigned to this workarea */ + void * atomAlias; /* Pointer to the alias symbol for this workarea */ + USHORT uiFieldExtent; /* Total number of fields allocated */ + USHORT uiFieldCount; /* Total number of fields used */ + LPFIELD lpFields; /* Pointer to an array of fields */ + void * lpFieldExtents; /* Void ptr for additional field properties */ + PHB_ITEM valResult; /* All purpose result holder */ + BOOL fTop; /* TRUE if "top" */ + BOOL fBottom; /* TRUE if "bottom" */ + BOOL fBof; /* TRUE if "bof" */ + BOOL fEof; /* TRUE if "eof" */ + BOOL fFound; /* TRUE if "found" */ + DBSCOPEINFO dbsi; /* Info regarding last LOCATE */ + DBFILTERINFO dbfi; /* Filter in effect */ + LPDBORDERCONDINFO lpdbOrdCondInfo; + LPDBRELINFO lpdbRelations; /* Parent/Child relationships used */ + USHORT uiParents; /* Number of parents for this area */ + USHORT heap; + USHORT heapSize; + USHORT rddID; + USHORT uiMaxFieldNameLength; + PHB_CODEPAGE cdPage; /* Area's codepage pointer */ + + /* + * DBFS's additions to the workarea structure + * + * Warning: The above section MUST match WORKAREA exactly! Any + * additions to the structure MUST be added below, as in this + * example. + */ + + FHANDLE hDataFile; /* Data file handle */ + FHANDLE hMemoFile; /* Memo file handle */ + char * szDataFileName; /* Name of data file */ + char * szMemoFileName; /* Name of memo file */ + USHORT uiHeaderLen; /* Size of header */ + USHORT uiRecordLen; /* Size of record */ + USHORT uiMemoBlockSize; /* Size of memo block */ + USHORT uiMemoVersion; /* MEMO file version */ + DBFHEADER dbfHeader; /* DBF header buffer */ + BYTE bTableType; /* DBF type */ + BYTE bMemoType; /* MEMO type used in DBF memo fields */ + BYTE bLockType; /* Type of locking shemes */ + BYTE bCryptType; /* Type of used encryption */ + USHORT * pFieldOffset; /* Pointer to field offset array */ + BYTE * pRecord; /* Buffer of record data */ + ULONG ulRecCount; /* Total records */ + ULONG ulRecNo; /* Current record */ + BOOL fAutoInc; /* WorkArea with auto increment fields */ + BOOL fHasMemo; /* WorkArea with Memo fields */ + BOOL fHasTags; /* WorkArea with MDX or CDX index */ + BOOL fDataFlush; /* data was written to DBF and not commited */ + BOOL fMemoFlush; /* data was written to MEMO and not commited */ + BOOL fShared; /* Shared file */ + BOOL fReadonly; /* Read only file */ + BOOL fValidBuffer; /* State of buffer */ + BOOL fPositioned; /* Positioned record */ + BOOL fRecordChanged; /* Record changed */ + BOOL fAppend; /* TRUE if new record is added */ + BOOL fDeleted; /* TRUE if record is deleted */ + BOOL fEncrypted; /* TRUE if record is encrypted */ + BOOL fTableEncrypted; /* TRUE if table is encrypted */ + BOOL fUpdateHeader; /* Update header of file */ + BOOL fFLocked; /* TRUE if file is locked */ + BOOL fHeaderLocked; /* TRUE if DBF header is locked */ + LPDBRELINFO lpdbPendingRel; /* Pointer to parent rel struct */ + ULONG * pLocksPos; /* List of records locked */ + ULONG ulNumLocksPos; /* Number of records locked */ + BYTE * pCryptKey; /* Pointer to encryption key */ + PHB_DYNS pTriggerSym; /* DynSym pointer to trigger function */ + + /* + * CDX's additions to the workarea structure + * + * Warning: The above section MUST match DBFAREA exactly! Any + * additions to the structure MUST be added below, as in this + * example. + */ + + BOOL fCdxAppend; /* Appended record changed */ + LPCDXINDEX lpIndexes; /* Pointer to indexes array */ + USHORT uiTag; /* current tag focus */ + LPCDXSORTINFO pSort; /* Index build structure */ + BYTE * bCdxSortTab; /* Table with storted characters */ + +} CDXAREA; + +typedef CDXAREA * LPCDXAREA; + +#ifndef CDXAREAP +#define CDXAREAP LPCDXAREA +#endif + +// m Bitmap, b Size, r RecNo +#define BM_SetBit(m,b,r) ((r)<=(b))?((m)[((r)-1)>>5] = (m)[((r)-1)>>5] | (1<<(((r)-1)%32))):0 +#define BM_ClrBit(m,b,r) ((r)<=(b))?((m)[((r)-1)>>5] = (m)[((r)-1)>>5] & ~(1<<(((r)-1)%32))):0 +#define BM_GetBit(m,b,r) (((r)<=(b))?(((m)[((r)-1)>>5] & (1<<(((r)-1)%32)))):0) + +typedef struct _BM_FILTER_ { + PHB_ITEM itmCobExpr; /* Block representation of the FILTER expression */ + PHB_ITEM abFilterText; /* String representation of FILTER expression */ + BOOL fFilter; /* flag to indicate that filter is active */ + BOOL fOptimized; /* Is (should be) filter optimized */ + ULONG* rmap; + ULONG Size; +} BM_FILTER; + +typedef BM_FILTER * LPBM_FILTER; + + +/* + * -- DBFCDX METHODS -- + */ + +#define SUPERTABLE ( &cdxSuper ) + +#define hb_cdxBof NULL +#define hb_cdxEof NULL +#define hb_cdxFound NULL +static ERRCODE hb_cdxGoBottom( CDXAREAP pArea ); +#define hb_cdxGoTo NULL +#define hb_cdxGoToId NULL +static ERRCODE hb_cdxGoTop( CDXAREAP pArea ); +static ERRCODE hb_cdxSeek( CDXAREAP pArea, BOOL bSoftSeek, PHB_ITEM pKey, BOOL bFindLast ); +static ERRCODE hb_cdxSkip( CDXAREAP pArea, LONG lToSkip ); +static ERRCODE hb_cdxSkipFilter( CDXAREAP pArea, LONG lUpDown ); +static ERRCODE hb_cdxSkipRaw( CDXAREAP pArea, LONG lToSkip ); +#define hb_cdxAddField NULL +static ERRCODE hb_cdxAppend( CDXAREAP pArea, BOOL bUnLockAll ); +#define hb_cdxCreateFields NULL +static ERRCODE hb_cdxDeleteRec( CDXAREAP pArea ); +#define hb_cdxDeleted NULL +#define hb_cdxFieldCount NULL +#define hb_cdxFieldDisplay NULL +#define hb_cdxFieldInfo NULL +#define hb_cdxFieldName NULL +static ERRCODE hb_cdxFlush( CDXAREAP pArea ); +#define hb_cdxGetRec NULL +#define hb_cdxGetValue NULL +#define hb_cdxGetVarLen NULL +static ERRCODE hb_cdxGoCold( CDXAREAP pArea ); +static ERRCODE hb_cdxGoHot( CDXAREAP pArea ); +static ERRCODE hb_cdxPutRec( CDXAREAP pArea, BYTE * pBuffer ); +#define hb_cdxPutValue NULL +static ERRCODE hb_cdxRecall( CDXAREAP pArea ); +#define hb_cdxRecCount NULL +#define hb_cdxRecInfo NULL +#define hb_cdxRecNo NULL +#define hb_cdxRecId NULL +#define hb_cdxSetFieldExtent NULL +#define hb_cdxAlias NULL +static ERRCODE hb_cdxClose( CDXAREAP pArea ); +#define hb_cdxCreate NULL +#define hb_cdxInfo NULL +#define hb_cdxNewArea NULL +static ERRCODE hb_cdxOpen( CDXAREAP pArea, LPDBOPENINFO pOpenInfo ); +#define hb_cdxRelease NULL +static ERRCODE hb_cdxStructSize( CDXAREAP pArea, USHORT * uiSize ); +#define hb_cdxSysName NULL +#define hb_cdxEval NULL +static ERRCODE hb_cdxPack ( CDXAREAP pArea ); +#define hb_cdxPackRec NULL +#define hb_cdxSort NULL +#define hb_cdxTrans NULL +#define hb_cdxTransRec NULL +static ERRCODE hb_cdxZap ( CDXAREAP pArea ); +#define hb_cdxChildEnd NULL +#define hb_cdxChildStart NULL +#define hb_cdxChildSync NULL +#define hb_cdxSyncChildren NULL +#define hb_cdxClearRel NULL +#define hb_cdxForceRel NULL +#define hb_cdxRelArea NULL +#define hb_cdxRelEval NULL +#define hb_cdxRelText NULL +#define hb_cdxSetRel NULL +static ERRCODE hb_cdxOrderListAdd( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ); +static ERRCODE hb_cdxOrderListClear( CDXAREAP pArea ); +#define hb_cdxOrderListDelete NULL +static ERRCODE hb_cdxOrderListFocus( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ); +static ERRCODE hb_cdxOrderListRebuild( CDXAREAP pArea ); +#define hb_cdxOrderCondition NULL +static ERRCODE hb_cdxOrderCreate( CDXAREAP pArea, LPDBORDERCREATEINFO pOrderInfo ); +static ERRCODE hb_cdxOrderDestroy( CDXAREAP pArea, LPDBORDERINFO pOrderInfo ); +static ERRCODE hb_cdxOrderInfo( CDXAREAP pArea, USHORT uiIndex, LPDBORDERINFO pOrderInfo ); +static ERRCODE hb_cdxClearFilter( CDXAREAP pArea ); +#define hb_cdxClearLocate NULL +#define hb_cdxClearScope NULL +static ERRCODE hb_cdxCountScope( CDXAREAP pArea, void * pPtr, LONG * plRec ); +#define hb_cdxFilterText NULL +#define hb_cdxScopeInfo NULL +static ERRCODE hb_cdxSetFilter( CDXAREAP pArea, LPDBFILTERINFO pFilterInfo ); +#define hb_cdxSetLocate NULL +#define hb_cdxSetScope NULL +#define hb_cdxSkipScope NULL +#define hb_cdxLocate NULL +#define hb_cdxCompile NULL +#define hb_cdxError NULL +#define hb_cdxEvalBlock NULL +#define hb_cdxRawLock NULL +#define hb_cdxLock NULL +#define hb_cdxUnLock NULL +#define hb_cdxCloseMemFile NULL +#define hb_cdxCreateMemFile NULL +#define hb_cdxGetValueFile NULL +#define hb_cdxOpenMemFile NULL +#define hb_cdxPutValueFile NULL +#define hb_cdxReadDBHeader NULL +#define hb_cdxWriteDBHeader NULL +#define hb_cdxInit NULL +#define hb_cdxExit NULL +#define hb_cdxDrop NULL +#define hb_cdxExists NULL +static ERRCODE hb_cdxRddInfo( LPRDDNODE pRDD, USHORT uiIndex, ULONG ulConnect, PHB_ITEM pItem ); +#define hb_cdxWhoCares NULL + +HB_EXTERN_END + +#endif /* HB_RDDCDX_H_ */ diff --git a/harbour/contrib/bmdbfcdx/make_b32.bat b/harbour/contrib/bmdbfcdx/make_b32.bat new file mode 100644 index 0000000000..b741127650 --- /dev/null +++ b/harbour/contrib/bmdbfcdx/make_b32.bat @@ -0,0 +1,35 @@ +@echo off +rem +rem $Id$ +rem + +if "%1" == "clean" goto CLEAN +if "%1" == "CLEAN" goto CLEAN + +:BUILD + + make -fmakefile.bc %1 %2 %3 > make_b32.log + if errorlevel 1 goto BUILD_ERR + +:BUILD_OK + + copy ..\..\lib\b32\bmdbfcdx.lib ..\..\lib\*.* > nul + if exist ..\..\lib\b32\bmdbfcdx.bak del ..\..\lib\b32\bmdbfcdx.bak + copy ..\..\lib\b32\bmsixcdx.lib ..\..\lib\*.* > nul + if exist ..\..\lib\b32\bmsixcdx.bak del ..\..\lib\b32\bmsixcdx.bak + goto EXIT + +:BUILD_ERR + + notepad make_b32.log + goto EXIT + +:CLEAN + if exist ..\..\lib\b32\bmdbfcdx.lib del ..\..\lib\b32\bmdbfcdx.lib + if exist ..\..\lib\b32\bmdbfcdx.bak del ..\..\lib\b32\bmdbfcdx.bak + if exist ..\..\lib\b32\bmsixcdx.lib del ..\..\lib\b32\bmsixcdx.lib + if exist ..\..\lib\b32\bmsixcdx.bak del ..\..\lib\b32\bmsixcdx.bak + + goto EXIT + +:EXIT \ No newline at end of file diff --git a/harbour/contrib/bmdbfcdx/makefile.bc b/harbour/contrib/bmdbfcdx/makefile.bc new file mode 100644 index 0000000000..803a33bb6e --- /dev/null +++ b/harbour/contrib/bmdbfcdx/makefile.bc @@ -0,0 +1,26 @@ +# +# $Id$ +# + +# makefile for Borland C/C++ 32 bits +# Building of bmdbfcdx.lib +# Clipper 5.3 DBFCDX compatible on SET OPTIMiZE ON + +INCLUDE_DIR = ..\..\include + +BIN_DIR = ..\..\bin\b32 +OBJ_DIR = ..\..\obj\b32 +LIB_DIR = ..\..\lib\b32 + +$(LIB_DIR)\bmdbfcdx.lib : \ + $(OBJ_DIR)\bmdbfcdx1.obj \ + $(OBJ_DIR)\bmsixcdx1.obj + +$(OBJ_DIR)\bmdbfcdx1.obj : bmdbfcdx1.c +$(OBJ_DIR)\bmsixcdx1.obj : bmsixcdx1.c + + +.c.obj: + bcc32 $(CLIBFLAGS) $(C_USR) -c -O2 -DWIN32 -I$(INCLUDE_DIR) -o$@ $< + tlib $(LIB_DIR)\bmdbfcdx.lib -+$@,, + diff --git a/harbour/contrib/bmdbfcdx/readme.txt b/harbour/contrib/bmdbfcdx/readme.txt new file mode 100644 index 0000000000..f03ca8fd96 --- /dev/null +++ b/harbour/contrib/bmdbfcdx/readme.txt @@ -0,0 +1,56 @@ + BMDBFCDX RDD: + ---------------------------------------------------------------------------- + + Is a DBFCDX RDD compatible with clipper 5.3, use SET OPTIMIZE ON to make a + static bitmap filters, with SET OPTIMIZE OFF works as harbour DBFCDX. + + Addons: + + BM_DbSeekWild( uKey, [lSoftSeek], [lFindLast], [lNext], [lAll] ) => .T./.F. or aSeekRec when lAll clause + BM_Turbo( lOnOff ) // Is only recomendable to use it on creating FILTERS + BM_DbGetFilterArray() => aFilterRec + BM_DbSetFilterArray( aFilterRec ) + BM_DbSetFilterArrayAdd( aFilterRec ) + BM_DbSetFilterArrayDel( aFilterRec ) + + Respecting command: + + SET OPTIMIZE + Change the setting that determines whether to optimize using the open orders + when processing a filtered database file +------------------------------------------------------------------------------ + Syntax + + SET OPTIMIZE ON | OFF | () + + Arguments + + ON enables optimization. + + OFF disables optimization. + + is a logical expression that must be enclosed in + parentheses. A value of true (.T.) is the same as ON, and a value of + false (.F.) is the same as OFF. + + Note: The initial default of this setting depends on the RDD. + + Description + + For RDDs that support optimization, such as DBFCDX, SET OPTIMIZE + determines whether to optimize filters based on the orders open in the + current work area. If this flag is ON, the RDD will optimize the search + for records that meet the filter condition to the fullest extent + possible, minimizing the need to read the actual data from the database + file. + + If this flag is OFF, the RDD will not optimize. + + Examples + + ¦ The following example enables optimization for the Inventor + database file using the SET OPTIMIZE command: + + USE Inventor NEW VIA "DBFCDX" + SET OPTIMIZE ON +