/* xbixtdx.cpp
XBase64 Software Library
Copyright (c) 1997,2003,2014,2022,2023 Gary A Kunkel
The xb64 software library is covered under the terms of the GPL Version 3, 2007 license.
Email Contact:
XDB-devel@lists.sourceforge.net
XDB-users@lists.sourceforge.net
This module handles temporary index logic
*/
#include "xbase.h"
#ifdef XB_TDX_SUPPORT
namespace xb{
/************************************************************************/
xbIxTdx::xbIxTdx( xbDbf *dbf ) : xbIxMdx( dbf ) {
//xbIxMdx::xbIxMdx( xbDbf *dbf ) : xbIx( dbf ){
// std::cout << "xbIxTdx::Constructor()\n";
// Init(); not needed, called in xbMdx
}
/************************************************************************/
xbIxTdx::~xbIxTdx() {
// std::cout << "xbIxTdx::Destructor()\n";
}
/***********************************************************************/
xbInt16 xbIxTdx::Close(){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
std::cout << "xbIxTdx::Close\n";
try{
if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
iErrorStop = 100;
throw iRc;
}
if(( iRc = xbRemove()) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbIxTdx::Close() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
xbase->WriteLogMessage( GetErrorMessage( iRc ));
}
return iRc;
}
/***********************************************************************/
//! @brief Create new tag.
/*!
This routine creates a new tag. When complete, sets the cur tag pointer to
the newly created tag.
\param sName Tag Name, including .MDX suffix
\param sKey Key Expression
\param sFilter Filter expression.
\param iDescending
\param iUnique xbtrue - Unique.
xbFalse - Not unique.
\param iOverLay xbTrue - Overlay if file already exists.
xbFalse - Don't overlay.
\param vpTag Output from method Pointer to vptag pointer.
\returns Return Codes
*/
xbInt16 xbIxTdx::CreateTag( const xbString &sName, const xbString &sKey, const xbString &sFilter, xbInt16 iDescending, xbInt16 iUnique, xbInt16 iOverlay, void **vpTag ){
xbInt16 iRc = XB_NO_ERROR;
xbInt16 iErrorStop = 0;
xbMdxTag *tte = NULL;
std::cout << "xbIxTdx::CreateTag()\n";
// std::cout << "CreateTag() name=[" << sName.Str() << "] key=[" << sKey.Str() << "] sFilter=[" << sFilter.Str() << "]\n";
// std::cout << "TagUseCnt = " << iTagUseCnt << std::endl;
try{
// verify room for new tag
if( !( iTagUseCnt < 47 )){
iErrorStop = 100;
iRc = XB_LIMIT_REACHED;
throw iRc;
}
// verify valid tag name
xbString sWorker = sName;
sWorker.Trim();
if( sWorker.Len() > 10 ){
iErrorStop = 110;
iRc = XB_INVALID_TAG;
throw iRc;
}
// verify tag not already defined
if( iTagUseCnt > 0 ){
if( GetTag( sWorker )){
iErrorStop = 120;
iRc = XB_INVALID_TAG;
throw iRc;
}
}
// allocate a tag structure here
if(( tte = (xbMdxTag *) calloc( 1, (size_t) sizeof( xbMdxTag ))) == NULL ){
iErrorStop = 130;
iRc = XB_NO_MEMORY;
throw iRc;
}
*vpTag = tte;
tte->sTagName = new xbString( sWorker );
//set up the key expression
sWorker = sFilter;
sWorker.Trim();
if( sWorker.Len() > 0 ){
if( sWorker.Len() == 0 || sWorker.Len() > 220 ){
iRc = XB_INVALID_TAG;
iErrorStop = 140;
throw iRc;
}
tte->sFiltExp = new xbString( sWorker );
tte->filter = new xbExp( dbf->GetXbasePtr());
if(( iRc = tte->filter->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
iErrorStop = 150;
throw iRc;
}
// tte->filter->DumpTree( 1 );
if((tte->filter->GetReturnType()) != 'L' ){
iRc = XB_INVALID_TAG;
iErrorStop = 160;
throw iRc;
}
tte->cHasFilter = 0x01;
}
//set up the key expression
sWorker = sKey;
sWorker.Trim();
if( sWorker.Len() == 0 || sWorker.Len() > 100 ){
iRc = XB_INVALID_TAG;
iErrorStop = 170;
throw iRc;
}
tte->sKeyExp = new xbString( sWorker );
tte->exp = new xbExp( dbf->GetXbasePtr());
if(( iRc = tte->exp->ParseExpression( dbf, sWorker )) != XB_NO_ERROR ){
iErrorStop = 180;
throw iRc;
}
xbDate d;
d.Sysdate();
if( iTagUseCnt == 0 ){
// first tag, new mdx file
// create the file name
// create temp file
xbString sIxFileName;
if(( iRc = CreateUniqueFileName( GetTempDirectory(), "TDX", sIxFileName )) != XB_NO_ERROR ){
iErrorStop = 190;
throw iRc;
}
// copy the file name to the class variable
this->SetFileName( sIxFileName );
if( FileExists() && !iOverlay ){
iErrorStop = 200;
iRc = XB_FILE_EXISTS;
throw iRc;
}
// first tag, need to create the file
if(( iRc = xbFopen( "w+b", dbf->GetShareMode())) != XB_NO_ERROR ){
iErrorStop = 210;
throw iRc;
}
cVersion = 2;
cCreateYY = (char) d.YearOf() - 1900;
cCreateMM = (char) d.MonthOf();
cCreateDD = (char) d.DayOf( XB_FMT_MONTH );
GetFileNamePart( sFileName );
sFileName.ToUpperCase();
SetBlockSize( (xbUInt32) dbf->GetCreateMdxBlockSize());
iBlockFactor = GetBlockSize() / 512;
cProdIxFlag = 0; // MDX is 1
cTagEntryCnt = 48;
iTagLen = 32;
ulPageCnt = 4;
ulFirstFreePage = 0;
ulNoOfBlockAvail = 0;
cNextTag = 1;
c1B = 0x1B;
cUpdateYY = cCreateYY;
cUpdateMM = cCreateMM;
cUpdateDD = cCreateDD;
if(( iRc = WriteHeadBlock( 0 )) != XB_NO_ERROR ){
iErrorStop = 220;
throw iRc;
}
}
// populate the tag table entry structure
tte->ulTagHdrPageNo = ulPageCnt;
ulPageCnt += (xbUInt32) iBlockFactor;
tte->sTagName->strncpy( tte->cTagName, 10 );
// cKeyFmt is always 0x10;
// tested 2+ZIPCD CITY+STATE or just standalone field - always 0x10
tte->cKeyFmt = 0x10; // = CalcTagKeyFmt( *tte->exp );
switch( tte->exp->GetReturnType()){
case XB_EXP_CHAR:
tte->cKeyType = 'C';
tte->iKeyLen = tte->exp->GetResultLen();
tte->iSecKeyType = 0;
break;
case XB_EXP_NUMERIC:
tte->cKeyType = 'N';
tte->iKeyLen = 12;
tte->iSecKeyType = 0;
break;
case XB_EXP_DATE:
tte->cKeyType = 'D';
tte->iKeyLen = 8;
tte->iSecKeyType = 1;
break;
default:
iErrorStop = 200;
iRc = XB_INVALID_INDEX;
throw iRc;
}
tte->cpKeyBuf = (char *) malloc( (size_t) tte->iKeyLen + 1 );
tte->cpKeyBuf2 = (char *) malloc( (size_t) tte->iKeyLen + 1 );
// if( 0 ){
// printf( "ulTagHdrPageNo=[%d] cTagName=[%-11s], cLeftChild=[%d] cRightChild=[%d] cParent=[%d] cKeyType=[%c]\n\n",
// tte->ulTagHdrPageNo, tte->cTagName, tte->cLeftChild, tte->cRightChild, tte->cParent, tte->cKeyType );
// }
// write the new tte entry here
char tteBuf[21];
memset( tteBuf, 0x00, 21 );
ePutUInt32( &tteBuf[0], tte->ulTagHdrPageNo );
for( xbUInt32 l = 0; l < tte->sTagName->Len() && l < 10; l++ ){
tteBuf[l+4] = tte->sTagName->GetCharacter(l+1);
}
tteBuf[15] = tte->cKeyFmt;
tteBuf[19] = 0x02; // appears to always be a 0x02
tteBuf[20] = tte->cKeyType;
if(( iRc = xbFseek( (iTagUseCnt * 32) + 544, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 230;
throw iRc;
}
if(( iRc = xbFwrite( tteBuf, 21, 1 )) != XB_NO_ERROR ){
iErrorStop = 240;
throw iRc;
}
// Begin Tag Header
tte->ulRootPage = ulPageCnt;
tte->ulTagSize = (xbUInt32) iBlockFactor;
ulPageCnt += 2;
tte->cKeyFmt2 = 0x10;
if( iDescending )
tte->cKeyFmt2 += 0x08;
if( iUnique ){
tte->cKeyFmt2 += 0x40;
tte->cUnique = 0x01;
}
tte->cTag11 = 0x1B; // always 0x1b ?
tte->cSerialNo = 0x01; // version incremented with each tag update
tte->ulLeftChild = tte->ulRootPage;
tte->ulRightChild = tte->ulRootPage;
tte->cTagYY = (char) d.YearOf() - 1900;
tte->cTagMM = (char) d.MonthOf();
tte->cTagDD = (char) d.DayOf( XB_FMT_MONTH );
tte->cKeyType2 = tte->cKeyType;
tte->iKeyItemLen = tte->iKeyLen + 4;
while(( tte->iKeyItemLen % 4 ) != 0 ) tte->iKeyItemLen++;
tte->iKeysPerBlock = (xbInt16) (GetBlockSize() - 12) / tte->iKeyItemLen;
//std::cout << "-------------- create tag info\n";
//std::cout << "keylen=" << tte->iKeyLen << " iKeyItemLen = " << tte->iKeyItemLen << " keys per block calc = " << tte->iKeysPerBlock << "\n";
tte->cKeyFmt3 = CalcTagKeyFmt( *tte->exp );
// printf( "ulRootPage=[%d] cKeyFmt2=[%d] cKeyType2=[%d] iKeyLen=[%d]iKeysPerBlock=[%d]\n", tte->ulRootPage, tte->cKeyFmt2, tte->cKeyType2, tte->iKeyLen, tte->iKeysPerBlock );
// printf( "iSecKeyType=[%d] iKeyItemLen=[%d] cUnique=[%d] \n", tte->iSecKeyType, tte->iKeyItemLen, tte->cUnique );
char *pBuf;
if(( pBuf = (char *) calloc( 1, (size_t) GetBlockSize())) == NULL ){
iErrorStop = 230;
iRc = XB_NO_MEMORY;
throw iRc;
}
char *wPtr;
wPtr = pBuf;
ePutUInt32( wPtr, tte->ulRootPage );
wPtr += 4;
ePutUInt32( wPtr, tte->ulTagSize );
wPtr += 4;
*wPtr = tte->cKeyFmt2;
wPtr++;
*wPtr = tte->cKeyType2;
wPtr += 2;
*wPtr = tte->cTag11;
wPtr += 1;
ePutInt16( wPtr, tte->iKeyLen );
wPtr += 2;
ePutInt16( wPtr, tte->iKeysPerBlock );
wPtr += 2;
ePutInt16( wPtr, tte->iSecKeyType );
wPtr += 2;
ePutInt16( wPtr, tte->iKeyItemLen );
wPtr += 2;
*wPtr = tte->cSerialNo;
wPtr += 3;
*wPtr = tte->cUnique;
wPtr++;
for( xbUInt32 l = 0; l < tte->sKeyExp->Len(); l++ )
*wPtr++ = tte->sKeyExp->GetCharacter(l+1);
wPtr = pBuf;
tte->cHasKeys = 0x00;
pBuf[246] = tte->cHasKeys;
wPtr += 248;
ePutUInt32( wPtr, tte->ulLeftChild );
wPtr += 4;
ePutUInt32( wPtr, tte->ulRightChild );
pBuf[257] = tte->cTagYY;
pBuf[258] = tte->cTagMM;
pBuf[259] = tte->cTagDD;
pBuf[480] = tte->cKeyFmt3;
if( sFilter.Len() > 0 ){
pBuf[245] = tte->cHasFilter;
wPtr = pBuf;
wPtr += 762;
for( xbUInt32 l = 0; l < sFilter.Len(); l++ )
*wPtr++ = sFilter.GetCharacter(l+1);
}
if(( iRc = xbFseek( tte->ulTagHdrPageNo * 512, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 250;
throw iRc;
}
if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
iErrorStop = 260;
throw iRc;
}
memset( pBuf, 0x00, GetBlockSize() );
if(( iRc = xbFwrite( pBuf, GetBlockSize(), 1 )) != XB_NO_ERROR ){
iErrorStop = 270;
throw iRc;
}
iTagUseCnt++;
cNextTag++;
if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
iErrorStop = 280;
throw iRc;
}
// add the new entry to the end of the list of tags
if( mdxTagTbl == NULL ){
mdxTagTbl = tte;
} else {
xbMdxTag *tteL = mdxTagTbl;
while( tteL->next )
tteL = tteL->next;
tteL->next = tte;
}
/* update the btree pointers */
CalcBtreePointers();
char bBuf[3];
xbMdxTag *tteWork = mdxTagTbl;
if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 290;
throw iRc;
}
while( tteWork ){
bBuf[0] = tteWork->cLeftChild;
bBuf[1] = tteWork->cRightChild;
bBuf[2] = tteWork->cParent;
if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
iErrorStop = 300;
throw iRc;
}
if( tteWork->next ){
if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
iErrorStop = 310;
throw iRc;
}
}
tteWork = tteWork->next;
}
free( pBuf );
}
catch (xbInt16 iRc ){
if( tte ){
if( tte->cpKeyBuf )
free( tte->cpKeyBuf );
if( tte->cpKeyBuf2 )
free( tte->cpKeyBuf2 );
if( tte->exp )
delete tte->exp;
if( tte->filter )
delete tte->filter;
if( tte->sKeyExp )
delete tte->sKeyExp;
if( tte->sFiltExp )
delete tte->sFiltExp;
if( tte->sTagName )
delete tte->sTagName;
free( tte );
}
xbString sMsg;
sMsg.Sprintf( "xbIxTdx::CreateTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
xbase->WriteLogMessage( GetErrorMessage( iRc ));
}
return iRc;
};
/***********************************************************************/
//! @brief Delete a given tag
/*!
\param vpTag Input tag ptr for tag to be deleted
\returns Return Codes
1 = Deleted entire MDX file, only had one tag
*/
xbInt16 xbIxTdx::DeleteTag( void *vpTag ){
xbInt16 iRc = 0;
xbInt16 iErrorStop = 0;
xbMdxTag * mpTag = (xbMdxTag *) vpTag;
xbIxNode *n = NULL;
xbBool bLoneTag = xbFalse;
try{
if( !vpTag ){
iErrorStop = 100;
iRc = XB_INVALID_TAG;
throw iRc;
}
// char cSaveHasFilter = mpTag->cHasFilter;
// char cSaveKeyFmt3 = mpTag->cKeyFmt3;
// xbString sSaveKey = mpTag->sKeyExp->Str();
if( iTagUseCnt == 1 ){
// std::cout << "xbIxTdx::DeleteTag - one tag found, delete the mdx file\n";
// close the mdx file
if(( iRc = xbIxMdx::Close()) != XB_NO_ERROR ){
iErrorStop = 110;
throw iRc;
}
// delete the file
xbRemove();
// init variables - needed?
// Init();
// iRc > 0 defines this as the only tag in an MDX file, MDX file deleted.
// signals to the calling process to drop the MDX file from the
// list of updateable indices.
bLoneTag = xbTrue;
} else {
// harvest tag nodes
if(( iRc = HarvestTagNodes( mpTag, xbTrue )) != XB_NO_ERROR ){
iErrorStop = 120;
throw iRc;
}
// remove an entry from tag table
// which tag is this?
xbInt16 iTagNo = 0;
xbMdxTag *mp = mdxTagTbl;
xbMdxTag *mpPrev = NULL;
while( mp && mp->ulTagHdrPageNo != mpTag->ulTagHdrPageNo ){
iTagNo++;
mpPrev = mp;
mp = mp->next;
}
// remove it from the linked list of tags
if( !mpPrev ){
mdxTagTbl = mp->next;
} else {
mpPrev->next = mp->next;
}
if( mp ){
if( mp->cpKeyBuf ) free( mp->cpKeyBuf );
if( mp->cpKeyBuf2 ) free( mp->cpKeyBuf2 );
if( mp->exp ) delete mp->exp;
if( mp->filter ) delete mp->filter;
if( mp->sKeyExp ) delete mp->sKeyExp;
if( mp->sFiltExp ) delete mp->sFiltExp;
if( mp->sTagName ) delete mp->sTagName;
free( mp );
}
xbInt32 iTarg = iTagNo * 32;
xbInt32 iSrc = iTarg + 32;
xbInt32 iLen = (iTagUseCnt - iTagNo) * 32;
if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 160;
throw iRc;
}
char Buf[1536]; // 47 tags + 1 in case tag #47 is deleted
memset( Buf, 0x00, 1536 );
if(( iRc = xbFread( Buf, 1504, 1 )) != XB_NO_ERROR ){
iErrorStop = 170;
throw iRc;
}
char *pTrg = Buf;
pTrg += iTarg;
char *pSrc = Buf;
pSrc += iSrc;
for( xbInt32 i = 0; i < iLen; i++ )
*pTrg++ = *pSrc++;
if(( iRc = xbFseek( 544, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 180;
throw iRc;
}
if(( iRc = xbFwrite( Buf, 1504, 1 )) != XB_NO_ERROR ){
iErrorStop = 190;
throw iRc;
}
iTagUseCnt--;
if(( iRc = WriteHeadBlock( 1 )) != XB_NO_ERROR ){
iErrorStop = 200;
throw iRc;
}
// update the btree pointers
CalcBtreePointers();
char bBuf[3];
xbMdxTag *tteWork = mdxTagTbl;
if(( iRc = xbFseek( 560, SEEK_SET )) != XB_NO_ERROR ){
iErrorStop = 210;
throw iRc;
}
while( tteWork ){
bBuf[0] = tteWork->cLeftChild;
bBuf[1] = tteWork->cRightChild;
bBuf[2] = tteWork->cParent;
if(( iRc = xbFwrite( bBuf, 3, 1 )) != XB_NO_ERROR ){
iErrorStop = 320;
throw iRc;
}
if( tteWork->next ){
if(( iRc = xbFseek( 29, SEEK_CUR )) != XB_NO_ERROR ){
iErrorStop = 330;
throw iRc;
}
}
tteWork = tteWork->next;
}
}
}
catch (xbInt16 iRc ){
xbString sMsg;
sMsg.Sprintf( "xbIxTdx::DeleteTag() Exception Caught. Error Stop = [%d] iRc = [%d]", iErrorStop, iRc );
xbase->WriteLogMessage( sMsg.Str() );
xbase->WriteLogMessage( GetErrorMessage( iRc ));
if( n )
free( n );
}
if( bLoneTag && !iRc )
return 1;
else
return iRc;
}
/************************************************************************/
/************************************************************************/
} /* namespace */
#endif /* XB_TDX_SUPPORT */